1
0
forked from IPKM/nmreval

Compare commits

..

123 Commits

Author SHA1 Message Date
90084e3481 dev (#297)
this time it will not break!

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#297
2024-11-14 17:20:31 +00:00
91b2594b90 Revert "dev (#295)"
This reverts commit 7145f9e3cd.
2024-11-14 12:14:17 +01:00
7145f9e3cd dev (#295)
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#295
2024-11-13 13:02:05 +00:00
4108deb69a run scripts
run scripts

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#291
2024-09-29 17:21:40 +00:00
b2a3881fa8 dev (#285)
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#285
2024-09-11 15:33:00 +00:00
1ab32af333 dev (#284)
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#284
Co-authored-by: Dominik Demuth <dominik.demuth@pkm.tu-darmstadt.de>
Co-committed-by: Dominik Demuth <dominik.demuth@pkm.tu-darmstadt.de>
2024-09-07 17:25:01 +00:00
e1b76e837d dev (#283)
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#283
2024-07-16 17:01:20 +00:00
5823ddd18c preview preparation needs to pass list of active data to children (#280)
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#280
2024-07-05 17:31:58 +00:00
a2a95e796a wrong addition to parameter length in iterator (#278)
fixes #277

Reviewed-on: IPKM/nmreval#278
2024-07-04 10:37:43 +00:00
8f92d8d822 dev (#275)
closes issues #267 #274, #255, #256

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#275
2024-06-24 15:59:33 +00:00
6ecc789cd5 missed index as option (#270)
Reviewed-on: IPKM/nmreval#270
2024-04-30 15:16:23 +00:00
Dominik Demuth
3ee7dca457 missed index as option 2024-04-30 17:15:46 +02:00
79d0ab1628 read-fid (#269); closes #268
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#269
2024-04-29 16:26:09 +00:00
1162458290 catch empty sets before fit (#265); closes #261
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#265
2024-04-04 17:26:52 +00:00
c8aad904a8 262-column-header (#264); closes #262
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#264
2024-04-03 15:53:19 +00:00
b25db92cf1 stop autoamtic ascii reading if first is cancelled (#260); close #251
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#260
2024-04-02 15:34:42 +00:00
403273e0d7 emit update of changed graph titles (#259); close #257
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#259
2024-03-27 18:02:53 +00:00
299bb043ea adjust log level for dockwidget (#258)
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#258
2024-03-27 16:59:43 +00:00
2f9cb761cf use tree colors in fit result for sub-funcs (#253)
closes #201

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#253
2024-02-27 15:36:14 +00:00
24f77f753c 242-uncaught-exception (#252)
close issue #242

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#252
2024-02-27 14:20:08 +00:00
04d384363a 249-asciireader (#250)
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#250
2024-02-20 16:19:23 +00:00
ffba4900a1 remove complex kwarg when necessary (#246)
closes #245

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#246
2024-02-13 16:17:08 +00:00
80d9c7098c 207-noncomplex-fits (#244)
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#244

closes #207
2024-02-11 17:40:50 +00:00
8d3ab75c97 bugfix (#241)
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#241
2024-02-10 16:46:15 +00:00
24640d374e handle graph export with empty data (#239); closes #233
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#239
2024-02-07 18:58:18 +00:00
881eff2770 update tool tip; fixes #234
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#238
2024-02-07 18:11:15 +00:00
40746bfa7c add exclude range to fit limits (#237)
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#237
2024-02-07 17:55:07 +00:00
567148b7e6 231-dsc-empty-baseline (#236)
closes #231
2024-02-06 17:23:00 +00:00
39b0fe75cb check relative path; remove possible cause of #231 (#232)
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#232
2024-02-03 11:25:35 +00:00
7161a17348 228-229-index-problems (#230)
bugfixes: closes #228, closes #229
2024-01-31 16:00:07 +00:00
813e18a744 make log-spacing explicit option for custom fit x values (#227)
closes #225
2024-01-30 18:01:15 +00:00
3626cfc7ea ensure sorted sets before averaging in pick points (#226)
should finally fix #189
2024-01-30 17:01:13 +00:00
575cb5e8f6 209-fit-tree (#222)
closes #209
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#222
2024-01-21 17:01:46 +00:00
465fb0c09a 194-fitrange (#219)
keyboard-setting of custom fit range; closes #194; helps for #32
2024-01-18 18:25:07 +00:00
06491ff413 add hint to sort before averaging; closes #213 (#218)
Reviewed-on: IPKM/nmreval#218
2024-01-18 16:40:55 +00:00
Dominik Demuth
256bc20846 add hint to sort before averaging 2024-01-18 17:39:12 +01:00
38a44047de set new y_err with value zero (#215)
closes #215
2024-01-15 18:41:37 +00:00
d83e112515 bugfixes-022024 (#211)
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#211
2024-01-11 12:39:03 +00:00
73bdc71a83 issue126-backup (#198)
reworked autosave, closes #126; update with restart
2024-01-03 12:30:04 +00:00
58e86f4abc bugfixes-01012024 (#197)
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#197
2024-01-02 10:10:49 +00:00
5ad6456b16 add sprinkles of number signs to text saves; closes #195 (#196)
Reviewed-on: IPKM/nmreval#196
2023-12-30 17:35:17 +00:00
Dominik Demuth
a1ab8ad889 add sprinkles of number signs to text saves; closes #195 2023-12-30 18:34:44 +01:00
57afee372f bugfix (#193)
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#193
2023-12-30 15:40:44 +00:00
24bba43b40 bugfixes (#192)
closes #123

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#192
2023-12-28 16:58:37 +00:00
694d47267d bugfixes (#191)
added basic syntax check; closes #148

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#191
2023-12-28 14:30:33 +00:00
2cf94af2c4 fix point selection for values with all the same x (#190)
bugfix for issue #189; closes #189

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#190
2023-12-28 10:24:34 +00:00
92a3933ed4 bugfixes (#188)
Bugfixes for several issues: closes #163, closes #151, closes #176, closes #138, closes #104

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#188
2023-12-26 16:05:19 +00:00
9815c0df40 fit statistics with extra term to avoid division by zero; (#186)
closes #181

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#186
2023-12-17 16:10:35 +00:00
789801228a 179 (#187)
closes #179

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#187
2023-12-17 16:09:59 +00:00
ce9bd5d2fd add merged set to graph of sources if source sets are in same graph (#185)
close #164

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#185
2023-12-17 16:05:39 +00:00
5b74ee1f29 fitresult: reset displayed ranges only if there is something to reset (#184)
closes #156

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#184
2023-12-17 16:03:39 +00:00
036d798813 issue-168 (#183)
fix FC reading problems; fixes #168

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#183
2023-12-15 18:27:32 +00:00
af0e0fc76f #177 (#178)
closes issue #177
2023-12-13 19:10:01 +00:00
a0c07231c3 removed unneeded workflow demo.yaml (#175)
Removed the demo.yaml

Reviewed-on: IPKM/nmreval#175
Co-authored-by: Markus Rosenstihl <markus.rosenstihl@pkm.tu-darmstadt.de>
Co-committed-by: Markus Rosenstihl <markus.rosenstihl@pkm.tu-darmstadt.de>
2023-12-08 07:49:30 +00:00
eda89b26fb build (#174)
Bookworm runner is installed and works. Image was built and runs too, closes #173

Reviewed-on: IPKM/nmreval#174
Co-authored-by: Markus Rosenstihl <markus.rosenstihl@pkm.tu-darmstadt.de>
Co-committed-by: Markus Rosenstihl <markus.rosenstihl@pkm.tu-darmstadt.de>
2023-12-07 14:17:16 +00:00
c2dc4cc6b8 action_test (#172)
Reviewed-on: IPKM/nmreval#172
2023-12-06 18:48:14 +00:00
f7a17f22cf action_test (#170)
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#170
2023-12-06 17:05:25 +00:00
Dominik Demuth
cd97dda2d4 trigger actions not on main branch 2023-12-06 17:55:39 +01:00
b50200592d upload fixed 2023-12-06 16:51:16 +01:00
bfc25e33f9 build naming fixed, finally 2023-12-06 16:41:34 +01:00
772c51669d build naming fixed, finally 2023-12-06 16:38:36 +01:00
35ae571de0 build naming fixed,nope 3 2023-12-06 16:36:56 +01:00
bcc828efdc build naming fixed,nope2 2023-12-06 16:33:22 +01:00
4df0ad6d20 build naming fixed,nope 2023-12-06 16:31:36 +01:00
adb71257ff build naming fixed,nope 2023-12-06 16:29:34 +01:00
c0dabbe9fe build naming fixed 2023-12-06 16:21:44 +01:00
500e473212 fixed build nameing, v15 2023-12-06 16:19:33 +01:00
3928e02b44 fixed build nameing, v14 2023-12-06 16:18:32 +01:00
0c448e8ee9 fixed build nameing, v13 2023-12-06 16:14:45 +01:00
6fd44a14fa fixed build nameing, v12 2023-12-06 16:12:05 +01:00
9323cb8883 fixed build nameing, v11 2023-12-06 16:10:44 +01:00
025b14a288 fixed build nameing, v10 2023-12-06 16:09:57 +01:00
a7fed328ae fixed build nameing, v9 2023-12-06 16:09:07 +01:00
a09a6bd988 fixed build nameing, v8 2023-12-06 16:08:25 +01:00
6ee8d27d4a fixed build nameing, v7 2023-12-06 16:07:32 +01:00
a789612eae fixed build nameing, v7 2023-12-06 16:06:18 +01:00
c7855a74f4 fixed build nameing, v6 2023-12-06 16:04:41 +01:00
d099253812 fixed build nameing, v5 2023-12-06 15:58:26 +01:00
7c586c4bf0 fixed build nameing, v4 2023-12-06 15:52:27 +01:00
0a9d596a91 fixed build nameing, v3 2023-12-06 15:48:45 +01:00
49a1b0d4fa fixed build nameing, v2 2023-12-06 15:47:53 +01:00
bd9288d20e fixed build nameing 2023-12-06 15:45:19 +01:00
223ddd4cda condensed build steps in workflow 2023-12-06 15:28:26 +01:00
53349d7764 uploading debugging 2023-12-06 15:24:36 +01:00
4afa7a37ca make the AppImage build docker friendly 2023-12-06 15:00:25 +01:00
32ca840c78 force a start of gpg-agent, v3 2023-12-06 14:53:07 +01:00
a3f57fbf73 force a start of gpg-agent, v2 2023-12-06 14:52:26 +01:00
c157ebb0f9 force a start of gpg-agent 2023-12-06 14:50:38 +01:00
962802ebe4 using vars instead of env context for variables 2023-12-06 14:47:16 +01:00
340bba747a debug 2023-12-06 14:42:51 +01:00
331f700933 added proper variables for GPG 2023-12-06 14:35:38 +01:00
2a1852b4ca added apt-get update and install for neede packages in workflow 2023-12-06 14:30:48 +01:00
98c9354883 added GO_PIPELINE_LABEL to workflow 2023-12-06 14:29:36 +01:00
94fc92fb56 workflow fixes 2023-12-06 14:16:48 +01:00
7e3541806b preliminary build workflow 2023-12-06 14:13:57 +01:00
bd9051be00 vars testing 2023-12-06 12:24:46 +01:00
d5293cf534 test ENV variables 2023-12-06 12:12:23 +01:00
d4812a25ea changed label to 'bullseye' 2023-12-06 12:01:24 +01:00
a37704acc4 moved demo.yaml workflow to correct directory 2023-12-06 11:42:27 +01:00
787d2f4c0f action runner demo added 2023-12-06 11:41:37 +01:00
Dominik Demuth
a9d284eabe rethink choices of 0c7ca0b9ba; minimum selection at least should work, cf. #166 2023-12-04 19:38:29 +01:00
Dominik Demuth
20592e05c6 undo stuff I introduced in aa0d14a322; should fix #165, reopens #151 2023-12-01 20:19:09 +01:00
Dominik Demuth
728eb34ca7 ignore sets without start parameter in fit prep 2023-12-01 17:58:48 +01:00
Dominik Demuth
843866be45 Fit: Ignore complex_state, if no complex function is selected; closes #144 2023-11-30 19:21:21 +01:00
Dominik Demuth
aa0d14a322 Make FitResults from all fixed parameters; closes #151 2023-11-30 18:16:07 +01:00
Dominik Demuth
12726e6f56 set order is now respected for fits; fixes #161 2023-11-30 17:47:53 +01:00
Dominik Demuth
d146f4fe7e Merge remote-tracking branch 'origin/master' 2023-11-29 19:35:31 +01:00
Dominik Demuth
edf69b6424 check if start parameter of fits are given; closes #157 2023-11-29 19:24:57 +01:00
Dominik Demuth
e662dcf03c add warning for non-working interpolations; fixes #159 2023-11-29 19:16:58 +01:00
Dominik Demuth
bb5c6a5491 check each tab if connected figure exists; maybe closes #150 2023-11-29 18:14:04 +01:00
Dominik Demuth
386bc5a371 undo of sets were missing from graph delete undo 2023-11-29 18:14:04 +01:00
Dominik Demuth
0c7ca0b9ba set options to make paintEvents faster; does not much for RAM usage (closes #104) 2023-11-29 18:14:03 +01:00
7c71061877 deal with missing weight key get_state and set_state; partly fixes #158 2023-11-29 08:11:14 +00:00
Dominik Demuth
8a6c909c3e button to reset view limits of fit results; closes #156 2023-11-28 19:31:05 +01:00
Dominik Demuth
340d5d1038 fit results are auto-scaled if data was not fit before; related to #156 2023-11-28 18:31:19 +01:00
Dominik Demuth
c04052294c add issue templates 2023-11-28 18:28:29 +01:00
Dominik Demuth
e063abc712 fix grace export bugs; closes #155 2023-11-23 19:43:50 +01:00
Dominik Demuth
412708cecd set cursor position manually in fit data parameter; closes #153 2023-11-23 18:40:56 +01:00
Dominik Demuth
c7a21c72f2 graph keys were not deleted in tabs because of tab switching; closes #150 2023-11-22 18:30:17 +01:00
Dominik Demuth
929bb80f2f edit spectra should now work better; closes #152 2023-11-22 17:48:08 +01:00
Dominik Demuth
dfe9eab817 fix 3.9/3.10 problem with traceback of last commit 2023-11-20 19:43:35 +01:00
Dominik Demuth
d18b3ee671 catch all exceptions during import of usermodels.py; closes #149 2023-11-20 19:06:02 +01:00
Dominik Demuth
64b270d7c1 remove enableAutoRange call to stop unwanted rescales; closes #143 2023-11-15 20:16:17 +01:00
105 changed files with 5596 additions and 2511 deletions

View File

@ -0,0 +1,51 @@
name: 🐞 Bug Report
description: File a bug/issue
title: "[BUG] <title>"
labels: ["Kind/Bug"]
assignees:
- dominik
body:
- type: textarea
attributes:
label: Current behavior
description: A concise description of what you're experiencing.
validations:
required: true
- type: input
attributes:
label: Version
description: For which version have you observed this behavior?
placeholder: You find the program version in "Help/About"
validations:
required: false
- type: textarea
attributes:
label: Expected behavior
description: A concise description of what you expected to happen.
validations:
required: false
- type: textarea
attributes:
label: Steps to reproduce
description: If possible, describe steps to reproduce the behavior.
placeholder: |
1. Pressed button '...'
2. See error...
3. Infinite sadness 😞
validations:
required: false
- type: textarea
attributes:
label: Log messages
description: |
Please copy and paste any relevant log output.
render: shell
validations:
required: false
- type: textarea
attributes:
label: Anything else?
description: |
Everything else that could provide more context about the issue, e.g. screenshots.
validations:
required: false

View File

@ -0,0 +1,26 @@
name: 🙂 Feature Request
about: Request a new feature or feature improvement
title: "[Feature]: <title>"
labels: ["Kind/Feature", "Kind/Enhancement"]
assignees:
- dominik
body:
- type: textarea
attributes:
label: Proposed Behavior
description: A concise description of what you want to happen.
validations:
required: true
- type: textarea
attributes:
label: Possible benefits
description: A concise description of why this is an benefit for users.
validations:
required: false
- type: textarea
attributes:
label: Anything else?
description: |
Everything else that could provide more context about the issue, e.g. screenshots.
validations:
required: false

View File

@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Mystery
url: https://c.xkcd.com/random/comic/
about: What is behind door number 3?

View File

@ -0,0 +1,38 @@
name: Build AppImage
run-name: ${{ gitea.actor }} is building THE AppImage 🚀 # a lot of excitement for this build
on:
push:
branches:
- master
jobs:
Explore-Gitea-Actions:
runs-on: bookworm
steps:
- name: Check out repository code
uses: actions/checkout@v3
- run: |
apt-get -y update
apt-get -y install python3-yaml python3-requests
- name: Declare some variables
shell: bash
run: |
echo "SHA_SHORT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
echo "YEAR=$(date +%Y)" >> $GITHUB_ENV
- name: Build AppImage
run: |
echo $GO_PIPELINE_LABEL
eval $(gpg-agent --daemon --allow-preset-passphrase --default-cache-ttl 1 --max-cache-ttl 7200)
/usr/lib/gnupg/gpg-preset-passphrase --preset --passphrase ${GPG_PASSPHRASE} ${GPG_KEYGRIP}
./tools/update_version.py
./tools/build.sh
env:
GPG_KEYGRIP: ${{ vars.GPG_KEYGRIP }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
GO_PIPELINE_LABEL: ${{ env.YEAR }}.${{ gitea.run_number }}_${{ env.SHA_SHORT }}
- name: Upload AppImage
run: ./tools/upload_gitea.sh
env:
GO_PIPELINE_LABEL: ${{ env.YEAR }}.${{ gitea.run_number }}_${{ env.SHA_SHORT }}
UPLOAD_TOKEN: ${{ secrets.UPLOAD_TOKEN }}
UPLOAD_USER: ${{ vars.UPLOAD_USER }}

61
bin/evaluate.py Executable file
View File

@ -0,0 +1,61 @@
#!/usr/bin/env python3
import sys
import pathlib
import os
sys.path.append(str(pathlib.Path(__file__).absolute().parent.parent / 'src'))
from nmreval.configs import check_for_config
# does a directory for config stuff exist? create it if not
check_for_config()
# pyqtgraph warns on Mac if QApplication is created when it is imported
# import pyqtgraph
from nmreval.lib.logger import handle_exception
sys.excepthook = handle_exception
from gui_qt import App
from gui_qt.Qt import QtCore
app = App(['NMReval'])
from gui_qt.main.mainwindow import NMRMainWindow
from gui_qt.lib.backup import BackupManager
def do_autosave():
# autosave and update timestamp in db
success = mplQt.autosave()
if success:
backuping.update_last_save()
# autosave stuff: keep track of instance and their backup files
backuping = BackupManager()
# look for autosaves in DB without running programs
files = backuping.search_unsaved()
# tell everyone what autosave files belongs to this process
pid = os.getpid()
bck_name = backuping.create_entry(pid)
mplQt = NMRMainWindow(bck_file=bck_name)
# one manual autosave to create the file
do_autosave()
# load all selected autosaves to program
for f in files:
mplQt.management.load_files(f)
f.unlink()
timer = QtCore.QTimer()
timer.timeout.connect(do_autosave)
timer.start(3 * 60 * 1000)
app.aboutToQuit.connect(backuping.close)
mplQt.show()
sys.exit(app.exec())

View File

@ -1,53 +1,54 @@
[build-system] [metadata]
requires = ["setuptools", "wheel", "setuptools-scm"] name = nmreval
build-backend = "setuptools.build_meta" version = 0.1
description = Evaluation of data
[project] long_description = file: README.md
name = "nmreval" author = Dominik Demuth
version = "0.1" author_email = dominik.demuth@physik.tu-darmstadt.de
description = "Evaluation of NMR and orther data" install_requires = [
authors = [ 'numpy',
{ name = "Dominik Demuth", email = "dominik.demuth@pkm.tu-darmstadt.de" }, 'scipy',
'matplotlib',
'bsddb3',
'pyqtgraph',
'pyqt',
'h5py',
] ]
maintainers = [ keywords = ['nmr', 'physics', 'science']
{ name = "Dominik Demuth", email = "dominik.demuth@pkm.tu-darmstadt.de" },
]
requires-python = ">=3.7"
classifiers = [ classifiers = [
"Development Status :: 3 - Alpha", 'Development Status :: 3 - Alpha',
"Intended Audience :: Science/Research", 'Intended Audience :: End Users/Desktop',
"Topic :: Scientific/Engineering :: Physics", 'Intended Audience :: Science/Research',
"License :: OSI Approved :: BSD License", 'Topic :: Scientific/Engineering :: Physics',
"Programming Language :: Python :: 3", 'Topic :: Scientific/Engineering :: Visualization',
"Programming Language :: Python :: 3.7", 'Programming Language :: Python :: 3',
"Programming Language :: Python :: 3.8", 'Programming Language :: Python :: 3.7',
"Programming Language :: Python :: 3.9", 'Programming Language :: Python :: 3.8',
"Programming Language :: Python :: 3.10", 'Programming Language :: Python :: 3.9',
"Programming Language :: Python :: 3.10", 'Programming Language :: Python :: 3.10',
"Programming Language :: Python :: 3.11", 'Programming Language :: Python :: 3 :: only',
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3 :: only",
"Operating System :: POSIX :: Linux"
]
keywords = ["nmr", "physics", "science"]
license = { text = "BSD 3-Clause License" }
dependencies = [
"numpy",
"scipy",
"h5py",
"PyQt5",
"pyqtgraph",
] ]
license = {text = "BSD 3-Clause License"}
[project.optional-dependencies] [tool.setuptools]
legacy = ["bsddb3"] include_package_data = true
[project.urls]
Repository = "https://gitea.pkm.physik.tu-darmstadt.de/IPKM/nmreval"
Issues = "https://gitea.pkm.physik.tu-darmstadt.de/IPKM/nmreval/issues"
[project.gui-scripts] [tool.setuptools.packages]
nmreval = "gui_qt.cli:main" find = {}
scripts = bin/evaluate.py
[tool.setuptools.packages.find] [tool.setuptools.packages.find]
where = ["src"] include =[
'nmreval*',
'gui_qt*',
'resources*',
]
[tool.setuptools.package_data]
* = *.txt, *.npz, *.png, *.json

8
requirements.txt Normal file
View File

@ -0,0 +1,8 @@
matplotlib
numpy
scipy
PyQt5
h5py
pyqtgraph
bsddb3
requests

4
setup.py Executable file
View File

@ -0,0 +1,4 @@
# Always prefer setuptools over distutils
from setuptools import setup
setup()

View File

@ -360,7 +360,7 @@ class Ui_ascii_reader(object):
self.x_label.setText(_translate("ascii_reader", "x")) self.x_label.setText(_translate("ascii_reader", "x"))
self.dsdfsf.setText(_translate("ascii_reader", "Numerical value")) self.dsdfsf.setText(_translate("ascii_reader", "Numerical value"))
self.label_9.setText(_translate("ascii_reader", "Match index")) self.label_9.setText(_translate("ascii_reader", "Match index"))
self.regex_input.setToolTip(_translate("ascii_reader", "<html><head/><body><p>Token:<br/>[abc]: Matches any of a, b, or c<br/>[a-z]: Matches any digit in the range a-z<br/>\\d: Matches any digit in the range 0-9 (equal to [0-9}</p><p>Quantifiers:<br/>a*: 0 or more of a<br/>a*: 1 or more of a<br/>a?: 0 or 1 of a</p></body></html>")) self.regex_input.setToolTip(_translate("ascii_reader", "<html><head/><body><p>Token:<br/>[abc]: Matches any of a, b, or c<br/>[a-z]: Matches any digit in the range a-z<br/>\\d: Matches any digit in the range 0-9 (equal to [0-9}</p><p>Quantifiers:<br/>a*: 0 or more of a<br/>a+: 1 or more of a<br/>a?: 0 or 1 of a</p></body></html>"))
self.re_button.setText(_translate("ascii_reader", "Regex")) self.re_button.setText(_translate("ascii_reader", "Regex"))
self.custom_button.setText(_translate("ascii_reader", "Custom value")) self.custom_button.setText(_translate("ascii_reader", "Custom value"))
self.label_8.setText(_translate("ascii_reader", "Filename")) self.label_8.setText(_translate("ascii_reader", "Filename"))

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'src/resources/_ui/basewindow.ui' # Form implementation generated from reading ui file './nmreval/src/resources/_ui/basewindow.ui'
# #
# Created by: PyQt5 UI code generator 5.15.9 # Created by: PyQt5 UI code generator 5.15.10
# #
# WARNING: Any manual changes made to this file will be lost when pyuic5 is # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing. # run again. Do not edit this file unless you know what you are doing.
@ -87,6 +87,8 @@ class Ui_BaseWindow(object):
self.menuSave.setObjectName("menuSave") self.menuSave.setObjectName("menuSave")
self.menuData = QtWidgets.QMenu(self.menubar) self.menuData = QtWidgets.QMenu(self.menubar)
self.menuData.setObjectName("menuData") self.menuData.setObjectName("menuData")
self.menuCut_to_visible_range = QtWidgets.QMenu(self.menuData)
self.menuCut_to_visible_range.setObjectName("menuCut_to_visible_range")
self.menuHelp = QtWidgets.QMenu(self.menubar) self.menuHelp = QtWidgets.QMenu(self.menubar)
self.menuHelp.setObjectName("menuHelp") self.menuHelp.setObjectName("menuHelp")
self.menuExtra = QtWidgets.QMenu(self.menubar) self.menuExtra = QtWidgets.QMenu(self.menubar)
@ -153,15 +155,6 @@ class Ui_BaseWindow(object):
self.toolBar_nmr.setIconSize(QtCore.QSize(24, 24)) self.toolBar_nmr.setIconSize(QtCore.QSize(24, 24))
self.toolBar_nmr.setObjectName("toolBar_nmr") self.toolBar_nmr.setObjectName("toolBar_nmr")
BaseWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.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) self.toolBar_spectrum = QtWidgets.QToolBar(BaseWindow)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
@ -313,8 +306,6 @@ class Ui_BaseWindow(object):
self.actionDerivation.setObjectName("actionDerivation") self.actionDerivation.setObjectName("actionDerivation")
self.actionIntegration = QtWidgets.QAction(BaseWindow) self.actionIntegration = QtWidgets.QAction(BaseWindow)
self.actionIntegration.setObjectName("actionIntegration") 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 = QtWidgets.QAction(BaseWindow)
self.actionMove_between_plots.setObjectName("actionMove_between_plots") self.actionMove_between_plots.setObjectName("actionMove_between_plots")
self.actionBaseline = QtWidgets.QAction(BaseWindow) self.actionBaseline = QtWidgets.QAction(BaseWindow)
@ -374,6 +365,15 @@ class Ui_BaseWindow(object):
self.actionBinning.setObjectName("actionBinning") self.actionBinning.setObjectName("actionBinning")
self.actionTNMH = QtWidgets.QAction(BaseWindow) self.actionTNMH = QtWidgets.QAction(BaseWindow)
self.actionTNMH.setObjectName("actionTNMH") self.actionTNMH.setObjectName("actionTNMH")
self.actionExclude_region = QtWidgets.QAction(BaseWindow)
self.actionExclude_region.setCheckable(True)
self.actionExclude_region.setObjectName("actionExclude_region")
self.action_cut_xaxis = QtWidgets.QAction(BaseWindow)
self.action_cut_xaxis.setObjectName("action_cut_xaxis")
self.action_cut_yaxis = QtWidgets.QAction(BaseWindow)
self.action_cut_yaxis.setObjectName("action_cut_yaxis")
self.actionUse_script = QtWidgets.QAction(BaseWindow)
self.actionUse_script.setObjectName("actionUse_script")
self.menuSave.addAction(self.actionSave) self.menuSave.addAction(self.actionSave)
self.menuSave.addAction(self.actionExportGraphic) self.menuSave.addAction(self.actionExportGraphic)
self.menuSave.addAction(self.action_save_fit_parameter) self.menuSave.addAction(self.action_save_fit_parameter)
@ -386,6 +386,9 @@ class Ui_BaseWindow(object):
self.menuFile.addSeparator() self.menuFile.addSeparator()
self.menuFile.addAction(self.action_close) self.menuFile.addAction(self.action_close)
self.menuFile.addSeparator() self.menuFile.addSeparator()
self.menuCut_to_visible_range.addSeparator()
self.menuCut_to_visible_range.addAction(self.action_cut_xaxis)
self.menuCut_to_visible_range.addAction(self.action_cut_yaxis)
self.menuData.addAction(self.action_new_set) self.menuData.addAction(self.action_new_set)
self.menuData.addAction(self.action_delete_sets) self.menuData.addAction(self.action_delete_sets)
self.menuData.addAction(self.actionMove_between_plots) self.menuData.addAction(self.actionMove_between_plots)
@ -395,9 +398,10 @@ class Ui_BaseWindow(object):
self.menuData.addAction(self.action_sort_pts) self.menuData.addAction(self.action_sort_pts)
self.menuData.addAction(self.actionSkip_points) self.menuData.addAction(self.actionSkip_points)
self.menuData.addSeparator() self.menuData.addSeparator()
self.menuData.addAction(self.action_cut) self.menuData.addAction(self.menuCut_to_visible_range.menuAction())
self.menuData.addSeparator() self.menuData.addSeparator()
self.menuData.addAction(self.actionChange_datatypes) self.menuData.addAction(self.actionChange_datatypes)
self.menuData.addAction(self.actionUse_script)
self.menuHelp.addAction(self.actionShow_error_log) self.menuHelp.addAction(self.actionShow_error_log)
self.menuHelp.addAction(self.actionUpdate) self.menuHelp.addAction(self.actionUpdate)
self.menuHelp.addAction(self.actionBugs) self.menuHelp.addAction(self.actionBugs)
@ -428,6 +432,7 @@ class Ui_BaseWindow(object):
self.menuLimits.addAction(self.action_no_range) self.menuLimits.addAction(self.action_no_range)
self.menuLimits.addAction(self.action_x_range) self.menuLimits.addAction(self.action_x_range)
self.menuLimits.addAction(self.action_custom_range) self.menuLimits.addAction(self.action_custom_range)
self.menuLimits.addAction(self.actionExclude_region)
self.menuFit.addAction(self.action_FitWidget) self.menuFit.addAction(self.action_FitWidget)
self.menuFit.addSeparator() self.menuFit.addSeparator()
self.menuFit.addAction(self.action_create_fit_function) self.menuFit.addAction(self.action_create_fit_function)
@ -496,7 +501,6 @@ class Ui_BaseWindow(object):
self.toolbar_edit.addAction(self.actionShift) self.toolbar_edit.addAction(self.actionShift)
self.toolBar_nmr.addAction(self.t1action) self.toolBar_nmr.addAction(self.t1action)
self.toolBar_nmr.addAction(self.actionCalculateT1) 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.action_edit)
self.toolBar_spectrum.addAction(self.actionPick_position) self.toolBar_spectrum.addAction(self.actionPick_position)
self.toolBar_data.addAction(self.actionConcatenate_sets) self.toolBar_data.addAction(self.actionConcatenate_sets)
@ -521,6 +525,7 @@ class Ui_BaseWindow(object):
self.menuFile.setTitle(_translate("BaseWindow", "&File")) self.menuFile.setTitle(_translate("BaseWindow", "&File"))
self.menuSave.setTitle(_translate("BaseWindow", "&Save...")) self.menuSave.setTitle(_translate("BaseWindow", "&Save..."))
self.menuData.setTitle(_translate("BaseWindow", "&Data")) self.menuData.setTitle(_translate("BaseWindow", "&Data"))
self.menuCut_to_visible_range.setTitle(_translate("BaseWindow", "Cut to visible range"))
self.menuHelp.setTitle(_translate("BaseWindow", "&Help")) self.menuHelp.setTitle(_translate("BaseWindow", "&Help"))
self.menuExtra.setTitle(_translate("BaseWindow", "Math")) self.menuExtra.setTitle(_translate("BaseWindow", "Math"))
self.menuNormalize.setTitle(_translate("BaseWindow", "&Normalize")) self.menuNormalize.setTitle(_translate("BaseWindow", "&Normalize"))
@ -537,7 +542,6 @@ class Ui_BaseWindow(object):
self.toolBar.setWindowTitle(_translate("BaseWindow", "Main")) self.toolBar.setWindowTitle(_translate("BaseWindow", "Main"))
self.toolbar_edit.setWindowTitle(_translate("BaseWindow", "Math")) self.toolbar_edit.setWindowTitle(_translate("BaseWindow", "Math"))
self.toolBar_nmr.setWindowTitle(_translate("BaseWindow", "NMR")) self.toolBar_nmr.setWindowTitle(_translate("BaseWindow", "NMR"))
self.toolBar_fit.setWindowTitle(_translate("BaseWindow", "Fit"))
self.toolBar_spectrum.setWindowTitle(_translate("BaseWindow", "Spectrum")) self.toolBar_spectrum.setWindowTitle(_translate("BaseWindow", "Spectrum"))
self.toolBar_data.setWindowTitle(_translate("BaseWindow", "Data")) self.toolBar_data.setWindowTitle(_translate("BaseWindow", "Data"))
self.action_close.setText(_translate("BaseWindow", "&Quit")) self.action_close.setText(_translate("BaseWindow", "&Quit"))
@ -615,7 +619,6 @@ class Ui_BaseWindow(object):
self.actionIntegrate.setText(_translate("BaseWindow", "Integrate")) self.actionIntegrate.setText(_translate("BaseWindow", "Integrate"))
self.actionDerivation.setText(_translate("BaseWindow", "Differentiation...")) self.actionDerivation.setText(_translate("BaseWindow", "Differentiation..."))
self.actionIntegration.setText(_translate("BaseWindow", "Integration...")) 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.actionMove_between_plots.setText(_translate("BaseWindow", "Move sets..."))
self.actionBaseline.setText(_translate("BaseWindow", "Baseline...")) self.actionBaseline.setText(_translate("BaseWindow", "Baseline..."))
self.actionCalculateT1.setText(_translate("BaseWindow", "Calculate relaxation...")) self.actionCalculateT1.setText(_translate("BaseWindow", "Calculate relaxation..."))
@ -642,6 +645,12 @@ class Ui_BaseWindow(object):
self.actionTNMH_model.setText(_translate("BaseWindow", "Tg , Hodge, TNMH,,,")) self.actionTNMH_model.setText(_translate("BaseWindow", "Tg , Hodge, TNMH,,,"))
self.actionBinning.setText(_translate("BaseWindow", "Binning...")) self.actionBinning.setText(_translate("BaseWindow", "Binning..."))
self.actionTNMH.setText(_translate("BaseWindow", "TNMH...")) self.actionTNMH.setText(_translate("BaseWindow", "TNMH..."))
self.actionExclude_region.setText(_translate("BaseWindow", "Exclude region"))
self.action_cut_xaxis.setText(_translate("BaseWindow", "x axis"))
self.action_cut_xaxis.setToolTip(_translate("BaseWindow", "Remove data points outside visible x range."))
self.action_cut_yaxis.setText(_translate("BaseWindow", "y axis"))
self.action_cut_yaxis.setToolTip(_translate("BaseWindow", "Remove data points outside visible y range. Uses real part of points."))
self.actionUse_script.setText(_translate("BaseWindow", "Use script..."))
from ..data.datawidget.datawidget import DataWidget from ..data.datawidget.datawidget import DataWidget
from ..data.integral_widget import IntegralWidget from ..data.integral_widget import IntegralWidget
from ..data.point_select import PointSelectWidget from ..data.point_select import PointSelectWidget

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/datawidget.ui' # Form implementation generated from reading ui file './nmreval/src/resources/_ui/datawidget.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.10
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
@ -49,6 +50,12 @@ class Ui_DataWidget(object):
self.horizontalLayout.addWidget(self.func_toolButton) self.horizontalLayout.addWidget(self.func_toolButton)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem) self.horizontalLayout.addItem(spacerItem)
self.pokemon_toolbutton = QtWidgets.QToolButton(self.frame)
self.pokemon_toolbutton.setText("")
self.pokemon_toolbutton.setToolButtonStyle(QtCore.Qt.ToolButtonTextOnly)
self.pokemon_toolbutton.setAutoRaise(True)
self.pokemon_toolbutton.setObjectName("pokemon_toolbutton")
self.horizontalLayout.addWidget(self.pokemon_toolbutton)
self.verticalLayout_2.addWidget(self.frame) self.verticalLayout_2.addWidget(self.frame)
self.retranslateUi(DataWidget) self.retranslateUi(DataWidget)

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/fcreader.ui' # Form implementation generated from reading ui file 'src/resources/_ui/fcreader.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.10
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
@ -47,6 +48,7 @@ class Ui_FCEval_dialog(object):
self.verticalLayout.addWidget(self.input_box) self.verticalLayout.addWidget(self.input_box)
self.region_box = QtWidgets.QGroupBox(FCEval_dialog) self.region_box = QtWidgets.QGroupBox(FCEval_dialog)
self.region_box.setCheckable(True) self.region_box.setCheckable(True)
self.region_box.setChecked(False)
self.region_box.setObjectName("region_box") self.region_box.setObjectName("region_box")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.region_box) self.horizontalLayout = QtWidgets.QHBoxLayout(self.region_box)
self.horizontalLayout.setContentsMargins(3, 3, 3, 3) self.horizontalLayout.setContentsMargins(3, 3, 3, 3)
@ -139,6 +141,7 @@ class Ui_FCEval_dialog(object):
self.line.setObjectName("line") self.line.setObjectName("line")
self.gridLayout.addWidget(self.line, 2, 0, 1, 2) self.gridLayout.addWidget(self.line, 2, 0, 1, 2)
self.graph_comboBox = QtWidgets.QComboBox(self.out_box) self.graph_comboBox = QtWidgets.QComboBox(self.out_box)
self.graph_comboBox.setEnabled(False)
self.graph_comboBox.setObjectName("graph_comboBox") self.graph_comboBox.setObjectName("graph_comboBox")
self.gridLayout.addWidget(self.graph_comboBox, 3, 1, 1, 1) self.gridLayout.addWidget(self.graph_comboBox, 3, 1, 1, 1)
self.graph_checkbox = QtWidgets.QCheckBox(self.out_box) self.graph_checkbox = QtWidgets.QCheckBox(self.out_box)
@ -167,8 +170,8 @@ class Ui_FCEval_dialog(object):
self.label_6.setBuddy(self.m0_cb) self.label_6.setBuddy(self.m0_cb)
self.retranslateUi(FCEval_dialog) self.retranslateUi(FCEval_dialog)
self.buttonBox.accepted.connect(FCEval_dialog.accept) self.buttonBox.accepted.connect(FCEval_dialog.accept) # type: ignore
self.buttonBox.rejected.connect(FCEval_dialog.reject) self.buttonBox.rejected.connect(FCEval_dialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(FCEval_dialog) QtCore.QMetaObject.connectSlotsByName(FCEval_dialog)
def retranslateUi(self, FCEval_dialog): def retranslateUi(self, FCEval_dialog):
@ -178,7 +181,7 @@ class Ui_FCEval_dialog(object):
self.file_pushbutton.setText(_translate("FCEval_dialog", "Add HDF files...")) self.file_pushbutton.setText(_translate("FCEval_dialog", "Add HDF files..."))
self.dir_pushbutton.setText(_translate("FCEval_dialog", "Add directory...")) self.dir_pushbutton.setText(_translate("FCEval_dialog", "Add directory..."))
self.overwrite_cb.setText(_translate("FCEval_dialog", "Overwrite prev. data")) 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.region_box.setTitle(_translate("FCEval_dialog", "Evaluate region (empty values default to values of the script)"))
self.start_lineedit.setPlaceholderText(_translate("FCEval_dialog", "start pos in µs")) self.start_lineedit.setPlaceholderText(_translate("FCEval_dialog", "start pos in µs"))
self.stop_lineedit.setPlaceholderText(_translate("FCEval_dialog", "end pos in µs")) self.stop_lineedit.setPlaceholderText(_translate("FCEval_dialog", "end pos in µs"))
self.fit_box.setTitle(_translate("FCEval_dialog", "Fit equation")) self.fit_box.setTitle(_translate("FCEval_dialog", "Fit equation"))

View File

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'resources/_ui/fitcreationdialog.ui' # Form implementation generated from reading ui file 'resources/_ui/fitcreationdialog.ui'
# #
# Created by: PyQt5 UI code generator 5.15.4 # Created by: PyQt5 UI code generator 5.15.10
# #
# WARNING: Any manual changes made to this file will be lost when pyuic5 is # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing. # run again. Do not edit this file unless you know what you are doing.
@ -51,9 +51,8 @@ class Ui_Dialog(object):
self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.namespace_box) self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.namespace_box)
self.verticalLayout_6.setObjectName("verticalLayout_6") self.verticalLayout_6.setObjectName("verticalLayout_6")
self.tabWidget.addTab(self.namespace_box, "") self.tabWidget.addTab(self.namespace_box, "")
self.plainTextEdit = CodeEditor(self.splitter) self.editor = EditorWidget(self.splitter)
self.plainTextEdit.setEnabled(True) self.editor.setObjectName("editor")
self.plainTextEdit.setObjectName("plainTextEdit")
self.verticalLayout.addWidget(self.splitter) self.verticalLayout.addWidget(self.splitter)
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
@ -63,8 +62,8 @@ class Ui_Dialog(object):
self.retranslateUi(Dialog) self.retranslateUi(Dialog)
self.tabWidget.setCurrentIndex(0) self.tabWidget.setCurrentIndex(0)
self.buttonBox.accepted.connect(Dialog.accept) self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
self.buttonBox.rejected.connect(Dialog.reject) self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(Dialog) QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog): def retranslateUi(self, Dialog):
@ -74,4 +73,4 @@ class Ui_Dialog(object):
self.tabWidget.setTabText(self.tabWidget.indexOf(self.args_box), _translate("Dialog", "Variables")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.args_box), _translate("Dialog", "Variables"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.kwargs_box), _translate("Dialog", "Multiple choice")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.kwargs_box), _translate("Dialog", "Multiple choice"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.namespace_box), _translate("Dialog", "Available Functions")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.namespace_box), _translate("Dialog", "Available Functions"))
from ..lib.codeeditor import CodeEditor from ..lib.codeeditor import EditorWidget

View File

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'src/resources/_ui/fitmodelwidget.ui' # Form implementation generated from reading ui file 'src/resources/_ui/fitmodelwidget.ui'
# #
# Created by: PyQt5 UI code generator 5.15.9 # Created by: PyQt5 UI code generator 5.15.10
# #
# WARNING: Any manual changes made to this file will be lost when pyuic5 is # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing. # run again. Do not edit this file unless you know what you are doing.
@ -42,6 +42,7 @@ class Ui_FitParameter(object):
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.parameter_line.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.parameter_line.sizePolicy().hasHeightForWidth())
self.parameter_line.setSizePolicy(sizePolicy) self.parameter_line.setSizePolicy(sizePolicy)
self.parameter_line.setMaximumSize(QtCore.QSize(160, 16777215))
self.parameter_line.setText("") self.parameter_line.setText("")
self.parameter_line.setObjectName("parameter_line") self.parameter_line.setObjectName("parameter_line")
self.horizontalLayout_2.addWidget(self.parameter_line) self.horizontalLayout_2.addWidget(self.parameter_line)
@ -51,6 +52,9 @@ class Ui_FitParameter(object):
self.global_checkbox = QtWidgets.QCheckBox(FitParameter) self.global_checkbox = QtWidgets.QCheckBox(FitParameter)
self.global_checkbox.setObjectName("global_checkbox") self.global_checkbox.setObjectName("global_checkbox")
self.horizontalLayout_2.addWidget(self.global_checkbox) self.horizontalLayout_2.addWidget(self.global_checkbox)
self.reset_button = QtWidgets.QPushButton(FitParameter)
self.reset_button.setObjectName("reset_button")
self.horizontalLayout_2.addWidget(self.reset_button)
self.verticalLayout.addLayout(self.horizontalLayout_2) self.verticalLayout.addLayout(self.horizontalLayout_2)
self.frame = QtWidgets.QFrame(FitParameter) self.frame = QtWidgets.QFrame(FitParameter)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
@ -82,6 +86,7 @@ class Ui_FitParameter(object):
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEdit.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.lineEdit.sizePolicy().hasHeightForWidth())
self.lineEdit.setSizePolicy(sizePolicy) self.lineEdit.setSizePolicy(sizePolicy)
self.lineEdit.setMaximumSize(QtCore.QSize(100, 16777215))
self.lineEdit.setText("") self.lineEdit.setText("")
self.lineEdit.setFrame(True) self.lineEdit.setFrame(True)
self.lineEdit.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) self.lineEdit.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
@ -100,6 +105,7 @@ class Ui_FitParameter(object):
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEdit_2.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.lineEdit_2.sizePolicy().hasHeightForWidth())
self.lineEdit_2.setSizePolicy(sizePolicy) self.lineEdit_2.setSizePolicy(sizePolicy)
self.lineEdit_2.setMaximumSize(QtCore.QSize(100, 16777215))
self.lineEdit_2.setText("") self.lineEdit_2.setText("")
self.lineEdit_2.setFrame(True) self.lineEdit_2.setFrame(True)
self.lineEdit_2.setObjectName("lineEdit_2") self.lineEdit_2.setObjectName("lineEdit_2")
@ -122,6 +128,7 @@ class Ui_FitParameter(object):
self.parameter_line.setPlaceholderText(_translate("FitParameter", "0")) self.parameter_line.setPlaceholderText(_translate("FitParameter", "0"))
self.fixed_check.setText(_translate("FitParameter", "Fix")) self.fixed_check.setText(_translate("FitParameter", "Fix"))
self.global_checkbox.setText(_translate("FitParameter", "Global")) self.global_checkbox.setText(_translate("FitParameter", "Global"))
self.reset_button.setText(_translate("FitParameter", "Use global"))
self.lineEdit.setToolTip(_translate("FitParameter", "<html><head/><body><p>Lower bound. Same bound is used for all data. Leave empty for no boundary condition.</p></body></html>")) self.lineEdit.setToolTip(_translate("FitParameter", "<html><head/><body><p>Lower bound. Same bound is used for all data. Leave empty for no boundary condition.</p></body></html>"))
self.label_3.setText(_translate("FitParameter", "Textlabel")) self.label_3.setText(_translate("FitParameter", "Textlabel"))
self.lineEdit_2.setToolTip(_translate("FitParameter", "<html><head/><body><p>Upper bound. Same bound is used for all data. Leave empty for no boundary condition.</p></body></html>")) self.lineEdit_2.setToolTip(_translate("FitParameter", "<html><head/><body><p>Upper bound. Same bound is used for all data. Leave empty for no boundary condition.</p></body></html>"))

View File

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'src/resources/_ui/fitresult.ui' # Form implementation generated from reading ui file 'src/resources/_ui/fitresult.ui'
# #
# Created by: PyQt5 UI code generator 5.15.9 # Created by: PyQt5 UI code generator 5.15.10
# #
# WARNING: Any manual changes made to this file will be lost when pyuic5 is # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing. # run again. Do not edit this file unless you know what you are doing.
@ -27,20 +27,48 @@ class Ui_Dialog(object):
self.stackPage1 = QtWidgets.QWidget() self.stackPage1 = QtWidgets.QWidget()
self.stackPage1.setObjectName("stackPage1") self.stackPage1.setObjectName("stackPage1")
self.gridLayout_3 = QtWidgets.QGridLayout(self.stackPage1) self.gridLayout_3 = QtWidgets.QGridLayout(self.stackPage1)
self.gridLayout_3.setContentsMargins(3, 3, 3, 3) self.gridLayout_3.setContentsMargins(6, 3, 6, 3)
self.gridLayout_3.setSpacing(3) self.gridLayout_3.setSpacing(3)
self.gridLayout_3.setObjectName("gridLayout_3") self.gridLayout_3.setObjectName("gridLayout_3")
self.logy_box = QtWidgets.QCheckBox(self.stackPage1) spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.logy_box.setLayoutDirection(QtCore.Qt.RightToLeft) self.gridLayout_3.addItem(spacerItem, 2, 3, 1, 1)
self.logy_box.setObjectName("logy_box") self.autoscale_box = QtWidgets.QToolButton(self.stackPage1)
self.gridLayout_3.addWidget(self.logy_box, 2, 1, 1, 1) self.autoscale_box.setObjectName("autoscale_box")
self.logx_box = QtWidgets.QCheckBox(self.stackPage1) self.gridLayout_3.addWidget(self.autoscale_box, 2, 4, 1, 1)
self.logx_box.setLayoutDirection(QtCore.Qt.RightToLeft) spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.gridLayout_3.addItem(spacerItem1, 2, 1, 1, 1)
self.verticalGroupBox_2 = QtWidgets.QGroupBox(self.stackPage1)
self.verticalGroupBox_2.setObjectName("verticalGroupBox_2")
self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.verticalGroupBox_2)
self.verticalLayout_4.setObjectName("verticalLayout_4")
self.logx_box = QtWidgets.QCheckBox(self.verticalGroupBox_2)
self.logx_box.setLayoutDirection(QtCore.Qt.LeftToRight)
self.logx_box.setObjectName("logx_box") self.logx_box.setObjectName("logx_box")
self.gridLayout_3.addWidget(self.logx_box, 2, 0, 1, 1) self.verticalLayout_4.addWidget(self.logx_box)
self.logy_box = QtWidgets.QCheckBox(self.verticalGroupBox_2)
self.logy_box.setLayoutDirection(QtCore.Qt.LeftToRight)
self.logy_box.setObjectName("logy_box")
self.verticalLayout_4.addWidget(self.logy_box)
self.gridLayout_3.addWidget(self.verticalGroupBox_2, 2, 2, 1, 1)
self.verticalGroupBox = QtWidgets.QGroupBox(self.stackPage1)
self.verticalGroupBox.setObjectName("verticalGroupBox")
self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalGroupBox)
self.verticalLayout.setObjectName("verticalLayout")
self.rel_dev_button = QtWidgets.QRadioButton(self.verticalGroupBox)
self.rel_dev_button.setObjectName("rel_dev_button")
self.buttonGroup = QtWidgets.QButtonGroup(Dialog)
self.buttonGroup.setObjectName("buttonGroup")
self.buttonGroup.addButton(self.rel_dev_button)
self.verticalLayout.addWidget(self.rel_dev_button)
self.abs_dev_button = QtWidgets.QRadioButton(self.verticalGroupBox)
self.abs_dev_button.setChecked(True)
self.abs_dev_button.setObjectName("abs_dev_button")
self.buttonGroup.addButton(self.abs_dev_button)
self.verticalLayout.addWidget(self.abs_dev_button)
self.gridLayout_3.addWidget(self.verticalGroupBox, 2, 0, 1, 1)
self.graphicsView = GraphicsLayoutWidget(self.stackPage1) self.graphicsView = GraphicsLayoutWidget(self.stackPage1)
self.graphicsView.setObjectName("graphicsView") self.graphicsView.setObjectName("graphicsView")
self.gridLayout_3.addWidget(self.graphicsView, 0, 0, 1, 2) self.gridLayout_3.addWidget(self.graphicsView, 0, 0, 1, 5)
self.stack.addTab(self.stackPage1, "") self.stack.addTab(self.stackPage1, "")
self.stackPage2 = QtWidgets.QWidget() self.stackPage2 = QtWidgets.QWidget()
self.stackPage2.setObjectName("stackPage2") self.stackPage2.setObjectName("stackPage2")
@ -84,10 +112,6 @@ class Ui_Dialog(object):
self.verticalLayout_3.addWidget(self.corr_tableWidget) self.verticalLayout_3.addWidget(self.corr_tableWidget)
self.stack.addTab(self.stackPage3, "") self.stack.addTab(self.stackPage3, "")
self.gridLayout.addWidget(self.stack, 0, 1, 5, 1) self.gridLayout.addWidget(self.stack, 0, 1, 5, 1)
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Retry)
self.buttonBox.setObjectName("buttonBox")
self.gridLayout.addWidget(self.buttonBox, 8, 0, 1, 2)
self.param_tableWidget = QtWidgets.QTableWidget(Dialog) self.param_tableWidget = QtWidgets.QTableWidget(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
@ -113,74 +137,6 @@ class Ui_Dialog(object):
self.line.setFrameShadow(QtWidgets.QFrame.Sunken) self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
self.line.setObjectName("line") self.line.setObjectName("line")
self.gridLayout.addWidget(self.line, 5, 0, 1, 2) self.gridLayout.addWidget(self.line, 5, 0, 1, 2)
self.groupBox = QtWidgets.QGroupBox(Dialog)
self.groupBox.setObjectName("groupBox")
self.gridLayout_2 = QtWidgets.QGridLayout(self.groupBox)
self.gridLayout_2.setContentsMargins(3, 3, 3, 3)
self.gridLayout_2.setSpacing(3)
self.gridLayout_2.setObjectName("gridLayout_2")
self.extrapolate_box = QtWidgets.QCheckBox(self.groupBox)
self.extrapolate_box.setObjectName("extrapolate_box")
self.gridLayout_2.addWidget(self.extrapolate_box, 1, 0, 1, 1)
self.parameter_checkbox = QtWidgets.QCheckBox(self.groupBox)
self.parameter_checkbox.setObjectName("parameter_checkbox")
self.gridLayout_2.addWidget(self.parameter_checkbox, 0, 5, 1, 1)
self.graph_comboBox = QtWidgets.QComboBox(self.groupBox)
self.graph_comboBox.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.graph_comboBox.sizePolicy().hasHeightForWidth())
self.graph_comboBox.setSizePolicy(sizePolicy)
self.graph_comboBox.setObjectName("graph_comboBox")
self.gridLayout_2.addWidget(self.graph_comboBox, 1, 6, 1, 1)
self.graph_checkBox = QtWidgets.QCheckBox(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.graph_checkBox.sizePolicy().hasHeightForWidth())
self.graph_checkBox.setSizePolicy(sizePolicy)
self.graph_checkBox.setChecked(True)
self.graph_checkBox.setObjectName("graph_checkBox")
self.gridLayout_2.addWidget(self.graph_checkBox, 1, 5, 1, 1)
self.minx_line = QtWidgets.QLineEdit(self.groupBox)
self.minx_line.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.minx_line.sizePolicy().hasHeightForWidth())
self.minx_line.setSizePolicy(sizePolicy)
self.minx_line.setObjectName("minx_line")
self.gridLayout_2.addWidget(self.minx_line, 1, 1, 1, 1)
self.line_2 = QtWidgets.QFrame(self.groupBox)
self.line_2.setFrameShape(QtWidgets.QFrame.VLine)
self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
self.line_2.setObjectName("line_2")
self.gridLayout_2.addWidget(self.line_2, 0, 4, 2, 1)
self.maxx_line = QtWidgets.QLineEdit(self.groupBox)
self.maxx_line.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.maxx_line.sizePolicy().hasHeightForWidth())
self.maxx_line.setSizePolicy(sizePolicy)
self.maxx_line.setObjectName("maxx_line")
self.gridLayout_2.addWidget(self.maxx_line, 1, 2, 1, 1)
self.numx_line = QtWidgets.QLineEdit(self.groupBox)
self.numx_line.setEnabled(False)
self.numx_line.setObjectName("numx_line")
self.gridLayout_2.addWidget(self.numx_line, 1, 3, 1, 1)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.curve_checkbox = QtWidgets.QCheckBox(self.groupBox)
self.curve_checkbox.setChecked(True)
self.curve_checkbox.setObjectName("curve_checkbox")
self.horizontalLayout.addWidget(self.curve_checkbox)
self.partial_checkBox = QtWidgets.QCheckBox(self.groupBox)
self.partial_checkBox.setObjectName("partial_checkBox")
self.horizontalLayout.addWidget(self.partial_checkBox)
self.gridLayout_2.addLayout(self.horizontalLayout, 0, 0, 1, 4)
self.gridLayout.addWidget(self.groupBox, 7, 0, 1, 2)
self.sets_comboBox = ElideComboBox(Dialog) self.sets_comboBox = ElideComboBox(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
@ -195,6 +151,102 @@ class Ui_Dialog(object):
self.reject_fit_checkBox = QtWidgets.QCheckBox(Dialog) self.reject_fit_checkBox = QtWidgets.QCheckBox(Dialog)
self.reject_fit_checkBox.setObjectName("reject_fit_checkBox") self.reject_fit_checkBox.setObjectName("reject_fit_checkBox")
self.gridLayout.addWidget(self.reject_fit_checkBox, 2, 0, 1, 1) self.gridLayout.addWidget(self.reject_fit_checkBox, 2, 0, 1, 1)
self.groupBox = QtWidgets.QGroupBox(Dialog)
self.groupBox.setObjectName("groupBox")
self.gridLayout_2 = QtWidgets.QGridLayout(self.groupBox)
self.gridLayout_2.setContentsMargins(3, 3, 3, 3)
self.gridLayout_2.setSpacing(3)
self.gridLayout_2.setObjectName("gridLayout_2")
self.graph_comboBox = QtWidgets.QComboBox(self.groupBox)
self.graph_comboBox.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.graph_comboBox.sizePolicy().hasHeightForWidth())
self.graph_comboBox.setSizePolicy(sizePolicy)
self.graph_comboBox.setObjectName("graph_comboBox")
self.gridLayout_2.addWidget(self.graph_comboBox, 1, 7, 1, 1)
self.minx_line = QtWidgets.QLineEdit(self.groupBox)
self.minx_line.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.minx_line.sizePolicy().hasHeightForWidth())
self.minx_line.setSizePolicy(sizePolicy)
self.minx_line.setObjectName("minx_line")
self.gridLayout_2.addWidget(self.minx_line, 1, 1, 1, 1)
self.extrapolate_box = QtWidgets.QCheckBox(self.groupBox)
self.extrapolate_box.setObjectName("extrapolate_box")
self.gridLayout_2.addWidget(self.extrapolate_box, 1, 0, 1, 1)
self.numx_line = QtWidgets.QLineEdit(self.groupBox)
self.numx_line.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.numx_line.sizePolicy().hasHeightForWidth())
self.numx_line.setSizePolicy(sizePolicy)
self.numx_line.setObjectName("numx_line")
self.gridLayout_2.addWidget(self.numx_line, 1, 3, 1, 1)
self.graph_checkBox = QtWidgets.QCheckBox(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.graph_checkBox.sizePolicy().hasHeightForWidth())
self.graph_checkBox.setSizePolicy(sizePolicy)
self.graph_checkBox.setChecked(True)
self.graph_checkBox.setObjectName("graph_checkBox")
self.gridLayout_2.addWidget(self.graph_checkBox, 1, 6, 1, 1)
self.maxx_line = QtWidgets.QLineEdit(self.groupBox)
self.maxx_line.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.maxx_line.sizePolicy().hasHeightForWidth())
self.maxx_line.setSizePolicy(sizePolicy)
self.maxx_line.setObjectName("maxx_line")
self.gridLayout_2.addWidget(self.maxx_line, 1, 2, 1, 1)
self.line_2 = QtWidgets.QFrame(self.groupBox)
self.line_2.setFrameShape(QtWidgets.QFrame.VLine)
self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
self.line_2.setObjectName("line_2")
self.gridLayout_2.addWidget(self.line_2, 0, 5, 2, 1)
self.newx_log_checkbox = QtWidgets.QCheckBox(self.groupBox)
self.newx_log_checkbox.setEnabled(False)
self.newx_log_checkbox.setObjectName("newx_log_checkbox")
self.gridLayout_2.addWidget(self.newx_log_checkbox, 1, 4, 1, 1)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.curve_checkbox = QtWidgets.QCheckBox(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.curve_checkbox.sizePolicy().hasHeightForWidth())
self.curve_checkbox.setSizePolicy(sizePolicy)
self.curve_checkbox.setChecked(True)
self.curve_checkbox.setObjectName("curve_checkbox")
self.horizontalLayout.addWidget(self.curve_checkbox)
self.partial_checkBox = QtWidgets.QCheckBox(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.partial_checkBox.sizePolicy().hasHeightForWidth())
self.partial_checkBox.setSizePolicy(sizePolicy)
self.partial_checkBox.setObjectName("partial_checkBox")
self.horizontalLayout.addWidget(self.partial_checkBox)
self.gridLayout_2.addLayout(self.horizontalLayout, 0, 0, 1, 5)
self.parameter_checkbox = QtWidgets.QCheckBox(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.parameter_checkbox.sizePolicy().hasHeightForWidth())
self.parameter_checkbox.setSizePolicy(sizePolicy)
self.parameter_checkbox.setObjectName("parameter_checkbox")
self.gridLayout_2.addWidget(self.parameter_checkbox, 0, 6, 1, 2)
self.gridLayout.addWidget(self.groupBox, 7, 0, 1, 2)
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Retry)
self.buttonBox.setObjectName("buttonBox")
self.gridLayout.addWidget(self.buttonBox, 8, 0, 1, 2)
self.retranslateUi(Dialog) self.retranslateUi(Dialog)
self.stack.setCurrentIndex(0) self.stack.setCurrentIndex(0)
@ -203,8 +255,14 @@ class Ui_Dialog(object):
def retranslateUi(self, Dialog): def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Fit results")) Dialog.setWindowTitle(_translate("Dialog", "Fit results"))
self.logy_box.setText(_translate("Dialog", "logarithmic y axis")) self.autoscale_box.setToolTip(_translate("Dialog", "Auto-scale graph for all sets"))
self.logx_box.setText(_translate("Dialog", "logarithmic x axis")) self.autoscale_box.setText(_translate("Dialog", "Autoscale all sets"))
self.verticalGroupBox_2.setTitle(_translate("Dialog", "Logarithmic axes"))
self.logx_box.setText(_translate("Dialog", "x axis"))
self.logy_box.setText(_translate("Dialog", "y axis"))
self.verticalGroupBox.setTitle(_translate("Dialog", "Residuals"))
self.rel_dev_button.setText(_translate("Dialog", "relative deviation"))
self.abs_dev_button.setText(_translate("Dialog", "absolute deviation"))
self.stack.setTabText(self.stack.indexOf(self.stackPage1), _translate("Dialog", "Plot")) self.stack.setTabText(self.stack.indexOf(self.stackPage1), _translate("Dialog", "Plot"))
self.stack.setTabText(self.stack.indexOf(self.stackPage2), _translate("Dialog", "Statistics")) self.stack.setTabText(self.stack.indexOf(self.stackPage2), _translate("Dialog", "Statistics"))
item = self.corr_tableWidget.horizontalHeaderItem(0) item = self.corr_tableWidget.horizontalHeaderItem(0)
@ -217,18 +275,19 @@ class Ui_Dialog(object):
item.setText(_translate("Dialog", "Partial Corr.")) item.setText(_translate("Dialog", "Partial Corr."))
self.stack.setTabText(self.stack.indexOf(self.stackPage3), _translate("Dialog", "Correlations")) self.stack.setTabText(self.stack.indexOf(self.stackPage3), _translate("Dialog", "Correlations"))
self.del_prev_checkBox.setText(_translate("Dialog", "Delete previous fits of this set")) self.del_prev_checkBox.setText(_translate("Dialog", "Delete previous fits of this set"))
self.reject_fit_checkBox.setText(_translate("Dialog", "Reject this fit"))
self.groupBox.setTitle(_translate("Dialog", "Output")) self.groupBox.setTitle(_translate("Dialog", "Output"))
self.extrapolate_box.setToolTip(_translate("Dialog", "Extrapolates only main function"))
self.extrapolate_box.setText(_translate("Dialog", "Extrapolate curves"))
self.parameter_checkbox.setText(_translate("Dialog", "Plot parameter"))
self.graph_checkBox.setText(_translate("Dialog", "New graph for parameter"))
self.minx_line.setToolTip(_translate("Dialog", "Leave empty to start at lowest point")) self.minx_line.setToolTip(_translate("Dialog", "Leave empty to start at lowest point"))
self.minx_line.setPlaceholderText(_translate("Dialog", "min x")) self.minx_line.setPlaceholderText(_translate("Dialog", "min x"))
self.extrapolate_box.setToolTip(_translate("Dialog", "Extrapolates only main function"))
self.extrapolate_box.setText(_translate("Dialog", "Extrapolate curves"))
self.numx_line.setPlaceholderText(_translate("Dialog", "# pts"))
self.graph_checkBox.setText(_translate("Dialog", "New graph for parameter"))
self.maxx_line.setToolTip(_translate("Dialog", "Leave empty to start at highest point")) self.maxx_line.setToolTip(_translate("Dialog", "Leave empty to start at highest point"))
self.maxx_line.setPlaceholderText(_translate("Dialog", "max x")) self.maxx_line.setPlaceholderText(_translate("Dialog", "max x"))
self.numx_line.setPlaceholderText(_translate("Dialog", "# pts")) self.newx_log_checkbox.setText(_translate("Dialog", "log-spaced?"))
self.curve_checkbox.setText(_translate("Dialog", "Plot fit curve")) self.curve_checkbox.setText(_translate("Dialog", "Plot fit curve"))
self.partial_checkBox.setText(_translate("Dialog", "Plot partial functions")) self.partial_checkBox.setText(_translate("Dialog", "Plot partial functions"))
self.reject_fit_checkBox.setText(_translate("Dialog", "Reject this fit")) self.parameter_checkbox.setText(_translate("Dialog", "Plot parameter"))
from ..lib.forms import ElideComboBox from ..lib.forms import ElideComboBox
from pyqtgraph import GraphicsLayoutWidget from pyqtgraph import GraphicsLayoutWidget

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'src/resources/_ui/graph.ui' # Form implementation generated from reading ui file 'resources/_ui/graph.ui'
# #
# Created by: PyQt5 UI code generator 5.15.9 # Created by: PyQt5 UI code generator 5.15.10
# #
# WARNING: Any manual changes made to this file will be lost when pyuic5 is # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing. # run again. Do not edit this file unless you know what you are doing.
@ -271,8 +271,11 @@ class Ui_GraphWindow(object):
self.label_4.setText(_translate("GraphWindow", "---")) self.label_4.setText(_translate("GraphWindow", "---"))
self.apply_button.setText(_translate("GraphWindow", "Apply")) self.apply_button.setText(_translate("GraphWindow", "Apply"))
self.label_5.setText(_translate("GraphWindow", "Title")) self.label_5.setText(_translate("GraphWindow", "Title"))
self.title_lineedit.setToolTip(_translate("GraphWindow", "<html><head/><body><p>Uses simple latex syntax, does not support italic/math environment. Sub-/superscripts need curly brackets.<br/></p><p>Example: \\alpha^{123}</p></body></html>"))
self.label_6.setText(_translate("GraphWindow", "X Axis")) self.label_6.setText(_translate("GraphWindow", "X Axis"))
self.xaxis_linedit.setToolTip(_translate("GraphWindow", "<html><head/><body><p>Uses simple latex syntax, does not support italic/math environment. Sub-/superscripts need curly brackets.<br/></p><p>Example: \\alpha^{123}</p></body></html>"))
self.label_7.setText(_translate("GraphWindow", "Y Axis")) self.label_7.setText(_translate("GraphWindow", "Y Axis"))
self.yaxis_linedit.setToolTip(_translate("GraphWindow", "<html><head/><body><p>Uses simple latex syntax, does not support italic/math environment. Sub-/superscripts need curly brackets.<br/></p><p>Example: \\alpha^{123}</p></body></html>"))
self.checkBox.setText(_translate("GraphWindow", "Show legend")) self.checkBox.setText(_translate("GraphWindow", "Show legend"))
from ..lib.graph_items import NMRPlotWidget from ..lib.graph_items import NMRPlotWidget
from ..lib.listwidget import QListWidgetSelect from ..lib.listwidget import QListWidgetSelect

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'src/resources/_ui/interpol_dialog.ui' # Form implementation generated from reading ui file './nmreval/src/resources/_ui/interpol_dialog.ui'
# #
# Created by: PyQt5 UI code generator 5.15.9 # Created by: PyQt5 UI code generator 5.15.10
# #
# WARNING: Any manual changes made to this file will be lost when pyuic5 is # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing. # run again. Do not edit this file unless you know what you are doing.
@ -55,7 +55,7 @@ class Ui_Dialog(object):
self.gridLayout.addWidget(self.interp_comboBox, 4, 1, 1, 1) self.gridLayout.addWidget(self.interp_comboBox, 4, 1, 1, 1)
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox") self.buttonBox.setObjectName("buttonBox")
self.gridLayout.addWidget(self.buttonBox, 12, 0, 1, 2) self.gridLayout.addWidget(self.buttonBox, 12, 0, 1, 2)
self.line = QtWidgets.QFrame(Dialog) self.line = QtWidgets.QFrame(Dialog)
@ -132,8 +132,6 @@ class Ui_Dialog(object):
self.label_8.setBuddy(self.dest_combobox) self.label_8.setBuddy(self.dest_combobox)
self.retranslateUi(Dialog) self.retranslateUi(Dialog)
self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(Dialog) QtCore.QMetaObject.connectSlotsByName(Dialog)
Dialog.setTabOrder(self.listWidget, self.ylog_checkBox) Dialog.setTabOrder(self.listWidget, self.ylog_checkBox)
Dialog.setTabOrder(self.ylog_checkBox, self.interp_comboBox) Dialog.setTabOrder(self.ylog_checkBox, self.interp_comboBox)

260
src/gui_qt/_py/pokeentry.py Normal file
View File

@ -0,0 +1,260 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file './nmreval/src/resources/_ui/pokeentry.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(640, 642)
self.verticalLayout_4 = QtWidgets.QVBoxLayout(Form)
self.verticalLayout_4.setObjectName("verticalLayout_4")
self.artwork_label = QtWidgets.QLabel(Form)
self.artwork_label.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
self.artwork_label.setObjectName("artwork_label")
self.verticalLayout_4.addWidget(self.artwork_label)
self.formLayout = QtWidgets.QFormLayout()
self.formLayout.setContentsMargins(3, 3, 3, 3)
self.formLayout.setVerticalSpacing(12)
self.formLayout.setObjectName("formLayout")
self.label = QtWidgets.QLabel(Form)
self.label.setObjectName("label")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label)
self.nationaldex_label = QtWidgets.QLabel(Form)
self.nationaldex_label.setObjectName("nationaldex_label")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.nationaldex_label)
self.label_1 = QtWidgets.QLabel(Form)
self.label_1.setObjectName("label_1")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_1)
self.species_label = QtWidgets.QLabel(Form)
self.species_label.setObjectName("species_label")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.species_label)
self.label_4 = QtWidgets.QLabel(Form)
self.label_4.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
self.label_4.setObjectName("label_4")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_4)
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.type1_label = QtWidgets.QLabel(Form)
self.type1_label.setObjectName("type1_label")
self.verticalLayout_2.addWidget(self.type1_label)
self.type2_label = QtWidgets.QLabel(Form)
self.type2_label.setObjectName("type2_label")
self.verticalLayout_2.addWidget(self.type2_label)
self.formLayout.setLayout(2, QtWidgets.QFormLayout.FieldRole, self.verticalLayout_2)
self.label_5 = QtWidgets.QLabel(Form)
self.label_5.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
self.label_5.setObjectName("label_5")
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_5)
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.ability1_label = QtWidgets.QLabel(Form)
self.ability1_label.setObjectName("ability1_label")
self.verticalLayout.addWidget(self.ability1_label)
self.ability2_label = QtWidgets.QLabel(Form)
self.ability2_label.setObjectName("ability2_label")
self.verticalLayout.addWidget(self.ability2_label)
self.ability3_label = QtWidgets.QLabel(Form)
self.ability3_label.setObjectName("ability3_label")
self.verticalLayout.addWidget(self.ability3_label)
self.formLayout.setLayout(3, QtWidgets.QFormLayout.FieldRole, self.verticalLayout)
self.label_2 = QtWidgets.QLabel(Form)
self.label_2.setObjectName("label_2")
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_2)
self.height_label = QtWidgets.QLabel(Form)
self.height_label.setObjectName("height_label")
self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.height_label)
self.label_3 = QtWidgets.QLabel(Form)
self.label_3.setObjectName("label_3")
self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.label_3)
self.weight_label = QtWidgets.QLabel(Form)
self.weight_label.setObjectName("weight_label")
self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.weight_label)
self.gender_label = QtWidgets.QLabel(Form)
self.gender_label.setObjectName("gender_label")
self.formLayout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.gender_label)
self.verticalLayout_4.addLayout(self.formLayout)
self.groupBox = QtWidgets.QGroupBox(Form)
self.groupBox.setFlat(True)
self.groupBox.setObjectName("groupBox")
self.gridLayout_4 = QtWidgets.QGridLayout(self.groupBox)
self.gridLayout_4.setContentsMargins(3, 3, 3, 3)
self.gridLayout_4.setObjectName("gridLayout_4")
self.spec_attack_bar = QtWidgets.QProgressBar(self.groupBox)
self.spec_attack_bar.setStyleSheet("QProgressBar{\n"
" border: none;\n"
" background-color: transparent;\n"
" text-align: center;\n"
"}\n"
"\n"
"QProgressBar::chunk {\n"
" background-color: #009cda;\n"
" border-radius: 3px;\n"
"}")
self.spec_attack_bar.setMaximum(194)
self.spec_attack_bar.setProperty("value", 24)
self.spec_attack_bar.setObjectName("spec_attack_bar")
self.gridLayout_4.addWidget(self.spec_attack_bar, 0, 3, 1, 1)
self.label_8 = QtWidgets.QLabel(self.groupBox)
self.label_8.setObjectName("label_8")
self.gridLayout_4.addWidget(self.label_8, 0, 2, 1, 1)
self.spec_defense_bar = QtWidgets.QProgressBar(self.groupBox)
self.spec_defense_bar.setStyleSheet("QProgressBar{\n"
" border: none;\n"
" background-color: transparent;\n"
" text-align: center;\n"
"}\n"
"\n"
"QProgressBar::chunk {\n"
" background-color: #009cda;\n"
" border-radius: 3px;\n"
"}")
self.spec_defense_bar.setMaximum(250)
self.spec_defense_bar.setProperty("value", 24)
self.spec_defense_bar.setObjectName("spec_defense_bar")
self.gridLayout_4.addWidget(self.spec_defense_bar, 1, 3, 1, 1)
self.label_6 = QtWidgets.QLabel(self.groupBox)
self.label_6.setFrameShadow(QtWidgets.QFrame.Plain)
self.label_6.setObjectName("label_6")
self.gridLayout_4.addWidget(self.label_6, 0, 0, 1, 1)
self.hp_bar = QtWidgets.QProgressBar(self.groupBox)
self.hp_bar.setStyleSheet("QProgressBar{\n"
" border: none;\n"
" background-color: transparent;\n"
" text-align: center;\n"
"}\n"
"\n"
"QProgressBar::chunk {\n"
" background-color: #009cda;\n"
" border-radius: 3px;\n"
"}")
self.hp_bar.setMaximum(255)
self.hp_bar.setProperty("value", 24)
self.hp_bar.setObjectName("hp_bar")
self.gridLayout_4.addWidget(self.hp_bar, 0, 1, 1, 1)
self.speed_bar = QtWidgets.QProgressBar(self.groupBox)
self.speed_bar.setStyleSheet("QProgressBar{\n"
" border: none;\n"
" background-color: transparent;\n"
" text-align: center;\n"
"}\n"
"\n"
"QProgressBar::chunk {\n"
" background-color: #009cda;\n"
" border-radius: 3px;\n"
"}")
self.speed_bar.setMaximum(200)
self.speed_bar.setProperty("value", 24)
self.speed_bar.setObjectName("speed_bar")
self.gridLayout_4.addWidget(self.speed_bar, 2, 3, 1, 1)
self.label_11 = QtWidgets.QLabel(self.groupBox)
self.label_11.setObjectName("label_11")
self.gridLayout_4.addWidget(self.label_11, 2, 2, 1, 1)
self.label_10 = QtWidgets.QLabel(self.groupBox)
self.label_10.setObjectName("label_10")
self.gridLayout_4.addWidget(self.label_10, 1, 2, 1, 1)
self.defense_bar = QtWidgets.QProgressBar(self.groupBox)
self.defense_bar.setStyleSheet("QProgressBar{\n"
" border: none;\n"
" background-color: transparent;\n"
" text-align: center;\n"
"}\n"
"\n"
"QProgressBar::chunk {\n"
" background-color: #009cda;\n"
" border-radius: 3px;\n"
"}")
self.defense_bar.setMaximum(250)
self.defense_bar.setProperty("value", 24)
self.defense_bar.setObjectName("defense_bar")
self.gridLayout_4.addWidget(self.defense_bar, 2, 1, 1, 1)
self.attack_bar = QtWidgets.QProgressBar(self.groupBox)
self.attack_bar.setStyleSheet("QProgressBar{\n"
" border: none;\n"
" background-color: transparent;\n"
" text-align: center;\n"
"}\n"
"\n"
"QProgressBar::chunk {\n"
" background-color: #009cda;\n"
" border-radius: 3px;\n"
"}")
self.attack_bar.setMaximum(190)
self.attack_bar.setProperty("value", 24)
self.attack_bar.setObjectName("attack_bar")
self.gridLayout_4.addWidget(self.attack_bar, 1, 1, 1, 1)
self.label_7 = QtWidgets.QLabel(self.groupBox)
self.label_7.setObjectName("label_7")
self.gridLayout_4.addWidget(self.label_7, 1, 0, 1, 1)
self.label_9 = QtWidgets.QLabel(self.groupBox)
self.label_9.setObjectName("label_9")
self.gridLayout_4.addWidget(self.label_9, 2, 0, 1, 1)
self.verticalLayout_4.addWidget(self.groupBox)
self.groupBox_2 = QtWidgets.QGroupBox(Form)
self.groupBox_2.setFlat(True)
self.groupBox_2.setCheckable(False)
self.groupBox_2.setObjectName("groupBox_2")
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.groupBox_2)
self.verticalLayout_3.setContentsMargins(3, 3, 3, 3)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.tableWidget = QtWidgets.QTableWidget(self.groupBox_2)
self.tableWidget.setStyleSheet("background-color: transparent;")
self.tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.tableWidget.setShowGrid(False)
self.tableWidget.setGridStyle(QtCore.Qt.NoPen)
self.tableWidget.setWordWrap(False)
self.tableWidget.setObjectName("tableWidget")
self.tableWidget.setColumnCount(0)
self.tableWidget.setRowCount(0)
self.tableWidget.horizontalHeader().setVisible(False)
self.tableWidget.verticalHeader().setVisible(False)
self.verticalLayout_3.addWidget(self.tableWidget)
self.verticalLayout_4.addWidget(self.groupBox_2)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_4.addItem(spacerItem)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.artwork_label.setText(_translate("Form", "TextLabel"))
self.label.setText(_translate("Form", "National No."))
self.nationaldex_label.setText(_translate("Form", "TextLabel"))
self.label_1.setText(_translate("Form", "Species"))
self.species_label.setText(_translate("Form", "TextLabel"))
self.label_4.setText(_translate("Form", "Type"))
self.type1_label.setText(_translate("Form", "TextLabel"))
self.type2_label.setText(_translate("Form", "TextLabel"))
self.label_5.setText(_translate("Form", "Abilities"))
self.ability1_label.setText(_translate("Form", "TextLabel"))
self.ability2_label.setText(_translate("Form", "TextLabel"))
self.ability3_label.setText(_translate("Form", "TextLabel"))
self.label_2.setText(_translate("Form", "Height"))
self.height_label.setText(_translate("Form", "0.0 m"))
self.label_3.setText(_translate("Form", "Weight"))
self.weight_label.setText(_translate("Form", "0.0 kg"))
self.gender_label.setText(_translate("Form", "TextLabel"))
self.groupBox.setTitle(_translate("Form", "Stats"))
self.spec_attack_bar.setFormat(_translate("Form", "%v"))
self.label_8.setText(_translate("Form", "Special Attack"))
self.spec_defense_bar.setFormat(_translate("Form", "%v"))
self.label_6.setText(_translate("Form", "HP"))
self.hp_bar.setFormat(_translate("Form", "%v"))
self.speed_bar.setFormat(_translate("Form", "%v"))
self.label_11.setText(_translate("Form", "Speed"))
self.label_10.setText(_translate("Form", "Special Defense"))
self.defense_bar.setFormat(_translate("Form", "%v"))
self.attack_bar.setFormat(_translate("Form", "%v"))
self.label_7.setText(_translate("Form", "Attack"))
self.label_9.setText(_translate("Form", "Defense"))
self.groupBox_2.setTitle(_translate("Form", "Evolution chain"))

View File

@ -1,118 +0,0 @@
# -*- 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"))

View File

@ -0,0 +1,131 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file './nmreval/src/resources/_ui/pokewindow.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(1687, 991)
self.gridLayout = QtWidgets.QGridLayout(Dialog)
self.gridLayout.setObjectName("gridLayout")
self.pushButton = QtWidgets.QPushButton(Dialog)
self.pushButton.setObjectName("pushButton")
self.gridLayout.addWidget(self.pushButton, 0, 2, 1, 1)
self.comboBox_2 = QtWidgets.QComboBox(Dialog)
self.comboBox_2.setObjectName("comboBox_2")
self.gridLayout.addWidget(self.comboBox_2, 0, 0, 1, 1)
self.comboBox = QtWidgets.QComboBox(Dialog)
self.comboBox.setObjectName("comboBox")
self.gridLayout.addWidget(self.comboBox, 0, 1, 1, 1)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.gridLayout.addItem(spacerItem, 0, 4, 1, 1)
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
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.setStandardButtons(QtWidgets.QDialogButtonBox.Close)
self.buttonBox.setObjectName("buttonBox")
self.gridLayout.addWidget(self.buttonBox, 0, 3, 1, 1)
self.splitter = QtWidgets.QSplitter(Dialog)
self.splitter.setOrientation(QtCore.Qt.Horizontal)
self.splitter.setObjectName("splitter")
self.tableWidget_2 = QtWidgets.QTableWidget(self.splitter)
self.tableWidget_2.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.tableWidget_2.setAlternatingRowColors(True)
self.tableWidget_2.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
self.tableWidget_2.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.tableWidget_2.setShowGrid(False)
self.tableWidget_2.setGridStyle(QtCore.Qt.NoPen)
self.tableWidget_2.setObjectName("tableWidget_2")
self.tableWidget_2.setColumnCount(13)
self.tableWidget_2.setRowCount(0)
item = QtWidgets.QTableWidgetItem()
self.tableWidget_2.setHorizontalHeaderItem(0, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget_2.setHorizontalHeaderItem(1, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget_2.setHorizontalHeaderItem(2, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget_2.setHorizontalHeaderItem(3, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget_2.setHorizontalHeaderItem(4, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget_2.setHorizontalHeaderItem(5, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget_2.setHorizontalHeaderItem(6, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget_2.setHorizontalHeaderItem(7, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget_2.setHorizontalHeaderItem(8, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget_2.setHorizontalHeaderItem(9, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget_2.setHorizontalHeaderItem(10, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget_2.setHorizontalHeaderItem(11, item)
item = QtWidgets.QTableWidgetItem()
self.tableWidget_2.setHorizontalHeaderItem(12, item)
self.tableWidget_2.horizontalHeader().setDefaultSectionSize(80)
self.tableWidget_2.horizontalHeader().setStretchLastSection(False)
self.tableWidget_2.verticalHeader().setVisible(False)
self.tabWidget = QtWidgets.QTabWidget(self.splitter)
self.tabWidget.setMinimumSize(QtCore.QSize(418, 0))
self.tabWidget.setObjectName("tabWidget")
self.gridLayout.addWidget(self.splitter, 1, 0, 1, 5)
self.retranslateUi(Dialog)
self.tabWidget.setCurrentIndex(-1)
self.buttonBox.rejected.connect(Dialog.close) # type: ignore
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Gotta catch \'em all!"))
self.pushButton.setText(_translate("Dialog", "Random"))
self.tableWidget_2.setSortingEnabled(True)
item = self.tableWidget_2.horizontalHeaderItem(0)
item.setText(_translate("Dialog", "#"))
item = self.tableWidget_2.horizontalHeaderItem(1)
item.setText(_translate("Dialog", "Pokemon"))
item = self.tableWidget_2.horizontalHeaderItem(2)
item.setText(_translate("Dialog", "Type"))
item = self.tableWidget_2.horizontalHeaderItem(3)
item.setText(_translate("Dialog", "Total"))
item = self.tableWidget_2.horizontalHeaderItem(4)
item.setText(_translate("Dialog", "HP"))
item.setToolTip(_translate("Dialog", "Hit Points; Kraftpunkte"))
item = self.tableWidget_2.horizontalHeaderItem(5)
item.setText(_translate("Dialog", "Attack"))
item.setToolTip(_translate("Dialog", "Attacke"))
item = self.tableWidget_2.horizontalHeaderItem(6)
item.setText(_translate("Dialog", "Defense"))
item.setToolTip(_translate("Dialog", "Verteidigung"))
item = self.tableWidget_2.horizontalHeaderItem(7)
item.setText(_translate("Dialog", "Sp. Attack"))
item.setToolTip(_translate("Dialog", "Special Attack; Spezial-Attacke"))
item = self.tableWidget_2.horizontalHeaderItem(8)
item.setText(_translate("Dialog", "Sp. Defense"))
item.setToolTip(_translate("Dialog", "Special Defense; Spezial-Verteidigung"))
item = self.tableWidget_2.horizontalHeaderItem(9)
item.setText(_translate("Dialog", "Speed"))
item.setToolTip(_translate("Dialog", "Initiative"))
item = self.tableWidget_2.horizontalHeaderItem(10)
item.setText(_translate("Dialog", "Height"))
item.setToolTip(_translate("Dialog", "Größe"))
item = self.tableWidget_2.horizontalHeaderItem(11)
item.setText(_translate("Dialog", "Weight"))
item.setToolTip(_translate("Dialog", "Gewicht"))
item = self.tableWidget_2.horizontalHeaderItem(12)
item.setText(_translate("Dialog", "BMI"))
item.setToolTip(_translate("Dialog", "Body-Mass-Index"))

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/ptstab.ui' # Form implementation generated from reading ui file 'nmreval/src/resources/_ui/ptstab.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.10
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
@ -13,29 +14,50 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object): class Ui_Form(object):
def setupUi(self, Form): def setupUi(self, Form):
Form.setObjectName("Form") Form.setObjectName("Form")
Form.resize(316, 747) Form.resize(417, 746)
self.verticalLayout = QtWidgets.QVBoxLayout(Form) self.gridLayout = QtWidgets.QGridLayout(Form)
self.verticalLayout.setContentsMargins(3, 3, 3, 3) self.gridLayout.setObjectName("gridLayout")
self.verticalLayout.setObjectName("verticalLayout") self.label_2 = QtWidgets.QLabel(Form)
self.label_2.setObjectName("label_2")
self.gridLayout.addWidget(self.label_2, 0, 0, 1, 2)
self.peaktable = QtWidgets.QListWidget(Form) self.peaktable = QtWidgets.QListWidget(Form)
self.peaktable.setEditTriggers(QtWidgets.QAbstractItemView.DoubleClicked|QtWidgets.QAbstractItemView.EditKeyPressed) self.peaktable.setEditTriggers(QtWidgets.QAbstractItemView.DoubleClicked|QtWidgets.QAbstractItemView.EditKeyPressed)
self.peaktable.setObjectName("peaktable") self.peaktable.setObjectName("peaktable")
self.verticalLayout.addWidget(self.peaktable) self.gridLayout.addWidget(self.peaktable, 1, 0, 1, 2)
self.groupBox = QtWidgets.QGroupBox(Form) self.special_checkbox = QtWidgets.QCheckBox(Form)
self.groupBox.setObjectName("groupBox") self.special_checkbox.setObjectName("special_checkbox")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.groupBox) self.gridLayout.addWidget(self.special_checkbox, 3, 0, 1, 1)
self.horizontalLayout.setContentsMargins(3, 3, 3, 3) self.special_comboBox = QtWidgets.QComboBox(Form)
self.horizontalLayout.setSpacing(3) self.special_comboBox.setEnabled(False)
self.horizontalLayout.setObjectName("horizontalLayout") self.special_comboBox.setObjectName("special_comboBox")
self.left_pt = QtWidgets.QSpinBox(self.groupBox) self.special_comboBox.addItem("")
self.left_pt.setMaximum(999) self.special_comboBox.addItem("")
self.left_pt.setObjectName("left_pt") self.special_comboBox.addItem("")
self.horizontalLayout.addWidget(self.left_pt) self.special_comboBox.addItem("")
self.right_pt = QtWidgets.QSpinBox(self.groupBox) self.gridLayout.addWidget(self.special_comboBox, 3, 1, 1, 1)
self.right_pt.setMaximum(999) spacerItem = QtWidgets.QSpacerItem(20, 30, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
self.right_pt.setObjectName("right_pt") self.gridLayout.addItem(spacerItem, 4, 0, 1, 1)
self.horizontalLayout.addWidget(self.right_pt) self.label_3 = QtWidgets.QLabel(Form)
self.average_combobox = QtWidgets.QComboBox(self.groupBox) self.label_3.setObjectName("label_3")
self.gridLayout.addWidget(self.label_3, 5, 0, 1, 1)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.left_limit = QtWidgets.QLineEdit(Form)
self.left_limit.setObjectName("left_limit")
self.horizontalLayout_2.addWidget(self.left_limit)
self.right_limit = QtWidgets.QLineEdit(Form)
self.right_limit.setObjectName("right_limit")
self.horizontalLayout_2.addWidget(self.right_limit)
self.limit_combobox = QtWidgets.QComboBox(Form)
self.limit_combobox.setObjectName("limit_combobox")
self.limit_combobox.addItem("")
self.limit_combobox.addItem("")
self.horizontalLayout_2.addWidget(self.limit_combobox)
self.gridLayout.addLayout(self.horizontalLayout_2, 5, 1, 1, 1)
self.label_5 = QtWidgets.QLabel(Form)
self.label_5.setObjectName("label_5")
self.gridLayout.addWidget(self.label_5, 6, 0, 1, 1)
self.average_combobox = QtWidgets.QComboBox(Form)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -45,89 +67,94 @@ class Ui_Form(object):
self.average_combobox.addItem("") self.average_combobox.addItem("")
self.average_combobox.addItem("") self.average_combobox.addItem("")
self.average_combobox.addItem("") self.average_combobox.addItem("")
self.horizontalLayout.addWidget(self.average_combobox) self.average_combobox.addItem("")
self.verticalLayout.addWidget(self.groupBox) self.gridLayout.addWidget(self.average_combobox, 6, 1, 1, 1)
self.groupBox_2 = QtWidgets.QGroupBox(Form) self.label_4 = QtWidgets.QLabel(Form)
self.groupBox_2.setCheckable(True) self.label_4.setObjectName("label_4")
self.groupBox_2.setChecked(False) self.gridLayout.addWidget(self.label_4, 7, 0, 1, 1)
self.groupBox_2.setObjectName("groupBox_2") self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.groupBox_2) self.horizontalLayout.setObjectName("horizontalLayout")
self.horizontalLayout_5.setContentsMargins(3, 3, 3, 3) self.xbutton = QtWidgets.QCheckBox(Form)
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.xbutton.setObjectName("xbutton")
self.gridLayout.addWidget(self.xbutton, 0, 0, 1, 1) self.horizontalLayout.addWidget(self.xbutton)
self.ybutton = QtWidgets.QCheckBox(self.groupBox_3) self.ybutton = QtWidgets.QCheckBox(Form)
self.ybutton.setChecked(True) self.ybutton.setChecked(True)
self.ybutton.setObjectName("ybutton") self.ybutton.setObjectName("ybutton")
self.gridLayout.addWidget(self.ybutton, 0, 1, 1, 1) self.horizontalLayout.addWidget(self.ybutton)
self.graph_checkbox = QtWidgets.QCheckBox(self.groupBox_3) self.gridLayout.addLayout(self.horizontalLayout, 7, 1, 1, 1)
self.label = QtWidgets.QLabel(Form)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 8, 0, 1, 1)
self.group_box = QtWidgets.QComboBox(Form)
self.group_box.setObjectName("group_box")
self.group_box.addItem("")
self.group_box.addItem("")
self.gridLayout.addWidget(self.group_box, 8, 1, 1, 1)
spacerItem1 = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
self.gridLayout.addItem(spacerItem1, 9, 0, 1, 1)
self.graph_checkbox = QtWidgets.QCheckBox(Form)
self.graph_checkbox.setChecked(True) self.graph_checkbox.setChecked(True)
self.graph_checkbox.setObjectName("graph_checkbox") self.graph_checkbox.setObjectName("graph_checkbox")
self.gridLayout.addWidget(self.graph_checkbox, 1, 0, 1, 1) self.gridLayout.addWidget(self.graph_checkbox, 10, 0, 1, 1)
self.graph_combobox = QtWidgets.QComboBox(self.groupBox_3) self.graph_combobox = QtWidgets.QComboBox(Form)
self.graph_combobox.setEnabled(False) self.graph_combobox.setEnabled(False)
self.graph_combobox.setObjectName("graph_combobox") self.graph_combobox.setObjectName("graph_combobox")
self.gridLayout.addWidget(self.graph_combobox, 1, 1, 1, 1) self.gridLayout.addWidget(self.graph_combobox, 10, 1, 1, 1)
self.verticalLayout.addWidget(self.groupBox_3) spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.gridLayout.addItem(spacerItem2, 12, 0, 1, 1)
self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1)
self.horizontalLayout_2.setSpacing(2)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.okButton = QtWidgets.QPushButton(Form) self.okButton = QtWidgets.QPushButton(Form)
icon = QtGui.QIcon.fromTheme("dialog-ok") icon = QtGui.QIcon.fromTheme("dialog-ok")
self.okButton.setIcon(icon) self.okButton.setIcon(icon)
self.okButton.setObjectName("okButton") self.okButton.setObjectName("okButton")
self.horizontalLayout_2.addWidget(self.okButton) self.gridLayout.addWidget(self.okButton, 11, 0, 1, 2)
self.deleteButton = QtWidgets.QPushButton(Form) self.deleteButton = QtWidgets.QPushButton(Form)
icon = QtGui.QIcon.fromTheme("dialog-cancel") icon = QtGui.QIcon.fromTheme("dialog-cancel")
self.deleteButton.setIcon(icon) self.deleteButton.setIcon(icon)
self.deleteButton.setObjectName("deleteButton") self.deleteButton.setObjectName("deleteButton")
self.horizontalLayout_2.addWidget(self.deleteButton) self.gridLayout.addWidget(self.deleteButton, 2, 0, 1, 2)
self.verticalLayout.addLayout(self.horizontalLayout_2) self.label_2.setBuddy(self.peaktable)
self.label_5.setBuddy(self.average_combobox)
self.label_4.setBuddy(self.xbutton)
self.label.setBuddy(self.group_box)
self.retranslateUi(Form) self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form) QtCore.QMetaObject.connectSlotsByName(Form)
Form.setTabOrder(self.peaktable, self.limit_combobox)
Form.setTabOrder(self.limit_combobox, self.average_combobox)
Form.setTabOrder(self.average_combobox, self.xbutton)
Form.setTabOrder(self.xbutton, self.ybutton)
Form.setTabOrder(self.ybutton, self.group_box)
Form.setTabOrder(self.group_box, self.graph_checkbox)
Form.setTabOrder(self.graph_checkbox, self.graph_combobox)
def retranslateUi(self, Form): def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form")) Form.setWindowTitle(_translate("Form", "Form"))
self.label_2.setText(_translate("Form", "Selected points and regions"))
self.peaktable.setToolTip(_translate("Form", "Edit by entering new value: \n" self.peaktable.setToolTip(_translate("Form", "Edit by entering new value: \n"
"Single number for points (e.g. 1e-6); \n" "Single number for points (e.g. 1e-6); \n"
"two numbers separated by space for regions (e.g. 1e-6 5e-6). \n" "two numbers separated by space for regions (e.g. 1e-6 5e-6). \n"
"Changing between regions and points is NOT possible")) "Changing between regions and points is NOT possible"))
self.groupBox.setTitle(_translate("Form", "Average")) self.special_checkbox.setText(_translate("Form", "Use special value"))
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.setToolTip(_translate("Form", "Automatic selection of respective points"))
self.special_comboBox.setItemText(0, _translate("Form", "max(y)")) self.special_comboBox.setItemText(0, _translate("Form", "max(y)"))
self.special_comboBox.setItemText(1, _translate("Form", "max(abs(y))")) self.special_comboBox.setItemText(1, _translate("Form", "max(abs(y))"))
self.special_comboBox.setItemText(2, _translate("Form", "min(y)")) self.special_comboBox.setItemText(2, _translate("Form", "min(y)"))
self.special_comboBox.setItemText(3, _translate("Form", "min(abs(y))")) self.special_comboBox.setItemText(3, _translate("Form", "min(abs(y))"))
self.groupBox_3.setTitle(_translate("Form", "Result")) self.label_3.setText(_translate("Form", "Region around points"))
self.limit_combobox.setItemText(0, _translate("Form", "points"))
self.limit_combobox.setItemText(1, _translate("Form", "range"))
self.label_5.setText(_translate("Form", "Aggregation"))
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.average_combobox.setItemText(3, _translate("Form", "Std. deviation"))
self.label_4.setText(_translate("Form", "New set based on"))
self.xbutton.setText(_translate("Form", "x")) self.xbutton.setText(_translate("Form", "x"))
self.ybutton.setText(_translate("Form", "y")) self.ybutton.setText(_translate("Form", "y"))
self.label.setText(_translate("Form", "Group by"))
self.group_box.setItemText(0, _translate("Form", "\"Group\" value"))
self.group_box.setItemText(1, _translate("Form", "x value"))
self.graph_checkbox.setText(_translate("Form", "New graph?")) self.graph_checkbox.setText(_translate("Form", "New graph?"))
self.okButton.setText(_translate("Form", "Apply")) self.okButton.setText(_translate("Form", "Apply"))
self.deleteButton.setText(_translate("Form", "Delete selected")) self.deleteButton.setText(_translate("Form", "Delete selection"))

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/smoothdialog.ui' # Form implementation generated from reading ui file 'src/resources/_ui/smoothdialog.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.10
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
@ -17,9 +18,37 @@ class Ui_SmoothDialog(object):
self.gridLayout = QtWidgets.QGridLayout(SmoothDialog) self.gridLayout = QtWidgets.QGridLayout(SmoothDialog)
self.gridLayout.setSpacing(3) self.gridLayout.setSpacing(3)
self.gridLayout.setObjectName("gridLayout") self.gridLayout.setObjectName("gridLayout")
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout.addItem(spacerItem, 7, 0, 1, 1)
self.frac_label = QtWidgets.QLabel(SmoothDialog) self.frac_label = QtWidgets.QLabel(SmoothDialog)
self.frac_label.setObjectName("frac_label") self.frac_label.setObjectName("frac_label")
self.gridLayout.addWidget(self.frac_label, 1, 0, 1, 1) self.gridLayout.addWidget(self.frac_label, 2, 0, 1, 1)
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, 5, 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, 3, 0, 1, 2)
self.y_checkBox = QtWidgets.QCheckBox(SmoothDialog)
self.y_checkBox.setObjectName("y_checkBox")
self.gridLayout.addWidget(self.y_checkBox, 6, 1, 1, 1)
self.x_checkBox = QtWidgets.QCheckBox(SmoothDialog)
self.x_checkBox.setObjectName("x_checkBox")
self.gridLayout.addWidget(self.x_checkBox, 6, 0, 1, 1)
self.widget_2 = QtWidgets.QWidget(SmoothDialog) self.widget_2 = QtWidgets.QWidget(SmoothDialog)
self.widget_2.setObjectName("widget_2") self.widget_2.setObjectName("widget_2")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.widget_2) self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.widget_2)
@ -35,37 +64,17 @@ class Ui_SmoothDialog(object):
self.iter_spinBox.setProperty("value", 1) self.iter_spinBox.setProperty("value", 1)
self.iter_spinBox.setObjectName("iter_spinBox") self.iter_spinBox.setObjectName("iter_spinBox")
self.horizontalLayout_3.addWidget(self.iter_spinBox) self.horizontalLayout_3.addWidget(self.iter_spinBox)
self.gridLayout.addWidget(self.widget_2, 3, 0, 1, 2) self.gridLayout.addWidget(self.widget_2, 4, 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 = QtWidgets.QSpinBox(SmoothDialog)
self.frac_spinBox.setMinimum(1) self.frac_spinBox.setMinimum(1)
self.frac_spinBox.setMaximum(999) self.frac_spinBox.setMaximum(999)
self.frac_spinBox.setObjectName("frac_spinBox") self.frac_spinBox.setObjectName("frac_spinBox")
self.gridLayout.addWidget(self.frac_spinBox, 1, 1, 1, 1) self.gridLayout.addWidget(self.frac_spinBox, 2, 1, 1, 1)
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, 8, 0, 1, 2)
self.comboBox = QtWidgets.QComboBox(SmoothDialog) self.comboBox = QtWidgets.QComboBox(SmoothDialog)
self.comboBox.setObjectName("comboBox") self.comboBox.setObjectName("comboBox")
self.comboBox.addItem("") self.comboBox.addItem("")
@ -77,22 +86,17 @@ class Ui_SmoothDialog(object):
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) self.gridLayout.addWidget(self.comboBox, 1, 0, 1, 2)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.label_2 = QtWidgets.QLabel(SmoothDialog)
self.gridLayout.addItem(spacerItem, 6, 0, 1, 1) self.label_2.setObjectName("label_2")
self.y_checkBox = QtWidgets.QCheckBox(SmoothDialog) self.gridLayout.addWidget(self.label_2, 0, 0, 1, 2)
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.frac_label.setBuddy(self.frac_spinBox)
self.label_3.setBuddy(self.iter_spinBox)
self.label.setBuddy(self.polynom_spinBox) self.label.setBuddy(self.polynom_spinBox)
self.label_3.setBuddy(self.iter_spinBox)
self.retranslateUi(SmoothDialog) self.retranslateUi(SmoothDialog)
self.buttonBox.accepted.connect(SmoothDialog.accept) self.buttonBox.accepted.connect(SmoothDialog.accept) # type: ignore
self.buttonBox.rejected.connect(SmoothDialog.reject) self.buttonBox.rejected.connect(SmoothDialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(SmoothDialog) QtCore.QMetaObject.connectSlotsByName(SmoothDialog)
SmoothDialog.setTabOrder(self.comboBox, self.frac_spinBox) SmoothDialog.setTabOrder(self.comboBox, self.frac_spinBox)
SmoothDialog.setTabOrder(self.frac_spinBox, self.polynom_spinBox) SmoothDialog.setTabOrder(self.frac_spinBox, self.polynom_spinBox)
@ -104,9 +108,11 @@ class Ui_SmoothDialog(object):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
SmoothDialog.setWindowTitle(_translate("SmoothDialog", "1D smoothing filter")) SmoothDialog.setWindowTitle(_translate("SmoothDialog", "1D smoothing filter"))
self.frac_label.setText(_translate("SmoothDialog", "Window length")) self.frac_label.setText(_translate("SmoothDialog", "Window length"))
self.label_3.setText(_translate("SmoothDialog", "Iterations"))
self.label.setText(_translate("SmoothDialog", "Polynomial degree")) self.label.setText(_translate("SmoothDialog", "Polynomial degree"))
self.polynom_spinBox.setToolTip(_translate("SmoothDialog", "Deg")) self.polynom_spinBox.setToolTip(_translate("SmoothDialog", "Deg"))
self.y_checkBox.setText(_translate("SmoothDialog", "y log-spaced?"))
self.x_checkBox.setText(_translate("SmoothDialog", "x log-spaced?"))
self.label_3.setText(_translate("SmoothDialog", "Iterations"))
self.frac_spinBox.setToolTip(_translate("SmoothDialog", "<html><head/><body><p>Number of data points used as smoothing window.</p></body></html>")) self.frac_spinBox.setToolTip(_translate("SmoothDialog", "<html><head/><body><p>Number of data points used as smoothing window.</p></body></html>"))
self.comboBox.setItemText(0, _translate("SmoothDialog", "Moving mean")) self.comboBox.setItemText(0, _translate("SmoothDialog", "Moving mean"))
self.comboBox.setItemText(1, _translate("SmoothDialog", "Savitzky-Golay")) self.comboBox.setItemText(1, _translate("SmoothDialog", "Savitzky-Golay"))
@ -117,5 +123,4 @@ class Ui_SmoothDialog(object):
self.comboBox.setItemText(6, _translate("SmoothDialog", "Moving maximum")) self.comboBox.setItemText(6, _translate("SmoothDialog", "Moving maximum"))
self.comboBox.setItemText(7, _translate("SmoothDialog", "Moving minimum")) self.comboBox.setItemText(7, _translate("SmoothDialog", "Moving minimum"))
self.comboBox.setItemText(8, _translate("SmoothDialog", "Moving sum")) self.comboBox.setItemText(8, _translate("SmoothDialog", "Moving sum"))
self.y_checkBox.setText(_translate("SmoothDialog", "y log-spaced?")) self.label_2.setText(_translate("SmoothDialog", "<html><head/><body><p><span style=\" font-weight:600;\">Note:</span> Sets must be sorted for correct results</p></body></html>"))
self.x_checkBox.setText(_translate("SmoothDialog", "x log-spaced?"))

View File

@ -2,9 +2,10 @@
# Form implementation generated from reading ui file 'resources/_ui/usermodeleditor.ui' # Form implementation generated from reading ui file 'resources/_ui/usermodeleditor.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.10
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
@ -20,15 +21,12 @@ class Ui_MainWindow(object):
self.verticalLayout.setContentsMargins(3, 3, 3, 3) self.verticalLayout.setContentsMargins(3, 3, 3, 3)
self.verticalLayout.setSpacing(3) self.verticalLayout.setSpacing(3)
self.verticalLayout.setObjectName("verticalLayout") self.verticalLayout.setObjectName("verticalLayout")
self.edit_field = CodeEditor(self.centralwidget) self.widget = EditorWidget(self.centralwidget)
font = QtGui.QFont() self.widget.setObjectName("widget")
font.setPointSize(10) self.verticalLayout.addWidget(self.widget)
self.edit_field.setFont(font)
self.edit_field.setObjectName("edit_field")
self.verticalLayout.addWidget(self.edit_field)
MainWindow.setCentralWidget(self.centralwidget) MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 30)) self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 20))
self.menubar.setObjectName("menubar") self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(self.menubar) self.menuFile = QtWidgets.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile") self.menuFile.setObjectName("menuFile")
@ -61,4 +59,4 @@ class Ui_MainWindow(object):
self.actionSave.setText(_translate("MainWindow", "Save")) self.actionSave.setText(_translate("MainWindow", "Save"))
self.actionSave_as.setText(_translate("MainWindow", "Save as...")) self.actionSave_as.setText(_translate("MainWindow", "Save as..."))
self.actionClose.setText(_translate("MainWindow", "Close")) self.actionClose.setText(_translate("MainWindow", "Close"))
from ..lib.codeeditor import CodeEditor from ..lib.codeeditor import EditorWidget

View File

@ -1,28 +0,0 @@
def main():
import sys
from nmreval.configs import check_for_config
# does a directory for config stuff exist? create it if not
check_for_config()
# pyqtgraph warns on Mac if QApplication is created when it is imported
# import pyqtgraph
from nmreval.lib.logger import handle_exception
sys.excepthook = handle_exception
from gui_qt import App
app = App(['Team Rocket FTW!'])
from gui_qt.main.mainwindow import NMRMainWindow
mplQt = NMRMainWindow()
mplQt.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()

View File

@ -300,10 +300,12 @@ class ExperimentContainer(QtCore.QObject):
self._relations.pop(relation_type) self._relations.pop(relation_type)
def _update_actions(self): def _update_actions(self):
self.actions.update({'sort': self._data.sort, self.actions.update({
'sort': self._data.sort,
'cut': self._data.cut, 'cut': self._data.cut,
'norm': self._data.normalize, 'norm': self._data.normalize,
'center': self.center}) 'center': self.center,
})
@plot_update @plot_update
def update(self, opts: dict): def update(self, opts: dict):
@ -311,9 +313,11 @@ class ExperimentContainer(QtCore.QObject):
def get_properties(self) -> dict: def get_properties(self) -> dict:
props = OrderedDict() props = OrderedDict()
props['General'] = OrderedDict([('Name', self.name), props['General'] = OrderedDict([
('Name', self.name),
('Value', str(self.value)), ('Value', str(self.value)),
('Group', str(self.group))]) ('Group', str(self.group)),
])
props['Symbol'] = OrderedDict() props['Symbol'] = OrderedDict()
props['Line'] = OrderedDict() props['Line'] = OrderedDict()
@ -480,10 +484,12 @@ class ExperimentContainer(QtCore.QObject):
else: else:
prefix = f'g[{i}].s[{j}].' prefix = f'g[{i}].s[{j}].'
namespace = {prefix + 'x': (self.x, 'x values'), namespace = {
prefix + 'x': (self.x, 'x values'),
prefix + 'y': [self.y, 'y values'], prefix + 'y': [self.y, 'y values'],
prefix + 'y_err': (self.y_err, 'y error values'), prefix + 'y_err': (self.y_err, 'y error values'),
prefix + 'value': (self.value, str(self.value))} prefix + 'value': (self.value, str(self.value)),
}
if len(self._fits) == 1: if len(self._fits) == 1:
namespace.update({ namespace.update({

View File

@ -17,7 +17,7 @@ class DataTree(QtWidgets.QTreeWidget):
moveItem = QtCore.pyqtSignal(list, str, str, int) # items, from, to, new row moveItem = QtCore.pyqtSignal(list, str, str, int) # items, from, to, new row
copyItem = QtCore.pyqtSignal(list, str) copyItem = QtCore.pyqtSignal(list, str)
saveFits = QtCore.pyqtSignal(list) saveFits = QtCore.pyqtSignal(list)
extendFits = QtCore.pyqtSignal(list) extendFits = QtCore.pyqtSignal(list, bool)
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
def __init__(self, parent=None): def __init__(self, parent=None):
@ -49,11 +49,16 @@ class DataTree(QtWidgets.QTreeWidget):
def add_graph(self, idd: str, name: str): def add_graph(self, idd: str, name: str):
item = QtWidgets.QTreeWidgetItem() item = QtWidgets.QTreeWidgetItem()
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsDropEnabled | QtCore.Qt.ItemIsEditable | item.setFlags(
QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable) QtCore.Qt.ItemFlag.ItemIsSelectable |
QtCore.Qt.ItemFlag.ItemIsDropEnabled |
QtCore.Qt.ItemFlag.ItemIsEditable |
QtCore.Qt.ItemFlag.ItemIsEnabled |
QtCore.Qt.ItemFlag.ItemIsUserCheckable
)
item.setText(0, name) item.setText(0, name)
item.setData(0, QtCore.Qt.UserRole, idd) item.setData(0, QtCore.Qt.ItemDataRole.UserRole, idd)
item.setCheckState(0, QtCore.Qt.Checked) item.setCheckState(0, QtCore.Qt.CheckState.Checked)
self.addTopLevelItem(item) self.addTopLevelItem(item)
self._checked_graphs.add(idd) self._checked_graphs.add(idd)
@ -67,14 +72,19 @@ class DataTree(QtWidgets.QTreeWidget):
for row in range(self.invisibleRootItem().childCount()): for row in range(self.invisibleRootItem().childCount()):
graph = self.invisibleRootItem().child(row) graph = self.invisibleRootItem().child(row)
if graph.data(0, QtCore.Qt.UserRole) == gid: if graph.data(0, QtCore.Qt.ItemDataRole.UserRole) == gid:
for (idd, name, value) in items: for (idd, name, value) in items:
item = QtWidgets.QTreeWidgetItem([name]) item = QtWidgets.QTreeWidgetItem([name])
item.setToolTip(0, f'Value: {value}') item.setToolTip(0, f'Value: {value}')
item.setData(0, QtCore.Qt.UserRole, idd) item.setData(0, QtCore.Qt.ItemDataRole.UserRole, idd)
item.setCheckState(0, QtCore.Qt.Checked) item.setCheckState(0, QtCore.Qt.CheckState.Checked)
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsEditable | item.setFlags(
QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable) QtCore.Qt.ItemFlag.ItemIsSelectable |
QtCore.Qt.ItemFlag.ItemIsDragEnabled |
QtCore.Qt.ItemFlag.ItemIsEditable |
QtCore.Qt.ItemFlag.ItemIsEnabled |
QtCore.Qt.ItemFlag.ItemIsUserCheckable
)
graph.addChild(item) graph.addChild(item)
self._checked_sets.add(idd) self._checked_sets.add(idd)
@ -85,8 +95,8 @@ class DataTree(QtWidgets.QTreeWidget):
@QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem) @QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem)
def data_change(self, item: QtWidgets.QTreeWidgetItem, emit: bool = True) -> tuple[set, set]: def data_change(self, item: QtWidgets.QTreeWidgetItem, emit: bool = True) -> tuple[set, set]:
idd = item.data(0, QtCore.Qt.UserRole) idd = item.data(0, QtCore.Qt.ItemDataRole.UserRole)
is_selected = item.checkState(0) == QtCore.Qt.Checked is_selected = item.checkState(0) == QtCore.Qt.CheckState.Checked
to_be_hidden = set() to_be_hidden = set()
to_be_shown = set() to_be_shown = set()
@ -104,9 +114,9 @@ class DataTree(QtWidgets.QTreeWidget):
self.blockSignals(True) self.blockSignals(True)
for i in range(item.childCount()): for i in range(item.childCount()):
child = item.child(i) child = item.child(i)
child.setCheckState(0, QtCore.Qt.Checked) child.setCheckState(0, QtCore.Qt.CheckState.Checked)
to_be_shown.add(child.data(0, QtCore.Qt.UserRole)) to_be_shown.add(child.data(0, QtCore.Qt.ItemDataRole.UserRole))
self._checked_sets.add(child.data(0, QtCore.Qt.UserRole)) self._checked_sets.add(child.data(0, QtCore.Qt.ItemDataRole.UserRole))
self.blockSignals(False) self.blockSignals(False)
# check state change to unchecked # check state change to unchecked
@ -115,10 +125,10 @@ class DataTree(QtWidgets.QTreeWidget):
self.blockSignals(True) self.blockSignals(True)
for i in range(item.childCount()): for i in range(item.childCount()):
child = item.child(i) child = item.child(i)
child.setCheckState(0, QtCore.Qt.Unchecked) child.setCheckState(0, QtCore.Qt.CheckState.Unchecked)
to_be_hidden.add(child.data(0, QtCore.Qt.UserRole)) to_be_hidden.add(child.data(0, QtCore.Qt.ItemDataRole.UserRole))
try: try:
self._checked_sets.remove(child.data(0, QtCore.Qt.UserRole)) self._checked_sets.remove(child.data(0, QtCore.Qt.ItemDataRole.UserRole))
except KeyError: except KeyError:
pass pass
self.blockSignals(False) self.blockSignals(False)
@ -153,7 +163,7 @@ class DataTree(QtWidgets.QTreeWidget):
@QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem) @QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem)
def new_selection(self, item: QtWidgets.QTreeWidgetItem): def new_selection(self, item: QtWidgets.QTreeWidgetItem):
if item.parent() is None: if item.parent() is None:
self.management.select_window(item.data(0, QtCore.Qt.UserRole)) self.management.select_window(item.data(0, QtCore.Qt.ItemDataRole.UserRole))
def dropEvent(self, evt: QtGui.QDropEvent): def dropEvent(self, evt: QtGui.QDropEvent):
dropped_index = self.indexAt(evt.pos()) dropped_index = self.indexAt(evt.pos())
@ -179,7 +189,7 @@ class DataTree(QtWidgets.QTreeWidget):
from_parent.removeChild(it) from_parent.removeChild(it)
tobemoved.append(it) tobemoved.append(it)
take_from.append(from_parent.data(0, QtCore.Qt.UserRole)) take_from.append(from_parent.data(0, QtCore.Qt.ItemDataRole.UserRole))
pos = QtCore.QModelIndex(persistent_drop) pos = QtCore.QModelIndex(persistent_drop)
if self.dropIndicatorPosition() == QtWidgets.QAbstractItemView.BelowItem: if self.dropIndicatorPosition() == QtWidgets.QAbstractItemView.BelowItem:
@ -191,8 +201,8 @@ class DataTree(QtWidgets.QTreeWidget):
else: else:
to_parent.insertChildren(row, tobemoved) to_parent.insertChildren(row, tobemoved)
self.management.move_sets([it.data(0, QtCore.Qt.UserRole) for it in tobemoved], self.management.move_sets([it.data(0, QtCore.Qt.ItemDataRole.UserRole) for it in tobemoved],
to_parent.data(0, QtCore.Qt.UserRole), take_from, to_parent.data(0, QtCore.Qt.ItemDataRole.UserRole), take_from,
pos=-1 if append else row) pos=-1 if append else row)
self.update_indexes() self.update_indexes()
@ -207,7 +217,7 @@ class DataTree(QtWidgets.QTreeWidget):
while iterator.value(): while iterator.value():
item = iterator.value() item = iterator.value()
if item is not None: if item is not None:
data = item.data(0, QtCore.Qt.UserRole) data = item.data(0, QtCore.Qt.ItemDataRole.UserRole)
if data == gid_out: if data == gid_out:
from_parent = item from_parent = item
@ -231,7 +241,7 @@ class DataTree(QtWidgets.QTreeWidget):
self.blockSignals(False) self.blockSignals(False)
def sort(self, graph_item: QtWidgets.QTreeWidgetItem, mode: str = 'value'): def sort(self, graph_item: QtWidgets.QTreeWidgetItem, mode: str = 'value'):
graph_id = graph_item.data(0, QtCore.Qt.UserRole) graph_id = graph_item.data(0, QtCore.Qt.ItemDataRole.UserRole)
sets = self.management.get_attributes(graph_id, mode) sets = self.management.get_attributes(graph_id, mode)
sets = [el[0] for el in sorted(sets.items(), key=lambda x: x[1])] sets = [el[0] for el in sorted(sets.items(), key=lambda x: x[1])]
@ -243,7 +253,7 @@ class DataTree(QtWidgets.QTreeWidget):
for s in sets: for s in sets:
for c in children: for c in children:
if c.data(0, QtCore.Qt.UserRole) == s: if c.data(0, QtCore.Qt.ItemDataRole.UserRole) == s:
graph_item.addChild(c) graph_item.addChild(c)
self.update_indexes() self.update_indexes()
@ -276,7 +286,7 @@ class DataTree(QtWidgets.QTreeWidget):
while iterator.value(): while iterator.value():
item = iterator.value() item = iterator.value()
if item is not None: if item is not None:
data = item.data(0, QtCore.Qt.UserRole) data = item.data(0, QtCore.Qt.ItemDataRole.UserRole)
if data == sid: if data == sid:
if name != item.text(0): if name != item.text(0):
item.setText(0, name) item.setText(0, name)
@ -285,7 +295,7 @@ class DataTree(QtWidgets.QTreeWidget):
iterator += 1 iterator += 1
def keyPressEvent(self, evt: QtGui.QKeyEvent): def keyPressEvent(self, evt: QtGui.QKeyEvent):
if evt.key() == QtCore.Qt.Key_Delete: if evt.key() == QtCore.Qt.Key.Key_Delete:
rm_sets = [] rm_sets = []
rm_graphs = [] rm_graphs = []
for idx in self.selectedIndexes(): for idx in self.selectedIndexes():
@ -296,20 +306,20 @@ class DataTree(QtWidgets.QTreeWidget):
if item.parent() is None: if item.parent() is None:
for c_i in range(item.childCount()): for c_i in range(item.childCount()):
# add sets inside graph to removal # add sets inside graph to removal
child_data = item.child(c_i).data(0, QtCore.Qt.UserRole) child_data = item.child(c_i).data(0, QtCore.Qt.ItemDataRole.UserRole)
if child_data not in rm_sets: if child_data not in rm_sets:
rm_sets.append(child_data) rm_sets.append(child_data)
rm_graphs.append(item.data(0, QtCore.Qt.UserRole)) rm_graphs.append(item.data(0, QtCore.Qt.ItemDataRole.UserRole))
else: else:
item_data = item.data(0, QtCore.Qt.UserRole) item_data = item.data(0, QtCore.Qt.ItemDataRole.UserRole)
if item_data not in rm_sets: if item_data not in rm_sets:
rm_sets.append(item_data) rm_sets.append(item_data)
# self.deleteItem.emit(rm_sets+rm_graphs) # self.deleteItem.emit(rm_sets+rm_graphs)
self.management.delete_sets(rm_sets+rm_graphs) self.management.delete_sets(rm_sets+rm_graphs)
elif evt.key() == QtCore.Qt.Key_Space: elif evt.key() == QtCore.Qt.Key.Key_Space:
sets = [] sets = []
from_parent = [] from_parent = []
@ -329,7 +339,7 @@ class DataTree(QtWidgets.QTreeWidget):
for it in sets: for it in sets:
if it in from_parent: if it in from_parent:
continue continue
it.setCheckState(0, QtCore.Qt.Unchecked if it.checkState(0) == QtCore.Qt.Checked else QtCore.Qt.Checked) it.setCheckState(0, QtCore.Qt.CheckState.Unchecked if it.checkState(0) == QtCore.Qt.CheckState.Checked else QtCore.Qt.CheckState.Checked)
s1, s2 = self.data_change(it, emit=False) s1, s2 = self.data_change(it, emit=False)
to_be_hidden |= s2 to_be_hidden |= s2
to_be_shown |= s1 to_be_shown |= s1
@ -353,7 +363,7 @@ class DataTree(QtWidgets.QTreeWidget):
# find all items that have to be removed # find all items that have to be removed
while iterator.value(): while iterator.value():
item = iterator.value() item = iterator.value()
_id = item.data(0, QtCore.Qt.UserRole) _id = item.data(0, QtCore.Qt.ItemDataRole.UserRole)
if _id in ids: if _id in ids:
try: try:
item_parent = item.parent() item_parent = item.parent()
@ -410,6 +420,7 @@ class DataTree(QtWidgets.QTreeWidget):
self.ctx_sets(evt, menu) self.ctx_sets(evt, menu)
def ctx_graphs(self, evt, menu): def ctx_graphs(self, evt, menu):
copy_action = menu.addAction('Replicate graph!')
del_action = menu.addAction('Exterminate graph!') del_action = menu.addAction('Exterminate graph!')
sort_menu = menu.addMenu('Sort sets') sort_menu = menu.addMenu('Sort sets')
@ -430,12 +441,16 @@ class DataTree(QtWidgets.QTreeWidget):
if i.column() == 0: if i.column() == 0:
continue continue
items.append(self.itemFromIndex(i)) items.append(self.itemFromIndex(i))
graphs.append(self.itemFromIndex(i).data(0, QtCore.Qt.UserRole)) graphs.append(self.itemFromIndex(i).data(0, QtCore.Qt.ItemDataRole.UserRole))
if action == del_action: if action == del_action:
for gid in graphs: for gid in graphs:
self.management.delete_graph(gid) self.management.delete_graph(gid)
elif action == copy_action:
for gid in graphs:
self.management.copy_graph(gid)
elif action.parent() == col_menu: elif action.parent() == col_menu:
for gid in graphs: for gid in graphs:
self.management.set_cycle(self.management.graphs[gid].sets, action.text()) self.management.set_cycle(self.management.graphs[gid].sets, action.text())
@ -450,7 +465,7 @@ class DataTree(QtWidgets.QTreeWidget):
del_action = menu.addAction('Exterminate sets') del_action = menu.addAction('Exterminate sets')
cp_action = menu.addAction('Replicate sets') cp_action = menu.addAction('Replicate sets')
cat_action = menu.addAction('Join us!') cat_action = menu.addAction('Join us!')
plt_action = save_action = extend_action = None plt_action = save_action = extend_action = subfit_action = None
menu.addSeparator() menu.addSeparator()
col_menu = menu.addMenu('Color cycle') col_menu = menu.addMenu('Color cycle')
for c in available_cycles.keys(): for c in available_cycles.keys():
@ -468,12 +483,12 @@ class DataTree(QtWidgets.QTreeWidget):
continue continue
else: else:
graph_id = parent.data(0, QtCore.Qt.UserRole) graph_id = parent.data(0, QtCore.Qt.ItemDataRole.UserRole)
if graph_id not in idx: if graph_id not in idx:
idx[graph_id] = [] idx[graph_id] = []
# collect sets in their graph # collect sets in their graph
idx[graph_id].append(item.data(0, QtCore.Qt.UserRole)) idx[graph_id].append(item.data(0, QtCore.Qt.ItemDataRole.UserRole))
data = self.management[item.data(0, QtCore.Qt.UserRole)] data = self.management[item.data(0, QtCore.Qt.ItemDataRole.UserRole)]
if data.mode == 'fit': if data.mode == 'fit':
has_fits = True has_fits = True
@ -482,6 +497,7 @@ class DataTree(QtWidgets.QTreeWidget):
plt_action = menu.addAction('Plot fit parameter') plt_action = menu.addAction('Plot fit parameter')
save_action = menu.addAction('Save fit parameter') save_action = menu.addAction('Save fit parameter')
extend_action = menu.addAction('Extrapolate fit') extend_action = menu.addAction('Extrapolate fit')
subfit_action = menu.addAction('Plot partial functions')
action = menu.exec(evt.globalPos()) action = menu.exec(evt.globalPos())
@ -489,6 +505,9 @@ class DataTree(QtWidgets.QTreeWidget):
for gid, sets in idx.items(): for gid, sets in idx.items():
s.extend(sets) s.extend(sets)
if action is None:
return
if action == del_action: if action == del_action:
self.management.delete_sets(s) self.management.delete_sets(s)
@ -506,7 +525,10 @@ class DataTree(QtWidgets.QTreeWidget):
self.saveFits.emit(s) self.saveFits.emit(s)
elif action == extend_action: elif action == extend_action:
self.extendFits.emit(s) self.extendFits.emit(s, False)
elif action == subfit_action:
self.extendFits.emit(s, True)
elif action.parent() == col_menu: elif action.parent() == col_menu:
self.management.set_cycle(s, action.text()) self.management.set_cycle(s, action.text())
@ -518,7 +540,7 @@ class DataTree(QtWidgets.QTreeWidget):
while iterator.value(): while iterator.value():
item = iterator.value() item = iterator.value()
if item is not None: if item is not None:
if item.data(0, QtCore.Qt.UserRole) == gid: if item.data(0, QtCore.Qt.ItemDataRole.UserRole) == gid:
item.setBackground(0, QtGui.QBrush(QtGui.QColor('gray'))) item.setBackground(0, QtGui.QBrush(QtGui.QColor('gray')))
else: else:
item.setBackground(0, QtGui.QBrush()) item.setBackground(0, QtGui.QBrush())
@ -527,11 +549,14 @@ class DataTree(QtWidgets.QTreeWidget):
def uncheck_sets(self, sets: list[str]): def uncheck_sets(self, sets: list[str]):
self.blockSignals(True) self.blockSignals(True)
iterator = QtWidgets.QTreeWidgetItemIterator(self) iterator = QtWidgets.QTreeWidgetItemIterator(self)
self._checked_sets = set()
while iterator.value(): while iterator.value():
item = iterator.value() item = iterator.value()
if item is not None: if item is not None:
if item.data(0, QtCore.Qt.UserRole) in sets: if item.data(0, QtCore.Qt.ItemDataRole.UserRole) in sets:
item.setCheckState(0, QtCore.Qt.Unchecked) item.setCheckState(0, QtCore.Qt.CheckState.Unchecked)
else:
self._checked_sets.add(item.data(0, QtCore.Qt.ItemDataRole.UserRole))
iterator += 1 iterator += 1
self.blockSignals(False) self.blockSignals(False)
@ -559,6 +584,8 @@ class DataWidget(QtWidgets.QWidget, Ui_DataWidget):
self.propwidget.expansionChanged.connect(self.show_property) self.propwidget.expansionChanged.connect(self.show_property)
self.proptable.propertyChanged.connect(self.change_property) self.proptable.propertyChanged.connect(self.change_property)
self.pokemon_toolbutton.clicked.connect(self.catchthemall)
make_action_icons(self) make_action_icons(self)
def add_graph(self, idd: str, name: str): def add_graph(self, idd: str, name: str):
@ -586,7 +613,7 @@ class DataWidget(QtWidgets.QWidget, Ui_DataWidget):
sid = [] sid = []
for i in self.tree.selectedIndexes(): for i in self.tree.selectedIndexes():
if i.column() == 0: if i.column() == 0:
sid.append(i.data(role=QtCore.Qt.UserRole)) sid.append(i.data(role=QtCore.Qt.ItemDataRole.UserRole))
self.startShowProperty.emit(sid) self.startShowProperty.emit(sid)
@ -595,15 +622,23 @@ class DataWidget(QtWidgets.QWidget, Ui_DataWidget):
self.proptable.populate(props) self.proptable.populate(props)
def change_property(self, key1, key2, value): def change_property(self, key1, key2, value):
ids = [item.data(0, QtCore.Qt.UserRole) for item in self.tree.selectedItems()]
if key2 == 'Value': if key2 == 'Value':
try: try:
value = float(value) value = float(value)
except ValueError: except ValueError:
QtWidgets.QMessageBox.warning(self, 'Invalid entry', QtWidgets.QMessageBox.warning(
'Value %r is not a valid number for `value`.' % value) self,
'Invalid entry',
f'Value {value!r} is not a valid number for `value`.')
return return
ids = []
for item in self.tree.selectedItems():
ids.append(item.data(0, QtCore.Qt.ItemDataRole.UserRole))
item.setToolTip(0, str(value))
else:
ids = [item.data(0, QtCore.Qt.ItemDataRole.UserRole) for item in self.tree.selectedItems()]
self.propertyChanged.emit(ids, key1, key2, value) self.propertyChanged.emit(ids, key1, key2, value)
def uncheck_sets(self, sets: list[str]): def uncheck_sets(self, sets: list[str]):
@ -612,6 +647,12 @@ class DataWidget(QtWidgets.QWidget, Ui_DataWidget):
def set_name(self, sid, value): def set_name(self, sid, value):
self.tree.set_name(sid, value) self.tree.set_name(sid, value)
def catchthemall(self):
from gui_qt.lib.pokemon import QPoke
dialog = QPoke( parent=self)
dialog.exec()
@property @property
def management(self): def management(self):
return self.tree.management return self.tree.management

View File

@ -41,7 +41,7 @@ class PropWidget(QtWidgets.QWidget):
idx = table.indexFromItem(item) idx = table.indexFromItem(item)
self.propertyChanged.emit(self.tab.tabText(tab_idx), self.propertyChanged.emit(self.tab.tabText(tab_idx),
table.item(idx.row(), idx.column()-1).text(), table.item(idx.row(), idx.column()-1).text(),
item.data(QtCore.Qt.DisplayRole)) item.data(QtCore.Qt.ItemDataRole.DisplayRole))
@QtCore.pyqtSlot(int) @QtCore.pyqtSlot(int)
def tab_change(self, idx: int): def tab_change(self, idx: int):
@ -66,10 +66,10 @@ class PropTable(QtWidgets.QTableWidget):
self.blockSignals(True) self.blockSignals(True)
for k, v in prop.items(): for k, v in prop.items():
value_item = QtWidgets.QTableWidgetItem('') value_item = QtWidgets.QTableWidgetItem('')
value_item.setData(QtCore.Qt.DisplayRole, v) value_item.setData(QtCore.Qt.ItemDataRole.DisplayRole, v)
key_item = QtWidgets.QTableWidgetItem(k) key_item = QtWidgets.QTableWidgetItem(k)
key_item.setFlags(QtCore.Qt.NoItemFlags) key_item.setFlags(QtCore.Qt.ItemFlag.NoItemFlags)
key_item.setForeground(QtGui.QBrush(QtGui.QColor(0, 0, 0))) key_item.setForeground(QtGui.QBrush(QtGui.QColor(0, 0, 0)))
self.setRowCount(self.rowCount()+1) self.setRowCount(self.rowCount()+1)

View File

@ -1,6 +1,6 @@
import re import re
from ..Qt import QtCore, QtWidgets from ..Qt import QtCore, QtWidgets, QtGui
from .._py.ptstab import Ui_Form from .._py.ptstab import Ui_Form
from ..lib.pg_objects import LogInfiniteLine, RegionItem from ..lib.pg_objects import LogInfiniteLine, RegionItem
@ -27,15 +27,23 @@ class PointSelectWidget(QtWidgets.QWidget, Ui_Form):
self._last_item = None self._last_item = None
self.connected_figure = '' self.connected_figure = ''
self._avg_modes = ['mean', 'sum', 'integral', 'std']
self._special_values = ['max', 'absmax', 'min', 'absmin']
self._group_modes = ['group', 'x']
self.okButton.clicked.connect(self.apply) self.okButton.clicked.connect(self.apply)
self.deleteButton.clicked.connect(self.remove_points) self.deleteButton.clicked.connect(self.remove_points)
self.peaktable.itemChanged.connect(self.editing_finished) self.peaktable.itemChanged.connect(self.editing_finished)
self.peaktable.itemDoubleClicked.connect(self.editing_started) self.peaktable.itemDoubleClicked.connect(self.editing_started)
self.left_limit.setValidator(QtGui.QDoubleValidator())
self.right_limit.setValidator(QtGui.QDoubleValidator())
def keyPressEvent(self, e): def keyPressEvent(self, e):
if e.key() == QtCore.Qt.Key_Delete: if e.key() == QtCore.Qt.Key.Key_Delete:
self.remove_points() self.remove_points()
elif e.key() == QtCore.Qt.Key_F2: elif e.key() == QtCore.Qt.Key.Key_F2:
self.editing_started() self.editing_started()
else: else:
super().keyPressEvent(e) super().keyPressEvent(e)
@ -91,7 +99,7 @@ class PointSelectWidget(QtWidgets.QWidget, Ui_Form):
item = QtWidgets.QListWidgetItem(f'{self.pts[-1][0]:.5g} - {self.pts[-1][1]:.5g}') item = QtWidgets.QListWidgetItem(f'{self.pts[-1][0]:.5g} - {self.pts[-1][1]:.5g}')
else: else:
item = QtWidgets.QListWidgetItem(f'{self.pts[-1]:.5g}') item = QtWidgets.QListWidgetItem(f'{self.pts[-1]:.5g}')
item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable) item.setFlags(item.flags() ^ QtCore.Qt.ItemFlag.ItemIsEditable)
self.peaktable.blockSignals(True) self.peaktable.blockSignals(True)
self.peaktable.addItem(item) self.peaktable.addItem(item)
self.peaktable.blockSignals(False) self.peaktable.blockSignals(False)
@ -102,21 +110,22 @@ class PointSelectWidget(QtWidgets.QWidget, Ui_Form):
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def apply(self) -> dict: def apply(self) -> dict:
ret_dic = {'avg_range': [self.left_pt.value(), self.right_pt.value()], ret_dic = {
'avg_mode': {0: 'mean', 1: 'sum', 2: 'integral'}[self.average_combobox.currentIndex()], 'avg_range': self.get_limits(),
'special': None, 'idx': None, 'avg_mode': self._avg_modes[self.average_combobox.currentIndex()],
'xy': (self.xbutton.isChecked(), self.ybutton.isChecked())} 'special': None,
'idx': None,
'xy': (self.xbutton.isChecked(), self.ybutton.isChecked()),
'groupby': self._group_modes[self.group_box.currentIndex()],
}
if self.groupBox_2.isChecked(): if self.special_checkbox.isChecked():
ret_dic['special'] = {0: 'max', 1: 'absmax', 2: 'min', 3: 'absmin'}[self.special_comboBox.currentIndex()] ret_dic['special'] = self._special_values[self.special_comboBox.currentIndex()]
if len(self.pts) != 0: if len(self.pts) != 0:
ret_dic['idx'] = self.pts ret_dic['idx'] = self.pts
if self.graph_checkbox.isChecked(): gid = self.graph_combobox.currentData() if not self.graph_checkbox.isChecked() else ''
gid = ''
else:
gid = self.graph_combobox.currentData()
self.points_selected.emit(ret_dic, gid) self.points_selected.emit(ret_dic, gid)
@ -199,4 +208,22 @@ class PointSelectWidget(QtWidgets.QWidget, Ui_Form):
@QtCore.pyqtSlot(int, name='on_graph_checkbox_stateChanged') @QtCore.pyqtSlot(int, name='on_graph_checkbox_stateChanged')
def changed_state(self, checked): def changed_state(self, checked):
self.graph_combobox.setEnabled(checked!=QtCore.Qt.Checked) self.graph_combobox.setEnabled(checked != QtCore.Qt.CheckState.Checked)
@QtCore.pyqtSlot(int, name='on_special_checkbox_stateChanged')
def changed_special(self, checked: int):
self.graph_combobox.setEnabled(checked != QtCore.Qt.CheckState.Checked)
def get_limits(self) -> tuple[float, float, str]:
try:
left = float(self.left_limit.text())
except ValueError:
left = 0.
try:
right = float(self.right_limit.text())
except ValueError:
right = 0.
return left, right, self.limit_combobox.currentText()

View File

@ -76,38 +76,24 @@ class QPreviewDialog(QtWidgets.QDialog, Ui_ApodEdit):
self.pivot_lineedit.textEdited.connect(lambda x: self.pvt_line.setValue(float(x))) self.pivot_lineedit.textEdited.connect(lambda x: self.pvt_line.setValue(float(x)))
def add_data(self: QPreviewDialog, data: FID | Spectrum) -> bool: def add_data(self: QPreviewDialog, data: FID | Spectrum) -> bool:
if isinstance(data, FID): if isinstance(data, FID):
if self._all_freq: valid, (real_plt, imag_plt, real_plt_fft, imag_plt_fft) = self._prep_time(data)
msg = QtWidgets.QMessageBox.warning(self, 'Mixed types',
'Timesignals and spectra cannot be edited at the same time.')
return False
else:
self._all_time = True
self._all_freq = False
elif isinstance(data, Spectrum): elif isinstance(data, Spectrum):
if self._all_time: valid, (real_plt, imag_plt, real_plt_fft, imag_plt_fft) = self._prep_freq(data)
msg = QtWidgets.QMessageBox.warning(self, 'Mixed types',
'Timesignals and spectra cannot be edited at the same time.')
return False
else:
self._all_time = False
self._all_freq = True
fid = data.copy() else:
spec = self._temp_fft_time(fid.x, fid.y, self.baseline_box.isChecked()) return False
if not valid:
return False
x_len = data.x.size x_len = data.x.size
self.zf_spinbox.setMaximum(min(2**17//x_len, 3)) self.zf_spinbox.setMaximum(min(2**17//x_len, 3))
real_plt = PlotItem(x=fid.x, y=fid.y.real, pen=mkPen('b'))
imag_plt = PlotItem(x=fid.x, y=fid.y.imag, pen=mkPen('r'))
self.time_graph.addItem(imag_plt) self.time_graph.addItem(imag_plt)
self.time_graph.addItem(real_plt) self.time_graph.addItem(real_plt)
real_plt_fft = PlotItem(x=spec[0], y=spec[1].real, pen=mkPen('b'))
imag_plt_fft = PlotItem(x=spec[0], y=spec[1].imag, pen=mkPen('r'))
self.freq_graph.addItem(imag_plt_fft) self.freq_graph.addItem(imag_plt_fft)
self.freq_graph.addItem(real_plt_fft) self.freq_graph.addItem(real_plt_fft)
@ -118,12 +104,52 @@ class QPreviewDialog(QtWidgets.QDialog, Ui_ApodEdit):
for p in [self._tmp_data_zf, self._tmp_data_ap]: for p in [self._tmp_data_zf, self._tmp_data_ap]:
p.append((data.x, data.y.copy())) p.append((data.x, data.y.copy()))
self._tmp_data_ph.append((data.x, data.y, spec[0], spec[1]))
self.graphs.append((real_plt, imag_plt, real_plt_fft, imag_plt_fft)) self.graphs.append((real_plt, imag_plt, real_plt_fft, imag_plt_fft))
return True return True
def _prep_time(self, data) -> tuple[bool, tuple]:
if self._all_freq:
_ = QtWidgets.QMessageBox.warning(self, 'Mixed types',
'Time signals and spectra cannot be edited at the same time.')
return False, tuple()
fid = data.copy()
spec = self._temp_fft_time(fid.x, fid.y, self.baseline_box.isChecked())
self._all_time = True
self._all_freq = False
real_plt = PlotItem(x=fid.x, y=fid.y.real, pen=mkPen('b'))
imag_plt = PlotItem(x=fid.x, y=fid.y.imag, pen=mkPen('r'))
real_plt_fft = PlotItem(x=spec[0], y=spec[1].real, pen=mkPen('b'))
imag_plt_fft = PlotItem(x=spec[0], y=spec[1].imag, pen=mkPen('r'))
self._tmp_data_ph.append((data.x, data.y, spec[0], spec[1]))
return True, (real_plt, imag_plt, real_plt_fft, imag_plt_fft)
def _prep_freq(self, data) -> tuple[bool, tuple]:
if self._all_time:
_ = QtWidgets.QMessageBox.warning(self, 'Mixed types',
'Time signals and spectra cannot be edited at the same time.')
return False, tuple()
spec = data.copy()
fid = self._temp_fft_time(spec.x, spec.y, self.baseline_box.isChecked())
self._all_time = False
self._all_freq = True
real_plt = PlotItem(x=fid[0], y=fid[1].real, pen=mkPen('b'))
imag_plt = PlotItem(x=fid[0], y=fid[1].imag, pen=mkPen('r'))
real_plt_fft = PlotItem(x=spec.x, y=spec.y.real, pen=mkPen('b'))
imag_plt_fft = PlotItem(x=spec.x, y=spec.y.imag, pen=mkPen('r'))
self._tmp_data_ph.append((data.x, data.y, spec.x, spec.y))
return True, (real_plt, imag_plt, real_plt_fft, imag_plt_fft)
@QtCore.pyqtSlot(name='on_baseline_box_clicked') @QtCore.pyqtSlot(name='on_baseline_box_clicked')
def _update_bl(self): def _update_bl(self):
if self.baseline_box.isChecked(): if self.baseline_box.isChecked():
@ -200,7 +226,7 @@ class QPreviewDialog(QtWidgets.QDialog, Ui_ApodEdit):
ph1 = self.ph1_spinbox.value() ph1 = self.ph1_spinbox.value()
for i, (x, y) in enumerate(self._tmp_data_ap): for i, (x, y) in enumerate(self._tmp_data_ap):
x_fft, y_fft = self._temp_fft_time(x, y, self.baseline_box.isChecked()) x_fft, y_fft = self._temp_fft(x, y, self.baseline_box.isChecked())
if ph0 != 0: if ph0 != 0:
y = self._temp_phase(x, y, ph0, 0, 0) y = self._temp_phase(x, y, ph0, 0, 0)
@ -213,7 +239,7 @@ class QPreviewDialog(QtWidgets.QDialog, Ui_ApodEdit):
else: else:
self.pvt_line.hide() self.pvt_line.hide()
for i, (x, y) in enumerate(self._tmp_data_ap): for i, (x, y) in enumerate(self._tmp_data_ap):
self._tmp_data_ph[i] = x, y, *self._temp_fft_time(x, y, self.baseline_box.isChecked()) self._tmp_data_ph[i] = x, y, *self._temp_fft(x, y, self.baseline_box.isChecked())
self._update_plots() self._update_plots()
@ -346,7 +372,6 @@ class QPreviewDialog(QtWidgets.QDialog, Ui_ApodEdit):
self.data = [] self.data = []
self.graphs = [] self.graphs = []
self.freq_graph.removeItem(self.pvt_line) self.freq_graph.removeItem(self.pvt_line)
self.time_graph.removeItem(self.pvt_line)
self.blockSignals(False) self.blockSignals(False)
@ -388,13 +413,16 @@ class QPreviewDialog(QtWidgets.QDialog, Ui_ApodEdit):
vb = self.time_graph.getPlotItem().getViewBox() vb = self.time_graph.getPlotItem().getViewBox()
vb.disableAutoRange(axis=vb.YAxis) vb.disableAutoRange(axis=vb.YAxis)
if self._all_time is not None:
self.zerofill_box.setVisible(self._all_time) self.zerofill_box.setVisible(self._all_time)
self.apod_box.setVisible(self._all_time) self.apod_box.setVisible(self._all_time)
self.shift_box.setVisible(self._all_time) self.shift_box.setVisible(self._all_time)
self.time_graph.setVisible(self._all_time) self.time_graph.setVisible(self._all_time)
self.logtime_widget.setVisible(self._all_time) self.logtime_widget.setVisible(self._all_time)
self._temp_baseline = self._temp_baseline_time if self._all_time else self._temp_baseline_freq
self._temp_fft = self._temp_fft_time if self._all_time else self._temp_fft_freq
@QtCore.pyqtSlot(int, name='on_logx_time_stateChanged') @QtCore.pyqtSlot(int, name='on_logx_time_stateChanged')
@QtCore.pyqtSlot(int, name='on_logy_time_stateChanged') @QtCore.pyqtSlot(int, name='on_logy_time_stateChanged')
@QtCore.pyqtSlot(int, name='on_logx_freq_stateChanged') @QtCore.pyqtSlot(int, name='on_logx_freq_stateChanged')
@ -414,14 +442,3 @@ class QPreviewDialog(QtWidgets.QDialog, Ui_ApodEdit):
vb = self.time_graph.getPlotItem().getViewBox() vb = self.time_graph.getPlotItem().getViewBox()
vb.disableAutoRange(axis=vb.YAxis) vb.disableAutoRange(axis=vb.YAxis)
self._temp_baseline = self._temp_baseline_time if self._all_time else self._temp_baseline_freq
self._temp_fft = self._temp_fft_time if self._all_time else self._temp_fft_freq
self.freq_graph.setVisible(self._all_time)
if self._all_freq:
self.time_graph.addItem(self.pvt_line)
else:
self.freq_graph.addItem(self.pvt_line)

View File

@ -3,10 +3,10 @@ from __future__ import annotations
from typing import Any from typing import Any
from numpy import ndarray, iscomplexobj, asarray from numpy import ndarray, iscomplexobj, asarray
from pyqtgraph import PlotDataItem
from ..Qt import QtGui, QtCore, QtWidgets from ..Qt import QtGui, QtCore, QtWidgets
from .._py.valueeditor import Ui_MaskDialog from .._py.valueeditor import Ui_MaskDialog
from ..lib.pg_objects import PlotItem
class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog): class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog):
@ -35,12 +35,12 @@ class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog):
self.tableView.setModel(self.model) self.tableView.setModel(self.model)
self.tableView.setSelectionModel(self.selection_model) self.tableView.setSelectionModel(self.selection_model)
self.tableView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.tableView.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.CustomContextMenu)
self.tableView.customContextMenuRequested.connect(self.ctx) self.tableView.customContextMenuRequested.connect(self.ctx)
self.selection_real = PlotDataItem(x=[], y=[], symbolSize=25, symbol='x', self.selection_real = PlotItem(x=[], y=[], symbolSize=25, symbol='x',
pen=None, symbolPen='#c9308e', symbolBrush='#c9308e') pen=None, symbolPen='#c9308e', symbolBrush='#c9308e')
self.selection_imag = PlotDataItem(x=[], y=[], symbolSize=25, symbol='+', self.selection_imag = PlotItem(x=[], y=[], symbolSize=25, symbol='+',
pen=None, symbolPen='#dcdcdc', symbolBrush='#dcdcdc') pen=None, symbolPen='#dcdcdc', symbolBrush='#dcdcdc')
def __call__(self, items: dict): def __call__(self, items: dict):
@ -133,7 +133,7 @@ class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog):
def keyPressEvent(self, evt): def keyPressEvent(self, evt):
if evt.matches(QtGui.QKeySequence.Copy): if evt.matches(QtGui.QKeySequence.Copy):
self.copy_selection() self.copy_selection()
elif evt.key() == QtCore.Qt.Key_Delete: elif evt.key() == QtCore.Qt.Key.Key_Delete:
self.delete_item() self.delete_item()
else: else:
super().keyPressEvent(evt) super().keyPressEvent(evt)
@ -229,7 +229,7 @@ class ValueModel(QtCore.QAbstractTableModel):
""" """
itemChanged = QtCore.pyqtSignal(int, int, str) itemChanged = QtCore.pyqtSignal(int, int, str)
load_number = 20 load_number = 20
maskRole = QtCore.Qt.UserRole+321 maskRole = QtCore.Qt.ItemDataRole.UserRole+321
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent=parent) super().__init__(parent=parent)
@ -240,7 +240,7 @@ class ValueModel(QtCore.QAbstractTableModel):
self.mask = None self.mask = None
self.headers = ['x', 'y', '\u0394y'] self.headers = ['x', 'y', '\u0394y']
for i, hd in enumerate(self.headers): for i, hd in enumerate(self.headers):
self.setHeaderData(i, QtCore.Qt.Horizontal, hd) self.setHeaderData(i, QtCore.Qt.Orientation.Horizontal, hd)
def rowCount(self, *args, **kwargs) -> int: def rowCount(self, *args, **kwargs) -> int:
return self.total_rows return self.total_rows
@ -258,25 +258,28 @@ class ValueModel(QtCore.QAbstractTableModel):
self.mask = mask.tolist() self.mask = mask.tolist()
self.endResetModel() self.endResetModel()
self.dataChanged.emit(self.index(0, 0), self.index(0, 1), [QtCore.Qt.DisplayRole]) self.dataChanged.emit(
self.index(0, 0),
self.index(0, 1), [QtCore.Qt.ItemDataRole.DisplayRole]
)
def data(self, idx: QtCore.QModelIndex, role=QtCore.Qt.DisplayRole) -> Any: def data(self, idx: QtCore.QModelIndex, role=QtCore.Qt.ItemDataRole.DisplayRole) -> Any:
if not idx.isValid(): if not idx.isValid():
return return
row = idx.row() row = idx.row()
if role in [QtCore.Qt.DisplayRole, QtCore.Qt.EditRole]: if role in [QtCore.Qt.ItemDataRole.DisplayRole, QtCore.Qt.ItemDataRole.EditRole]:
val = self._data[row][idx.column()] val = self._data[row][idx.column()]
return self.as_string(val) return self.as_string(val)
elif role == QtCore.Qt.BackgroundRole: elif role == QtCore.Qt.ItemDataRole.BackgroundRole:
pal = QtGui.QGuiApplication.palette() pal = QtGui.QGuiApplication.palette()
if not self.mask[row]: if not self.mask[row]:
return pal.color(QtGui.QPalette.Disabled, QtGui.QPalette.Base) return pal.color(QtGui.QPalette.Disabled, QtGui.QPalette.Base)
else: else:
return pal.color(QtGui.QPalette.Base) return pal.color(QtGui.QPalette.Base)
elif role == QtCore.Qt.ForegroundRole: elif role == QtCore.Qt.ItemDataRole.ForegroundRole:
pal = QtGui.QGuiApplication.palette() pal = QtGui.QGuiApplication.palette()
if not self.mask[row]: if not self.mask[row]:
return pal.color(QtGui.QPalette.Disabled, QtGui.QPalette.Text) return pal.color(QtGui.QPalette.Disabled, QtGui.QPalette.Text)
@ -289,7 +292,7 @@ class ValueModel(QtCore.QAbstractTableModel):
else: else:
return return
def setData(self, idx: QtCore.QModelIndex, value: str | bool, role=QtCore.Qt.DisplayRole) -> Any: def setData(self, idx: QtCore.QModelIndex, value: str | bool, role=QtCore.Qt.ItemDataRole.DisplayRole) -> Any:
col, row = idx.column(), idx.row() col, row = idx.column(), idx.row()
if role == ValueModel.maskRole: if role == ValueModel.maskRole:
@ -299,7 +302,7 @@ class ValueModel(QtCore.QAbstractTableModel):
return True return True
if value: if value:
if role == QtCore.Qt.EditRole: if role == QtCore.Qt.ItemDataRole.EditRole:
if value == self.as_string(self._data[row][col]): if value == self.as_string(self._data[row][col]):
return True return True
@ -322,9 +325,9 @@ class ValueModel(QtCore.QAbstractTableModel):
else: else:
return False return False
def headerData(self, section: int, orientation, role=QtCore.Qt.DisplayRole) -> Any: def headerData(self, section: int, orientation, role=QtCore.Qt.ItemDataRole.DisplayRole) -> Any:
if role == QtCore.Qt.DisplayRole: if role == QtCore.Qt.ItemDataRole.DisplayRole:
if orientation == QtCore.Qt.Horizontal: if orientation == QtCore.Qt.Orientation.Horizontal:
return self.headers[section] return self.headers[section]
else: else:
return str(section+1) return str(section+1)
@ -346,7 +349,7 @@ class ValueModel(QtCore.QAbstractTableModel):
self.endInsertRows() self.endInsertRows()
def flags(self, idx: QtCore.QModelIndex) -> QtCore.Qt.ItemFlag: def flags(self, idx: QtCore.QModelIndex) -> QtCore.Qt.ItemFlag:
return QtCore.QAbstractTableModel.flags(self, idx) | QtCore.Qt.ItemIsEditable return QtCore.QAbstractTableModel.flags(self, idx) | QtCore.Qt.ItemFlag.ItemIsEditable
def removeRows(self, pos: int, rows: int, parent=None, *args, **kwargs) -> bool: def removeRows(self, pos: int, rows: int, parent=None, *args, **kwargs) -> bool:
self.beginRemoveRows(parent, pos, pos+rows-1) self.beginRemoveRows(parent, pos, pos+rows-1)
@ -382,6 +385,6 @@ class ValueModel(QtCore.QAbstractTableModel):
@staticmethod @staticmethod
def as_string(value) -> str: def as_string(value) -> str:
if isinstance(value, complex): if isinstance(value, complex):
return f'{value.real:.8g}{value.imag:+.8g}j' return f'{value.real:.13g}{value.imag:+.13g}j'
else: else:
return f'{value:.8g}' return f'{value:.13g}'

View File

@ -136,8 +136,8 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
max_x = max(max_x, data.x.max()) max_x = max(max_x, data.x.max())
item = QtWidgets.QListWidgetItem(name) item = QtWidgets.QListWidgetItem(name)
item.setCheckState(QtCore.Qt.Checked) item.setCheckState(QtCore.Qt.CheckState.Checked)
item.setData(QtCore.Qt.UserRole, key) item.setData(QtCore.Qt.ItemDataRole.UserRole, key)
item.setForeground(mkBrush(c.rgb())) item.setForeground(mkBrush(c.rgb()))
self.listWidget.addItem(item) self.listWidget.addItem(item)
@ -191,10 +191,10 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
for idx in range(self.listWidget.count()): for idx in range(self.listWidget.count()):
item = self.listWidget.item(idx) item = self.listWidget.item(idx)
if item.checkState() == QtCore.Qt.Unchecked: if item.checkState() == QtCore.Qt.CheckState.Unchecked:
continue continue
key = item.data(QtCore.Qt.UserRole) key = item.data(QtCore.Qt.ItemDataRole.UserRole)
plot = self._plots[key] plot = self._plots[key]
data, _ = self._dsc[key] data, _ = self._dsc[key]
@ -214,7 +214,7 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
item = self.listWidget.item(idx) item = self.listWidget.item(idx)
tree_item = QtWidgets.QTreeWidgetItem([item.text()]) tree_item = QtWidgets.QTreeWidgetItem([item.text()])
values = self._tg_value.get(item.data(QtCore.Qt.UserRole)) values = self._tg_value.get(item.data(QtCore.Qt.ItemDataRole.UserRole))
if values is not None: if values is not None:
for name, pos in values.items(): for name, pos in values.items():
@ -223,7 +223,7 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
self.tg_tree.addTopLevelItem(tree_item) self.tg_tree.addTopLevelItem(tree_item)
key = item.data(QtCore.Qt.UserRole) key = item.data(QtCore.Qt.ItemDataRole.UserRole)
plot = self._plots[key] plot = self._plots[key]
data, _ = self._dsc[key] data, _ = self._dsc[key]
@ -251,7 +251,7 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
@QtCore.pyqtSlot(QtWidgets.QListWidgetItem) @QtCore.pyqtSlot(QtWidgets.QListWidgetItem)
def change_visibility(self, item: QtWidgets.QListWidgetItem): def change_visibility(self, item: QtWidgets.QListWidgetItem):
is_checked = bool(item.checkState()) is_checked = bool(item.checkState())
plot = self._plots[item.data(QtCore.Qt.UserRole)] plot = self._plots[item.data(QtCore.Qt.ItemDataRole.UserRole)]
for val in plot: for val in plot:
val.setVisible(is_checked) val.setVisible(is_checked)
@ -275,10 +275,10 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
self.tnmh_tree.clear() self.tnmh_tree.clear()
for idx in range(self.listWidget.count()): for idx in range(self.listWidget.count()):
item = self.listWidget.item(idx) item = self.listWidget.item(idx)
if item.checkState() == QtCore.Qt.Unchecked: if item.checkState() == QtCore.Qt.CheckState.Unchecked:
continue continue
key = item.data(QtCore.Qt.UserRole) key = item.data(QtCore.Qt.ItemDataRole.UserRole)
data = self.get_fictive(key, baselines) data = self.get_fictive(key, baselines)
@ -292,7 +292,7 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
item = self.listWidget.item(idx) item = self.listWidget.item(idx)
tree_item = QtWidgets.QTreeWidgetItem([item.text()]) tree_item = QtWidgets.QTreeWidgetItem([item.text()])
values = self._fit.get(item.data(QtCore.Qt.UserRole)) values = self._fit.get(item.data(QtCore.Qt.ItemDataRole.UserRole))
if values is not None: if values is not None:
child_item = QtWidgets.QTreeWidgetItem([values.parameter_string()]) child_item = QtWidgets.QTreeWidgetItem([values.parameter_string()])
@ -305,10 +305,10 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
ret_dic = {} ret_dic = {}
for idx in range(self.listWidget.count()): for idx in range(self.listWidget.count()):
item = self.listWidget.item(idx) item = self.listWidget.item(idx)
if item.checkState() == QtCore.Qt.Unchecked: if item.checkState() == QtCore.Qt.CheckState.Unchecked:
continue continue
key = item.data(QtCore.Qt.UserRole) key = item.data(QtCore.Qt.ItemDataRole.UserRole)
cp = None cp = None
if self.fictive_export_check.isChecked(): if self.fictive_export_check.isChecked():
@ -332,10 +332,10 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
m = [] m = []
for idx in range(self.listWidget.count()): for idx in range(self.listWidget.count()):
item = self.listWidget.item(idx) item = self.listWidget.item(idx)
if item.checkState() == QtCore.Qt.Unchecked: if item.checkState() == QtCore.Qt.CheckState.Unchecked:
continue continue
key = item.data(QtCore.Qt.UserRole) key = item.data(QtCore.Qt.ItemDataRole.UserRole)
data, _ = self._dsc[key] data, _ = self._dsc[key]
try: try:
tg_value = self._tg_value[key][tg_type][0] tg_value = self._tg_value[key][tg_type][0]

View File

View File

@ -1,5 +1,6 @@
# CodeEditor based on QT example, Python syntax highlighter found on Python site # CodeEditor based on QT example, Python syntax highlighter found on Python site
import typing
from ast import parse
from ..Qt import QtGui, QtCore, QtWidgets from ..Qt import QtGui, QtCore, QtWidgets
@ -71,7 +72,8 @@ class PythonHighlighter(QtGui.QSyntaxHighlighter):
(r'\bdef\b\s*(\w+)', 1, STYLES['defclass']), (r'\bdef\b\s*(\w+)', 1, STYLES['defclass']),
# 'class' followed by an identifier # 'class' followed by an identifier
(r'\bclass\b\s*(\w+)', 1, STYLES['defclass']), (r'\bclass\b\s*(\w+)', 1, STYLES['defclass']),
# @ followed by a word
# decorator @ followed by a word
(r'\s*@(\w+)\s*', 0, STYLES['property']), (r'\s*@(\w+)\s*', 0, STYLES['property']),
# Numeric literals # Numeric literals
@ -79,7 +81,6 @@ class PythonHighlighter(QtGui.QSyntaxHighlighter):
(r'\b[+-]?0[xX][\dA-Fa-f]+[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']), (r'\b[+-]?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\b', 0, STYLES['numbers']),
# Double-quoted string, possibly containing escape sequences # Double-quoted string, possibly containing escape sequences
(r'[rf]?"[^"\\]*(\\.[^"\\]*)*"', 0, STYLES['string']), (r'[rf]?"[^"\\]*(\\.[^"\\]*)*"', 0, STYLES['string']),
# Single-quoted string, possibly containing escape sequences # Single-quoted string, possibly containing escape sequences
@ -186,10 +187,10 @@ class CodeEditor(QtWidgets.QPlainTextEdit):
self.highlight = PythonHighlighter(self.document()) self.highlight = PythonHighlighter(self.document())
def keyPressEvent(self, evt): def keyPressEvent(self, evt):
if evt.key() == QtCore.Qt.Key_Tab: if evt.key() == QtCore.Qt.Key.Key_Tab:
# use spaces instead of tab # use spaces instead of tab
self.insertPlainText(' '*4) self.insertPlainText(' '*4)
elif evt.key() == QtCore.Qt.Key_Insert: elif evt.key() == QtCore.Qt.Key.Key_Insert:
self.setOverwriteMode(not self.overwriteMode()) self.setOverwriteMode(not self.overwriteMode())
else: else:
super().keyPressEvent(evt) super().keyPressEvent(evt)
@ -224,7 +225,7 @@ class CodeEditor(QtWidgets.QPlainTextEdit):
def paintevent_linenumber(self, evt): def paintevent_linenumber(self, evt):
painter = QtGui.QPainter(self.current_linenumber) painter = QtGui.QPainter(self.current_linenumber)
painter.fillRect(evt.rect(), QtCore.Qt.lightGray) painter.fillRect(evt.rect(), QtCore.Qt.GlobalColor.lightGray)
block = self.firstVisibleBlock() block = self.firstVisibleBlock()
block_number = block.blockNumber() block_number = block.blockNumber()
@ -236,9 +237,9 @@ class CodeEditor(QtWidgets.QPlainTextEdit):
while block.isValid() and (top <= evt.rect().bottom()): while block.isValid() and (top <= evt.rect().bottom()):
if block.isVisible() and (bottom >= evt.rect().top()): if block.isVisible() and (bottom >= evt.rect().top()):
number = str(block_number + 1) number = str(block_number + 1)
painter.setPen(QtCore.Qt.black) painter.setPen(QtCore.Qt.GlobalColor.black)
painter.drawText(0, int(top), self.current_linenumber.width() - 3, height, painter.drawText(0, int(top), self.current_linenumber.width() - 3, height,
QtCore.Qt.AlignRight, number) QtCore.Qt.AlignmentFlag.AlignRight, number)
block = block.next() block = block.next()
top = bottom top = bottom
@ -251,7 +252,7 @@ class CodeEditor(QtWidgets.QPlainTextEdit):
if not self.isReadOnly(): if not self.isReadOnly():
selection = QtWidgets.QTextEdit.ExtraSelection() selection = QtWidgets.QTextEdit.ExtraSelection()
line_color = QtGui.QColor(QtCore.Qt.yellow).lighter(180) line_color = QtGui.QColor(QtCore.Qt.GlobalColor.yellow).lighter(180)
selection.format.setBackground(line_color) selection.format.setBackground(line_color)
selection.format.setProperty(QtGui.QTextFormat.FullWidthSelection, True) selection.format.setProperty(QtGui.QTextFormat.FullWidthSelection, True)
@ -260,3 +261,49 @@ class CodeEditor(QtWidgets.QPlainTextEdit):
extra_selections.append(selection) extra_selections.append(selection)
self.setExtraSelections(extra_selections) self.setExtraSelections(extra_selections)
class EditorWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
layout = QtWidgets.QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
self.editor = CodeEditor(self)
layout.addWidget(self.editor)
self.error_label = QtWidgets.QLabel(self)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.error_label.setFont(font)
self.error_label.setVisible(False)
layout.addWidget(self.error_label)
self.setLayout(layout)
for attr in ['appendPlainText', 'toPlainText', 'insertPlainText', 'setPlainText']:
setattr(self, attr, getattr(self.editor, attr))
self.editor.textChanged.connect(self._check_syntax)
def _check_syntax(self) -> (int, tuple[typing.Any]):
is_valid = True
# Compile into an AST and check for syntax errors.
try:
_ = parse(self.toPlainText(), filename='<string>')
except SyntaxError as e:
self.error_label.setText(f'Syntax error in line {e.lineno}: {e.args[0]}')
is_valid = False
except Exception as e:
self.error_label.setText(f'Unexpected error: {e.args[0]}')
is_valid = False
self.error_label.setVisible(not is_valid)

View File

@ -0,0 +1,30 @@
from __future__ import annotations
from pathlib import Path
from .usermodeleditor import QUsermodelEditor
from ..Qt import QtWidgets, QtCore, QtGui
class QEditor(QUsermodelEditor):
runSignal = QtCore.pyqtSignal(str)
def __init__(self, path: str | Path = None, parent=None):
super().__init__(path, parent=parent)
self.add_run_button()
def add_run_button(self):
self.disclaimer = QtWidgets.QLabel("This is work in progress and less than perfect :(")
self.disclaimer.setStyleSheet('QLabel {color: rgb(255, 0, 0); font-weight: bold; font-size: 2.5em;};')
self.centralwidget.layout().insertWidget(0, self.disclaimer)
self.run_button = QtWidgets.QPushButton("Run")
self.centralwidget.layout().addWidget(self.run_button)
self.run_button.clicked.connect(self.start_script)
@QtCore.pyqtSlot()
def start_script(self):
self.runSignal.emit(self.edit_field.toPlainText())

View File

@ -3,7 +3,7 @@ from __future__ import annotations
from pathlib import Path from pathlib import Path
from ..Qt import QtWidgets, QtCore, QtGui from ..Qt import QtWidgets, QtCore, QtGui
from ..lib.codeeditor import CodeEditor from .codeeditor import EditorWidget
class QUsermodelEditor(QtWidgets.QMainWindow): class QUsermodelEditor(QtWidgets.QMainWindow):
@ -26,7 +26,7 @@ class QUsermodelEditor(QtWidgets.QMainWindow):
layout.setContentsMargins(3, 3, 3, 3) layout.setContentsMargins(3, 3, 3, 3)
layout.setSpacing(3) layout.setSpacing(3)
self.edit_field = CodeEditor(self.centralwidget) self.edit_field = EditorWidget(self.centralwidget)
font = QtGui.QFont('default') font = QtGui.QFont('default')
font.setStyleHint(font.Monospace) font.setStyleHint(font.Monospace)
font.setPointSize(10) font.setPointSize(10)
@ -50,18 +50,20 @@ class QUsermodelEditor(QtWidgets.QMainWindow):
self.menuFile.addAction('Close', self.close, QtGui.QKeySequence.Quit) self.menuFile.addAction('Close', self.close, QtGui.QKeySequence.Quit)
self.resize(800, 600) self.resize(800, 600)
self.setGeometry(QtWidgets.QStyle.alignedRect( self.setGeometry(
QtCore.Qt.LeftToRight, QtCore.Qt.AlignCenter, QtWidgets.QStyle.alignedRect(
self.size(), QtWidgets.qApp.desktop().availableGeometry() QtCore.Qt.LayoutDirection.LeftToRight,
)) QtCore.Qt.AlignmentFlag.AlignCenter,
self.size(),
QtWidgets.qApp.desktop().availableGeometry()
)
)
@property
def is_modified(self): def is_modified(self):
return self.edit_field.document().isModified() return self.edit_field.editor.document().isModified()
@is_modified.setter def set_modified(self, val: bool):
def is_modified(self, val: bool): self.edit_field.editor.document().setModified(val)
self.edit_field.document().setModified(val)
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def open_file(self): def open_file(self):
@ -75,17 +77,22 @@ class QUsermodelEditor(QtWidgets.QMainWindow):
def read_file(self, fname: str | Path): def read_file(self, fname: str | Path):
self.set_fname_opts(fname) self.set_fname_opts(fname)
if self.fname is not None:
with self.fname.open('r') as f: with self.fname.open('r') as f:
self.edit_field.setPlainText(f.read()) self.edit_field.setPlainText(f.read())
def set_fname_opts(self, fname: str | Path): def set_fname_opts(self, fname: str | Path):
fname = Path(fname)
if fname.is_file():
self.fname = Path(fname) self.fname = Path(fname)
self._dir = self.fname.parent self._dir = self.fname.parent
self.setWindowTitle('Edit ' + str(fname)) self.setWindowTitle('Edit ' + str(fname))
elif fname.is_dir():
self._dir = fname
@property
def changes_saved(self) -> bool: def changes_saved(self) -> bool:
if not self.is_modified: if not self.is_modified():
return True return True
ret = QtWidgets.QMessageBox.question(self, 'Time to think', ret = QtWidgets.QMessageBox.question(self, 'Time to think',
@ -97,9 +104,9 @@ class QUsermodelEditor(QtWidgets.QMainWindow):
self.save_file() self.save_file()
if ret == QtWidgets.QMessageBox.No: if ret == QtWidgets.QMessageBox.No:
self.is_modified = False self.set_modified(False)
return not self.is_modified return not self.is_modified()
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def save_file(self): def save_file(self):
@ -111,9 +118,9 @@ class QUsermodelEditor(QtWidgets.QMainWindow):
self.set_fname_opts(outfile) self.set_fname_opts(outfile)
self.is_modified = False self.set_modified(False)
return self.is_modified return self.is_modified()
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def overwrite_file(self): def overwrite_file(self):
@ -123,10 +130,10 @@ class QUsermodelEditor(QtWidgets.QMainWindow):
self.modelsChanged.emit() self.modelsChanged.emit()
self.is_modified = False self.set_modified(False)
def closeEvent(self, evt: QtGui.QCloseEvent): def closeEvent(self, evt: QtGui.QCloseEvent):
if not self.changes_saved: if not self.changes_saved():
evt.ignore() evt.ignore()
else: else:
super().closeEvent(evt) super().closeEvent(evt)

View File

@ -1,134 +1,11 @@
from __future__ import annotations from __future__ import annotations
from nmreval.utils.text import convert
from ..Qt import QtCore, QtWidgets, QtGui from ..Qt import QtCore, QtWidgets, QtGui
from .._py.fitmodelwidget import Ui_FitParameter
from .._py.save_fitmodel_dialog import Ui_SaveDialog from .._py.save_fitmodel_dialog import Ui_SaveDialog
from ..lib.iconloading import get_icon from ..lib.iconloading import get_icon
from ..lib.tables import TableWidget from ..lib.tables import TableWidget
class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter):
value_requested = QtCore.pyqtSignal(object)
value_changed = QtCore.pyqtSignal(str)
state_changed = QtCore.pyqtSignal()
replace_single_value = QtCore.pyqtSignal(object)
def __init__(self, label: str = 'Fitparameter', parent=None, fixed: bool = False):
super().__init__(parent)
self.setupUi(self)
self.name = label
self.parametername.setText(convert(label) + ' ')
self.parameter_line.setText('1')
self.parameter_line.setMaximumWidth(160)
self.lineEdit.setMaximumWidth(100)
self.lineEdit_2.setMaximumWidth(100)
self.label_3.setText(f'&lt; {convert(label)} &lt;')
self.checkBox.stateChanged.connect(self.enableBounds)
self.global_checkbox.stateChanged.connect(lambda: self.state_changed.emit())
self.parameter_line.editingFinished.connect(self.update_parameter)
self.parameter_line.values_requested.connect(lambda: self.value_requested.emit(self))
self.parameter_line.replace_single_values.connect(lambda: self.replace_single_value.emit(None))
self.parameter_line.editingFinished.connect(lambda: self.value_changed.emit(self.parameter_line.text()))
self.fixed_check.toggled.connect(self.set_fixed)
if fixed:
self.fixed_check.hide()
self.parameter_pos = None
self.func_idx = None
self._linetext = '1'
self.menu = QtWidgets.QMenu(self)
def set_parameter_string(self, p: str):
self.parameter_line.setText(p)
self.parameter_line.setToolTip(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: float | None, bds: tuple[float, float, bool] = None,
fixed: bool = None, glob: bool = None):
ptext = f'{p:.4g}'
self.set_parameter_string(ptext)
if bds is not None:
self.set_bounds(*bds)
if fixed is not None:
self.fixed_check.setCheckState(QtCore.Qt.Unchecked if fixed else QtCore.Qt.Checked)
if glob is not None:
self.global_checkbox.setCheckState(QtCore.Qt.Checked if glob else QtCore.Qt.Unchecked)
def get_parameter(self):
try:
p = float(self.parameter_line.text().replace(',', '.'))
except ValueError:
p = self.parameter_line.text().replace(',', '.')
if self.checkBox.isChecked():
lb_text = self.lineEdit.text()
lb = None
if lb_text:
try:
lb = float(lb_text.replace(',', '.'))
except ValueError:
lb = lb_text
ub_text = self.lineEdit_2.text()
rb = None
if ub_text:
try:
rb = float(ub_text.replace(',', '.'))
except ValueError:
rb = ub_text
else:
lb = rb = None
bounds = (lb, rb)
return p, bounds, not self.fixed_check.isChecked(), self.global_checkbox.isChecked()
@QtCore.pyqtSlot(bool)
def set_fixed(self, state: bool):
# self.global_checkbox.setVisible(not state)
self.frame.setVisible(not state)
@QtCore.pyqtSlot()
def update_parameter(self):
new_value = self.parameter_line.text()
if not new_value:
self.parameter_line.setText('1')
try:
float(new_value)
is_text = False
except ValueError:
is_text = True
self.global_checkbox.setCheckState(False)
self.set_fixed(is_text)
class QSaveModelDialog(QtWidgets.QDialog, Ui_SaveDialog): class QSaveModelDialog(QtWidgets.QDialog, Ui_SaveDialog):
def __init__(self, types=None, parent=None): def __init__(self, types=None, parent=None):
super().__init__(parent=parent) super().__init__(parent=parent)
@ -168,30 +45,37 @@ class FitModelTree(QtWidgets.QTreeWidget):
treeChanged = QtCore.pyqtSignal() treeChanged = QtCore.pyqtSignal()
itemRemoved = QtCore.pyqtSignal(int) itemRemoved = QtCore.pyqtSignal(int)
counterRole = QtCore.Qt.UserRole + 1 counterRole = QtCore.Qt.ItemDataRole.UserRole + 1
operatorRole = QtCore.Qt.UserRole + 2 operatorRole = QtCore.Qt.ItemDataRole.UserRole + 2
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent=parent) super().__init__(parent=parent)
self.setHeaderHidden(True) self.setHeaderHidden(True)
self.setDragEnabled(True) self.setDragEnabled(True)
self.setDragDropMode(QtWidgets.QTreeWidget.InternalMove) self.setDragDropMode(QtWidgets.QTreeWidget.InternalMove)
self.setDefaultDropAction(QtCore.Qt.MoveAction) self.setDefaultDropAction(QtCore.Qt.DropAction.MoveAction)
self.itemSelectionChanged.connect(lambda: self.treeChanged.emit()) self.itemSelectionChanged.connect(lambda: self.treeChanged.emit())
def keyPressEvent(self, evt): def keyPressEvent(self, evt):
operators = [QtCore.Qt.Key_Plus, QtCore.Qt.Key_Asterisk, operators = [
QtCore.Qt.Key_Minus, QtCore.Qt.Key_Slash] QtCore.Qt.Key.Key_Plus,
QtCore.Qt.Key.Key_Asterisk,
QtCore.Qt.Key.Key_Minus,
QtCore.Qt.Key.Key_Slash
]
if evt.key() == QtCore.Qt.Key_Delete: if evt.key() == QtCore.Qt.Key.Key_Delete:
for item in self.selectedItems(): for item in self.selectedItems():
self.remove_function(item) self.remove_function(item)
elif evt.key() == QtCore.Qt.Key_Space: elif evt.key() == QtCore.Qt.Key.Key_Space:
for item in self.treeWidget.selectedItems(): for item in self.selectedItems():
item.setCheckState(0, QtCore.Qt.Checked) if item.checkState( cs = item.checkState(0)
0) == QtCore.Qt.Unchecked else item.setCheckState(0, QtCore.Qt.Unchecked) if cs == QtCore.Qt.CheckState.Unchecked:
item.setCheckState(0, QtCore.Qt.CheckState.Checked)
else:
item.setCheckState(0, QtCore.Qt.CheckState.Unchecked)
elif evt.key() in operators: elif evt.key() in operators:
idx = operators.index(evt.key()) idx = operators.index(evt.key())
@ -242,7 +126,7 @@ class FitModelTree(QtWidgets.QTreeWidget):
color = QtGui.QColor(color) color = QtGui.QColor(color)
it = QtWidgets.QTreeWidgetItem() it = QtWidgets.QTreeWidgetItem()
it.setData(0, QtCore.Qt.UserRole, idx) it.setData(0, QtCore.Qt.ItemDataRole.UserRole, idx)
it.setData(0, self.counterRole, cnt) it.setData(0, self.counterRole, cnt)
it.setData(0, self.operatorRole, op) it.setData(0, self.operatorRole, op)
it.setText(0, name) it.setText(0, name)
@ -253,7 +137,7 @@ class FitModelTree(QtWidgets.QTreeWidget):
it.setForeground(0, QtGui.QBrush(color)) it.setForeground(0, QtGui.QBrush(color))
it.setIcon(0, get_icon(self.icons[op])) it.setIcon(0, get_icon(self.icons[op]))
it.setCheckState(0, QtCore.Qt.Checked if active else QtCore.Qt.Unchecked) it.setCheckState(0, QtCore.Qt.CheckState.Checked if active else QtCore.Qt.CheckState.Unchecked)
if parent is None: if parent is None:
self.addTopLevelItem(it) self.addTopLevelItem(it)
@ -273,7 +157,7 @@ class FitModelTree(QtWidgets.QTreeWidget):
def get_selected(self): def get_selected(self):
try: try:
it = self.selectedItems()[0] it = self.selectedItems()[0]
function_nr = it.data(0, QtCore.Qt.UserRole) function_nr = it.data(0, QtCore.Qt.ItemDataRole.UserRole)
idx = it.data(0, self.counterRole) idx = it.data(0, self.counterRole)
except IndexError: except IndexError:
@ -296,10 +180,10 @@ class FitModelTree(QtWidgets.QTreeWidget):
it = parent.child(i) it = parent.child(i)
child = { child = {
'idx': it.data(0, QtCore.Qt.UserRole), 'idx': it.data(0, QtCore.Qt.ItemDataRole.UserRole),
'op': it.data(0, self.operatorRole), 'op': it.data(0, self.operatorRole),
'pos': pos, 'pos': pos,
'active': (it.checkState(0) == QtCore.Qt.Checked), 'active': (it.checkState(0) == QtCore.Qt.CheckState.Checked),
'children': [] 'children': []
} }
@ -367,8 +251,8 @@ class FitTableWidget(TableWidget):
for (sid, name) in set_ids: for (sid, name) in set_ids:
item = QtWidgets.QTableWidgetItem(name) item = QtWidgets.QTableWidgetItem(name)
item.setCheckState(QtCore.Qt.Checked) item.setCheckState(QtCore.Qt.CheckState.Checked)
item.setData(QtCore.Qt.UserRole+1, sid) item.setData(QtCore.Qt.ItemDataRole.UserRole+1, sid)
row = self.rowCount() row = self.rowCount()
self.setRowCount(row+1) self.setRowCount(row+1)
self.setItem(row, 0, item) self.setItem(row, 0, item)
@ -386,15 +270,15 @@ class FitTableWidget(TableWidget):
for i in range(self.rowCount()): for i in range(self.rowCount()):
item = self.item(i, 0) item = self.item(i, 0)
if item.checkState() == QtCore.Qt.Checked: if item.checkState() == QtCore.Qt.CheckState.Checked:
mod = self.cellWidget(i, 1).currentData() mod = self.cellWidget(i, 1).currentData()
if mod is None: if mod is None:
mod = default mod = default
if include_name: if include_name:
arg = (item.data(QtCore.Qt.UserRole+1), item.text()) arg = (item.data(QtCore.Qt.ItemDataRole.UserRole+1), item.text())
else: else:
arg = item.data(QtCore.Qt.UserRole+1) arg = item.data(QtCore.Qt.ItemDataRole.UserRole+1)
if mod not in data: if mod not in data:
data[mod] = [] data[mod] = []
@ -407,8 +291,8 @@ class FitTableWidget(TableWidget):
for i in range(self.rowCount()): for i in range(self.rowCount()):
item = self.item(i, 0) item = self.item(i, 0)
if include_name: if include_name:
ret_val.append((item.data(QtCore.Qt.UserRole+1), item.text())) ret_val.append((item.data(QtCore.Qt.ItemDataRole.UserRole+1), item.text()))
else: else:
ret_val.append(item.data(QtCore.Qt.UserRole+1)) ret_val.append(item.data(QtCore.Qt.ItemDataRole.UserRole+1))
return ret_val return ret_val

View File

@ -1,25 +1,24 @@
from __future__ import annotations from __future__ import annotations
from typing import Optional
from nmreval.fit.parameter import Parameter from nmreval.fit.parameter import Parameter
from nmreval.utils.text import convert from nmreval.utils.text import convert
from ..Qt import QtWidgets, QtCore, QtGui from ..Qt import QtWidgets, QtCore, QtGui
from .._py.fitfuncwidget import Ui_FormFit from .._py.fitfuncwidget import Ui_FormFit
from .._py.fitmodelwidget import Ui_FitParameter
from ..lib.forms import SelectionWidget from ..lib.forms import SelectionWidget
from .fit_forms import FitModelWidget
class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
value_requested = QtCore.pyqtSignal(int) value_requested = QtCore.pyqtSignal(int)
def __init__(self, parent=None): def __init__(self, func_id: int, parent=None):
super().__init__(parent=parent) super().__init__(parent=parent)
self.setupUi(self) self.setupUi(self)
self.func = None self.func = None
self.func_idx = None self.func_idx = None
self.func_id = func_id
self.max_width = QtCore.QSize(0, 0) self.max_width = QtCore.QSize(0, 0)
self.global_parameter = [] self.global_parameter = []
self.data_parameter = [] self.data_parameter = []
@ -30,16 +29,15 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
self.scrollwidget2.setLayout(QtWidgets.QVBoxLayout()) self.scrollwidget2.setLayout(QtWidgets.QVBoxLayout())
def eventFilter(self, src: QtCore.QObject, evt: QtCore.QEvent): def eventFilter(self, src: QtCore.QObject, evt: QtCore.QEvent):
modifiers = QtCore.Qt.KeyboardModifier.ControlModifier | QtCore.Qt.KeyboardModifier.ShiftModifier
if isinstance(evt, QtGui.QKeyEvent): if isinstance(evt, QtGui.QKeyEvent):
if (evt.key() == QtCore.Qt.Key_Right) and \ if (evt.key() == QtCore.Qt.Key.Key_Right) and (evt.modifiers() == modifiers):
(evt.modifiers() == QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier):
self.change_single_parameter(src.value, sender=src) self.change_single_parameter(src.value, sender=src)
self.select_next_preview(1) self.select_next_preview(1)
return True return True
elif (evt.key() == QtCore.Qt.Key_Left) and \ elif (evt.key() == QtCore.Qt.Key.Key_Left) and (evt.modifiers() == modifiers):
(evt.modifiers() == QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier):
self.change_single_parameter(src.value, sender=src) self.change_single_parameter(src.value, sender=src)
self.select_next_preview(-1) self.select_next_preview(-1)
@ -65,7 +63,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
self.glob_values = [1] * len(func.params) self.glob_values = [1] * len(func.params)
for k, v in enumerate(func.params): for k, v in enumerate(func.params):
widgt = FitModelWidget(label=v, parent=self.scrollwidget) widgt = ParameterGlobalWidget(name=v, parent=self.scrollwidget)
widgt.parameter_pos = k widgt.parameter_pos = k
widgt.func_idx = idx widgt.func_idx = idx
try: try:
@ -95,7 +93,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
for w1, w2 in zip(self.global_parameter, self.data_parameter): for w1, w2 in zip(self.global_parameter, self.data_parameter):
w1.parametername.setFixedSize(self.max_width) w1.parametername.setFixedSize(self.max_width)
w1.checkBox.setFixedSize(self.max_width) w1.checkBox.setFixedSize(self.max_width)
w2.label.setFixedSize(self.max_width) w2.parametername.setFixedSize(self.max_width)
if hasattr(func, 'choices') and func.choices is not None: if hasattr(func, 'choices') and func.choices is not None:
cbox = func.choices cbox = func.choices
@ -175,7 +173,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
# disable single parameter if it is set global, enable if global is unset # disable single parameter if it is set global, enable if global is unset
widget = self.sender() widget = self.sender()
idx = self.global_parameter.index(widget) idx = self.global_parameter.index(widget)
enable = (widget.global_checkbox.checkState() == QtCore.Qt.Unchecked) enable = (widget.global_checkbox.checkState() == QtCore.Qt.CheckState.Unchecked)
self.data_parameter[idx].setEnabled(enable) self.data_parameter[idx].setEnabled(enable)
def select_next_preview(self, direction): def select_next_preview(self, direction):
@ -215,7 +213,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
param_general = [] param_general = []
for g in self.global_parameter: for g in self.global_parameter:
if isinstance(g, FitModelWidget): if isinstance(g, ParameterGlobalWidget):
p_i, bds_i, fixed_i, global_i = g.get_parameter() p_i, bds_i, fixed_i, global_i = g.get_parameter()
parameter_i = Parameter(name=g.name, value=p_i, lb=bds_i[0], ub=bds_i[1], var=fixed_i) parameter_i = Parameter(name=g.name, value=p_i, lb=bds_i[0], ub=bds_i[1], var=fixed_i)
param_general.append(parameter_i) param_general.append(parameter_i)
@ -236,7 +234,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
p = [] p = []
for i, (p_i, g) in enumerate(zip(parameter, self.global_parameter)): for i, (p_i, g) in enumerate(zip(parameter, self.global_parameter)):
if isinstance(g, FitModelWidget): if isinstance(g, ParameterGlobalWidget):
if (p_i is None) or is_global[i]: if (p_i is None) or is_global[i]:
# set has no oen value # set has no oen value
p.append(param_general[i].copy()) p.append(param_general[i].copy())
@ -303,9 +301,11 @@ class ParameterSingleWidget(QtWidgets.QWidget):
self._init_ui() self._init_ui()
self.name = name self.name = name
self.label.setText(convert(name)) self.parametername.setText(convert(name))
self.label.setToolTip('If this is bold then this parameter is only for this data. ' self.parametername.setToolTip(
'Otherwise, the general parameter is used and displayed') 'If this is bold then this parameter is only for this data. '
'Otherwise, the general parameter is used and displayed'
)
# self.value_line.setValidator(QtGui.QDoubleValidator()) # self.value_line.setValidator(QtGui.QDoubleValidator())
self.value_line.textChanged.connect(lambda: self.valueChanged.emit(self.value) if self.value is not None else 0) self.value_line.textChanged.connect(lambda: self.valueChanged.emit(self.value) if self.value is not None else 0)
@ -316,8 +316,8 @@ class ParameterSingleWidget(QtWidgets.QWidget):
layout.setContentsMargins(2, 2, 2, 2) layout.setContentsMargins(2, 2, 2, 2)
layout.setSpacing(2) layout.setSpacing(2)
self.label = QtWidgets.QLabel(self) self.parametername = QtWidgets.QLabel(self)
layout.addWidget(self.label) layout.addWidget(self.parametername)
layout.addSpacerItem(QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)) layout.addSpacerItem(QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum))
@ -343,9 +343,137 @@ class ParameterSingleWidget(QtWidgets.QWidget):
def value(self, val): def value(self, val):
# self.value_line.setText(f'{float(val):.5g}') # self.value_line.setText(f'{float(val):.5g}')
self.value_line.setText(f'{val}') self.value_line.setText(f'{val}')
self.value_line.setCursorPosition(0)
def show_as_local_parameter(self, is_local: bool): def show_as_local_parameter(self, is_local: bool):
if is_local: if is_local:
self.label.setStyleSheet('font-weight: bold;') self.parametername.setStyleSheet('font-weight: bold;')
else: else:
self.label.setStyleSheet('') self.parametername.setStyleSheet('')
class ParameterGlobalWidget(QtWidgets.QWidget, Ui_FitParameter):
"""
Widget to show a global parameter
"""
value_requested = QtCore.pyqtSignal(object)
value_changed = QtCore.pyqtSignal(str)
state_changed = QtCore.pyqtSignal()
replace_single_value = QtCore.pyqtSignal(object)
def __init__(self, name: str = 'Fitparameter', parent=None, fixed: bool = False):
super().__init__(parent)
self.setupUi(self)
self.name = name
self.reset_button.setVisible(False)
self.parametername.setText(convert(name) + ' ')
self.parameter_line.setText('1')
self.parameter_line.setMaximumWidth(160)
self.lineEdit.setMaximumWidth(100)
self.lineEdit_2.setMaximumWidth(100)
self.label_3.setText(f'&lt; {convert(name)} &lt;')
self.checkBox.stateChanged.connect(self.enableBounds)
self.global_checkbox.stateChanged.connect(lambda: self.state_changed.emit())
self.parameter_line.editingFinished.connect(self.update_parameter)
self.parameter_line.values_requested.connect(lambda: self.value_requested.emit(self))
self.parameter_line.replace_single_values.connect(lambda: self.replace_single_value.emit(None))
self.parameter_line.editingFinished.connect(lambda: self.value_changed.emit(self.parameter_line.text()))
self.fixed_check.toggled.connect(self.set_fixed)
if fixed:
self.fixed_check.hide()
self.reset_button.setVisible(False)
self.parameter_pos = None
self.func_idx = None
self._linetext = '1'
self.menu = QtWidgets.QMenu(self)
def set_parameter_string(self, p: str):
self.parameter_line.setText(p)
self.parameter_line.setToolTip(p)
def set_bounds(self, lb: float, ub: float, cbox: bool = True):
self.checkBox.setCheckState(QtCore.Qt.CheckState.Checked if cbox else QtCore.Qt.CheckState.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: float | None, bds: tuple[float, float, bool] = None,
fixed: bool = None, glob: bool = None):
ptext = f'{p:.4g}'
self.set_parameter_string(ptext)
if bds is not None:
self.set_bounds(*bds)
if fixed is not None:
self.fixed_check.setCheckState(QtCore.Qt.CheckState.Unchecked if fixed else QtCore.Qt.CheckState.Checked)
if glob is not None:
self.global_checkbox.setCheckState(QtCore.Qt.CheckState.Checked if glob else QtCore.Qt.CheckState.Unchecked)
def get_parameter(self):
try:
p = float(self.parameter_line.text().replace(',', '.'))
except ValueError:
p = self.parameter_line.text().replace(',', '.')
if self.checkBox.isChecked():
lb_text = self.lineEdit.text()
lb = None
if lb_text:
try:
lb = float(lb_text.replace(',', '.'))
except ValueError:
lb = lb_text
ub_text = self.lineEdit_2.text()
rb = None
if ub_text:
try:
rb = float(ub_text.replace(',', '.'))
except ValueError:
rb = ub_text
else:
lb = rb = None
bounds = (lb, rb)
return p, bounds, not self.fixed_check.isChecked(), self.global_checkbox.isChecked()
@QtCore.pyqtSlot(bool)
def set_fixed(self, state: bool):
# self.global_checkbox.setVisible(not state)
self.frame.setVisible(not state)
@QtCore.pyqtSlot()
def update_parameter(self):
new_value = self.parameter_line.text()
if not new_value:
self.parameter_line.setText('1')
try:
float(new_value)
is_text = False
except ValueError:
is_text = True
self.global_checkbox.setCheckState(False)
self.set_fixed(is_text or self.fixed_check.isChecked())

View File

@ -0,0 +1,99 @@
from ..Qt import QtWidgets, QtGui, QtCore
from ..lib.iconloading import get_icon
from ..lib.pg_objects import RegionItem
class FitToolbar(QtWidgets.QToolBar):
def __init__(
self,
fitaction: QtWidgets.QAction,
limit_menu: QtWidgets.QMenu,
parent=None,
):
super().__init__('Fit', parent=parent)
self.fit_action = fitaction
self.region = RegionItem()
self.addAction(fitaction)
self.fitlim_button = QtWidgets.QToolButton(self)
self.fitlim_button.setMenu(limit_menu)
self.fitlim_button.setPopupMode(self.fitlim_button.InstantPopup)
self.fitlim_button.setIcon(get_icon('fit_region'))
self.addWidget(self.fitlim_button)
self.label = QtWidgets.QLabel(self)
self.label.setText('L: ')
self.addWidget(self.label)
self.label.setEnabled(False)
self.lineedit = QtWidgets.QLineEdit(self)
self.lineedit.setValidator(QtGui.QDoubleValidator())
self.lineedit.setMaximumWidth(92)
self.addWidget(self.lineedit)
self.lineedit.setEnabled(False)
self.label2 = QtWidgets.QLabel(self)
self.label2.setText(' R: ')
self.addWidget(self.label2)
self.label2.setEnabled(False)
self.lineedit2 = QtWidgets.QLineEdit(self)
self.lineedit2.setValidator(QtGui.QDoubleValidator())
self.addWidget(self.lineedit2)
self.lineedit2.setMaximumWidth(92)
self.lineedit2.setEnabled(False)
self.limit_group = QtWidgets.QActionGroup(self)
for ac in limit_menu.actions():
self.limit_group.addAction(ac)
self.limit_group.triggered.connect(self.change_limit_type)
self.region.sigRegionChanged.connect(self.change_labels)
self.change_labels()
self.lineedit.textChanged.connect(self.move_region)
self.lineedit2.textChanged.connect(self.move_region)
@QtCore.pyqtSlot(QtWidgets.QAction)
def change_limit_type(self, action: QtWidgets.QAction):
is_custom = (action.text() in ['Custom', 'Exclude region'])
print(is_custom)
for w in [self.label, self.label2, self.lineedit, self.lineedit2]:
w.setEnabled(is_custom)
def change_labels(self):
r = self.region.getRegion()
self.lineedit.blockSignals(True)
self.lineedit.setText(f'{r[0]:.4g}')
self.lineedit.blockSignals(False)
self.lineedit2.blockSignals(True)
self.lineedit2.setText(f'{r[1]:.4g}')
self.lineedit2.blockSignals(False)
def move_region(self):
try:
r_min = float(self.lineedit.text())
except ValueError:
r_min = None
try:
r_max = float(self.lineedit2.text())
except ValueError:
r_max = None
if r_min is not None and r_max is not None:
self.region.setRegion((r_min, r_max), use_log=True)
def get_limit(self):
action_text = self.limit_group.checkedAction().text()
return {
'None': 'none',
'Visible x range': 'x',
'Custom': ('in', self.region.getRegion()),
'Exclude region': ('out', self.region.getRegion()),
}[action_text]

View File

@ -6,6 +6,7 @@ from nmreval.configs import config_paths
from nmreval import models from nmreval import models
from nmreval.lib.importer import find_models from nmreval.lib.importer import find_models
from nmreval.lib.colors import BaseColor, Tab10 from nmreval.lib.colors import BaseColor, Tab10
from nmreval.lib.logger import logger
from nmreval.utils.text import convert from nmreval.utils.text import convert
from ..lib.iconloading import get_icon from ..lib.iconloading import get_icon
@ -50,8 +51,17 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
user_defined = [] user_defined = []
try: try:
user_defined = find_models(config_paths() / 'usermodels.py') user_defined = find_models(config_paths() / 'usermodels.py')
except FileNotFoundError: except Exception as e:
pass import traceback, sys
exc_type, exc_value, exc_traceback = sys.exc_info()
_ = QtWidgets.QMessageBox.warning(
self,
'No user functions',
f'Loading user-defined function failed with exception:\n'
f'{"".join(traceback.format_exception(exc_type, exc_value, exc_traceback, limit=1))}')
logger.exception("Invalid usermodels.py", exc_info=e)
for model in user_defined: for model in user_defined:
name = model.__name__ name = model.__name__
@ -155,7 +165,7 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
self.iscomplex = False self.iscomplex = False
while iterator.value(): while iterator.value():
item = iterator.value() item = iterator.value()
f = self.functions[item.data(0, QtCore.Qt.UserRole)] f = self.functions[item.data(0, QtCore.Qt.ItemDataRole.UserRole)]
if hasattr(f, 'iscomplex') and f.iscomplex: if hasattr(f, 'iscomplex') and f.iscomplex:
self.iscomplex = True self.iscomplex = True
break break
@ -216,7 +226,7 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
iterator = QtWidgets.QTreeWidgetItemIterator(self.functree) iterator = QtWidgets.QTreeWidgetItemIterator(self.functree)
while iterator.value(): while iterator.value():
item = iterator.value() item = iterator.value()
f = self.functions[item.data(0, QtCore.Qt.UserRole)] f = self.functions[item.data(0, QtCore.Qt.ItemDataRole.UserRole)]
cnt = item.data(0, self.functree.counterRole) 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)] all_parameters[f'{f.name}_{cnt}'] = [(convert(pp, new='str'), (cnt, i)) for i, pp in enumerate(f.params)]
@ -225,7 +235,19 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
return all_parameters return all_parameters
def get_complex_state(self): def get_complex_state(self):
return self.complex_comboBox.currentIndex() if self.iscomplex else None iscomplex = False
iterator = QtWidgets.QTreeWidgetItemIterator(self.functree)
while iterator.value():
item = iterator.value()
if item.checkState(0) != QtCore.Qt.CheckState.Unchecked:
f = self.functions[item.data(0, QtCore.Qt.ItemDataRole.UserRole)]
if hasattr(f, 'iscomplex') and f.iscomplex:
iscomplex = True
break
iterator += 1
return self.complex_comboBox.currentIndex() if iscomplex else None
def set_complex_state(self, state): def set_complex_state(self, state):
if state is not None: if state is not None:

View File

@ -9,7 +9,6 @@ import numpy as np
from pyqtgraph import mkPen from pyqtgraph import mkPen
from nmreval.fit._meta import MultiModel, ModelFactory from nmreval.fit._meta import MultiModel, ModelFactory
from nmreval.fit.data import Data
from nmreval.fit.model import Model from nmreval.fit.model import Model
from nmreval.fit.parameter import Parameters from nmreval.fit.parameter import Parameters
from nmreval.fit.result import FitResult from nmreval.fit.result import FitResult
@ -42,8 +41,8 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
self._management = mgmt self._management = mgmt
self._current_model = next(QFitDialog.model_cnt) self._current_model = next(QFitDialog.model_cnt)
self.show_combobox.setItemData(0, self._current_model, QtCore.Qt.UserRole) self.show_combobox.setItemData(0, self._current_model, QtCore.Qt.ItemDataRole.UserRole)
self.default_combobox.setItemData(0, self._current_model, QtCore.Qt.UserRole) self.default_combobox.setItemData(0, self._current_model, QtCore.Qt.ItemDataRole.UserRole)
self.data_table = FitTableWidget(self.data_widget) self.data_table = FitTableWidget(self.data_widget)
self.data_widget.addWidget(self.data_table) self.data_widget.addWidget(self.data_table)
@ -78,8 +77,12 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
""" """
w = self.param_widgets[idx] w = self.param_widgets[idx]
self.stackedWidget.removeWidget(w) self.stackedWidget.removeWidget(w)
w.setParent(None)
w.deleteLater() w.deleteLater()
del self.param_widgets[idx] del self.param_widgets[idx]
_, func_id = self.functionwidget.get_selected()
self.get_functions()
self._current_function = None self._current_function = None
if len(self.param_widgets) == 0: if len(self.param_widgets) == 0:
@ -105,7 +108,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
if function is None: if function is None:
return return
dialog = QFitParameterWidget(self.stackedWidget) dialog = QFitParameterWidget(function_id, self.stackedWidget)
data_names = self.data_table.data_list(include_name=True) data_names = self.data_table.data_list(include_name=True)
dialog.set_function(function, function_idx) dialog.set_function(function, function_idx)
@ -150,9 +153,9 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
# deselect all fit sets # deselect all fit sets
for i in range(self.data_table.rowCount()): for i in range(self.data_table.rowCount()):
data_id = self.data_table.item(i, 0).data(QtCore.Qt.UserRole+1) data_id = self.data_table.item(i, 0).data(QtCore.Qt.ItemDataRole.UserRole+1)
if self._management[data_id].mode == 'fit' or self._management[data_id].has_relation(Relations.isFitPartOf): if self._management[data_id].mode == 'fit' or self._management[data_id].has_relation(Relations.isFitPartOf):
self.data_table.item(i, 0).setCheckState(QtCore.Qt.Unchecked) self.data_table.item(i, 0).setCheckState(QtCore.Qt.CheckState.Unchecked)
if self.models: if self.models:
for m in self.models.keys(): for m in self.models.keys():
@ -176,7 +179,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
self.default_combobox.addItem('Model '+idx, userData=idx) self.default_combobox.addItem('Model '+idx, userData=idx)
self.show_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.setItemData(self.show_combobox.count()-1, idx, QtCore.Qt.ItemDataRole.UserRole)
self.show_combobox.setCurrentIndex(self.show_combobox.count()-1) self.show_combobox.setCurrentIndex(self.show_combobox.count()-1)
self._current_model = idx self._current_model = idx
@ -190,7 +193,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
self.get_functions() self.get_functions()
self.functionwidget.clear() self.functionwidget.clear()
self._current_model = self.show_combobox.itemData(idx, QtCore.Qt.UserRole) self._current_model = self.show_combobox.itemData(idx, QtCore.Qt.ItemDataRole.UserRole)
if self._current_model in self.models and len(self.models[self._current_model]): if self._current_model in self.models and len(self.models[self._current_model]):
for el in self.models[self._current_model]: for el in self.models[self._current_model]:
self.functionwidget.add_function(**el) self.functionwidget.add_function(**el)
@ -207,9 +210,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
for m in self.models[model_id]: for m in self.models[model_id]:
func_id = m['cnt'] func_id = m['cnt']
self.stackedWidget.removeWidget(self.param_widgets[func_id]) self.remove_function(func_id)
self.param_widgets.pop(func_id)
self._complex.pop(model_id) self._complex.pop(model_id)
self._func_list.pop(model_id) self._func_list.pop(model_id)
@ -220,8 +221,14 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
if len(self.models) == 1: if len(self.models) == 1:
self.model_frame.hide() self.model_frame.hide()
def _prepare(self, model: list, function_use: list = None, def _prepare(
parameter: dict = None, add_idx: bool = False, cnt: int = 0) -> tuple[dict, int]: self,
model: list,
function_use: list = None,
parameter: dict = None,
add_idx: bool = False,
cnt: int = 0,
) -> tuple[dict, int]:
if parameter is None: if parameter is None:
parameter = { parameter = {
@ -264,7 +271,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
if f['children']: if f['children']:
# recurse for children # recurse for children
_, cnt = self._prepare(f['children'], parameter=parameter, add_idx=add_idx, cnt=cnt) _, cnt = self._prepare(f['children'], parameter=parameter, function_use=function_use, add_idx=add_idx, cnt=cnt)
return parameter, cnt return parameter, cnt
@ -276,17 +283,20 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
func_dict = {} func_dict = {}
for model_name, model_parameter in self.models.items(): for model_name, model_parameter in self.models.items():
func, order, param_len = ModelFactory.create_from_list(model_parameter) func, order, param_len, _ = ModelFactory.create_from_list(model_parameter)
multiple_funcs = isinstance(func, MultiModel)
if func is None: if func is None:
continue continue
func = Model(func) func = Model(func)
if model_name in data: if model_name in data:
parameter, _ = self._prepare(model_parameter, function_use=data[model_name], add_idx=isinstance(func, MultiModel)) parameter, _ = self._prepare(model_parameter, function_use=data[model_name], add_idx=multiple_funcs)
if parameter is None: if parameter is None or not isinstance(parameter, dict):
return
if ('data_parameter' not in parameter) or ('global_parameter' not in parameter):
return return
for (data_parameter, _) in parameter['data_parameter'].values(): for (data_parameter, _) in parameter['data_parameter'].values():
@ -385,7 +395,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
func_dict = {} func_dict = {}
for k, mod in self.models.items(): for k, mod in self.models.items():
func, order, param_len = ModelFactory.create_from_list(mod) func, order, param_len, _ = ModelFactory.create_from_list(mod)
multiple_funcs = isinstance(func, MultiModel) multiple_funcs = isinstance(func, MultiModel)
if k in data: if k in data:
@ -493,7 +503,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
if model_p['active']: if model_p['active']:
cnt += self.param_widgets[model_p['cnt']].set_parameter(fit_id, param[cnt:]) cnt += self.param_widgets[model_p['cnt']].set_parameter(fit_id, param[cnt:])
if model_p['children']: if model_p['children']:
cnt += self.set_parameter_iter(fit_id, param, model_p['children'], cnt=cnt) cnt = self.set_parameter_iter(fit_id, param, model_p['children'], cnt=cnt)
return cnt return cnt

View File

@ -8,9 +8,9 @@ from typing import Any
import numpy as np import numpy as np
from gui_qt.Qt import QtCore, QtWidgets, QtGui from ..Qt import QtCore, QtWidgets, QtGui
from gui_qt._py.fitcreationdialog import Ui_Dialog from .._py.fitcreationdialog import Ui_Dialog
from gui_qt.lib.namespace import QNamespaceWidget from ..editors.namespace import QNamespaceWidget
__all__ = ['QUserFitCreator'] __all__ = ['QUserFitCreator']
@ -48,13 +48,13 @@ class QUserFitCreator(QtWidgets.QDialog, Ui_Dialog):
self.update_function() self.update_function()
def __call__(self, filepath: str|pathlib.Path): def __call__(self, filepath: str | pathlib.Path):
self.filepath = pathlib.Path(filepath) self.filepath = pathlib.Path(filepath)
return self return self
def update_function(self): def update_function(self):
prev_text = self.plainTextEdit.toPlainText().split('\n') prev_text = self.editor.toPlainText().split('\n')
func_body = '' func_body = ''
in_body = False in_body = False
for line in prev_text: for line in prev_text:
@ -89,9 +89,12 @@ class QUserFitCreator(QtWidgets.QDialog, Ui_Dialog):
else: else:
k += f' def func(x):\n' k += f' def func(x):\n'
if func_body:
k += func_body k += func_body
else:
k += ' return x'
self.plainTextEdit.setPlainText(k) self.editor.setPlainText(k)
except Exception as e: except Exception as e:
QtWidgets.QMessageBox.warning(self, 'Failure', f'Error found: {e.args[0]}') QtWidgets.QMessageBox.warning(self, 'Failure', f'Error found: {e.args[0]}')
@ -215,7 +218,7 @@ class KwargsWidget(QtWidgets.QWidget):
for i in range(self.choices.count()): for i in range(self.choices.count()):
kwargs.append(self.choices.widget(i).get_strings()) kwargs.append(self.choices.widget(i).get_strings())
if kwargs: if kwargs:
return f" choices = {', '.join(kwargs)}\n" return f" choices = [{', '.join(kwargs)}]\n"
else: else:
return '' return ''
@ -475,12 +478,3 @@ class DescWidget(QtWidgets.QWidget):
f" equation = r'{self.eq_lineedit.text()}'\n" f" equation = r'{self.eq_lineedit.text()}'\n"
return stringi return stringi
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication([])
win = QUserFitCreator()
win.show()
sys.exit(app.exec())

View File

@ -1,6 +1,9 @@
from __future__ import annotations
from math import isnan from math import isnan
from pyqtgraph import mkBrush, mkPen from pyqtgraph import mkBrush, mkPen, mkColor
from numpy import abs as np_abs, isfinite as np_isfinite
from nmreval.utils.text import convert from nmreval.utils.text import convert
from ..lib.graph_items import logTickValues from ..lib.graph_items import logTickValues
@ -15,7 +18,7 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
closed = QtCore.pyqtSignal(dict, list, str, bool, bool, list) closed = QtCore.pyqtSignal(dict, list, str, bool, bool, list)
redoFit = QtCore.pyqtSignal(dict) redoFit = QtCore.pyqtSignal(dict)
def __init__(self, results: list, management, parent=None): def __init__(self, results: list, sub_colors: dict, management, parent=None):
super().__init__(parent=parent) super().__init__(parent=parent)
self.setupUi(self) self.setupUi(self)
@ -27,12 +30,14 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
self.extrapolate_box.stateChanged.connect(lambda x: self.maxx_line.setEnabled(x)) self.extrapolate_box.stateChanged.connect(lambda x: self.maxx_line.setEnabled(x))
self.extrapolate_box.stateChanged.connect(lambda x: self.minx_line.setEnabled(x)) self.extrapolate_box.stateChanged.connect(lambda x: self.minx_line.setEnabled(x))
self.extrapolate_box.stateChanged.connect(lambda x: self.numx_line.setEnabled(x)) self.extrapolate_box.stateChanged.connect(lambda x: self.numx_line.setEnabled(x))
self.extrapolate_box.stateChanged.connect(lambda x: self.newx_log_checkbox.setEnabled(x))
self._previous_fits = {} self._previous_fits = {}
self._opts = [] self._opts = []
self._results = {} self._results = {}
self.graph_opts = {} self.graph_opts = {}
self.last_idx = None self.last_idx = None
self.func_colors = sub_colors
self.fit_plot = self.graphicsView.addPlot(row=1, col=0, title='Fit') self.fit_plot = self.graphicsView.addPlot(row=1, col=0, title='Fit')
self.resid_plot = self.graphicsView.addPlot(row=0, col=0, title='Residual') self.resid_plot = self.graphicsView.addPlot(row=0, col=0, title='Residual')
@ -44,21 +49,29 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
self.graphicsView.ci.layout.setRowStretchFactor(0, 1) self.graphicsView.ci.layout.setRowStretchFactor(0, 1)
self.graphicsView.ci.layout.setRowStretchFactor(1, 2) self.graphicsView.ci.layout.setRowStretchFactor(1, 2)
self.resid_graph = PlotItem(x=[], y=[], self.resid_graph = PlotItem(
x=[], y=[],
symbol='o', symbolPen=None, symbolBrush=mkBrush(color=(31, 119, 180)), symbol='o', symbolPen=None, symbolBrush=mkBrush(color=(31, 119, 180)),
pen=None) pen=None
self.resid_graph_imag = PlotItem(x=[], y=[], )
self.resid_graph_imag = PlotItem(
x=[], y=[],
symbol='s', symbolPen=None, symbolBrush=mkBrush(color=(255, 127, 14)), symbol='s', symbolPen=None, symbolBrush=mkBrush(color=(255, 127, 14)),
pen=None) pen=None
)
self.resid_plot.addItem(self.resid_graph) self.resid_plot.addItem(self.resid_graph)
self.resid_plot.addItem(self.resid_graph_imag) self.resid_plot.addItem(self.resid_graph_imag)
self.data_graph = PlotItem(x=[], y=[], self.data_graph = PlotItem(
x=[], y=[],
symbol='o', symbolPen=None, symbolBrush=mkBrush(color=(31, 119, 180)), symbol='o', symbolPen=None, symbolBrush=mkBrush(color=(31, 119, 180)),
pen=None) pen=None
self.data_graph_imag = PlotItem(x=[], y=[], )
self.data_graph_imag = PlotItem(
x=[], y=[],
symbol='s', symbolPen=None, symbolBrush=mkBrush(color=(255, 127, 14)), symbol='s', symbolPen=None, symbolBrush=mkBrush(color=(255, 127, 14)),
pen=None) pen=None
)
self.fit_plot.addItem(self.data_graph) self.fit_plot.addItem(self.data_graph)
self.fit_plot.addItem(self.data_graph_imag) self.fit_plot.addItem(self.data_graph_imag)
@ -69,21 +82,26 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
self.cmap = RdBuCMap(vmin=-1, vmax=1) self.cmap = RdBuCMap(vmin=-1, vmax=1)
self.graph_checkBox.stateChanged.connect(lambda x: self.graph_comboBox.setEnabled(x == QtCore.Qt.Unchecked)) self.graph_checkBox.stateChanged.connect(
lambda x: self.graph_comboBox.setEnabled(x == QtCore.Qt.CheckState.Unchecked)
)
self.logy_box.stateChanged.connect(lambda x: self.fit_plot.setLogMode(y=bool(x))) self.logy_box.stateChanged.connect(lambda x: self.fit_plot.setLogMode(y=bool(x)))
self.logx_box.stateChanged.connect(lambda x: self.fit_plot.setLogMode(x=bool(x))) self.logx_box.stateChanged.connect(lambda x: self.fit_plot.setLogMode(x=bool(x)))
self.resid_plot.setXLink(self.fit_plot) self.resid_plot.setXLink(self.fit_plot)
self.buttonGroup.buttonToggled.connect(self._plot_residuals)
self.set_results(results) self.set_results(results)
def __call__(self, results: list): def __call__(self, results: list, sub_colors: dict):
self._previous_fits = {} self._previous_fits = {}
self.sets_comboBox.blockSignals(True) self.sets_comboBox.blockSignals(True)
self.sets_comboBox.clear() self.sets_comboBox.clear()
self.sets_comboBox.blockSignals(False) self.sets_comboBox.blockSignals(False)
self._results = {} self._results = {}
self._opts = {} self._opts = {}
self.func_colors = sub_colors
self.set_results(results) self.set_results(results)
@ -112,7 +130,7 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
@QtCore.pyqtSlot(int, name='on_sets_comboBox_currentIndexChanged') @QtCore.pyqtSlot(int, name='on_sets_comboBox_currentIndexChanged')
def set_parameter(self, idx: int): def set_parameter(self, idx: int):
set_id = self.sets_comboBox.itemData(idx, QtCore.Qt.UserRole) set_id = self.sets_comboBox.itemData(idx, QtCore.Qt.ItemDataRole.UserRole)
res = self._results[set_id] res = self._results[set_id]
self.param_tableWidget.setRowCount(len(res.parameter)) self.param_tableWidget.setRowCount(len(res.parameter))
@ -138,11 +156,11 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
def change_opts(self, _): def change_opts(self, _):
idx = self.sets_comboBox.currentIndex() idx = self.sets_comboBox.currentIndex()
self._opts[idx] = (self.reject_fit_checkBox.checkState() == QtCore.Qt.Checked, self._opts[idx] = (self.reject_fit_checkBox.checkState() == QtCore.Qt.CheckState.Checked,
self.del_prev_checkBox.checkState() == QtCore.Qt.Checked) self.del_prev_checkBox.checkState() == QtCore.Qt.CheckState.Checked)
def show_results(self, idx): def show_results(self, idx):
set_id = self.sets_comboBox.itemData(idx, QtCore.Qt.UserRole) set_id = self.sets_comboBox.itemData(idx, QtCore.Qt.ItemDataRole.UserRole)
self.set_plot(set_id) self.set_plot(set_id)
self.set_correlation(set_id) self.set_correlation(set_id)
self.set_statistics(set_id) self.set_statistics(set_id)
@ -153,6 +171,15 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
self.del_prev_checkBox.setChecked(self._opts[idx][1]) self.del_prev_checkBox.setChecked(self._opts[idx][1])
self.del_prev_checkBox.blockSignals(False) self.del_prev_checkBox.blockSignals(False)
@QtCore.pyqtSlot(name='on_autoscale_box_clicked')
def reset_fit_ranges(self):
for i in range(self.sets_comboBox.count()):
graph_id = self.sets_comboBox.itemData(i)
if graph_id in self.graph_opts:
self.graph_opts.pop(graph_id)
self.fit_plot.enableAutoRange()
def set_plot(self, idx: str): def set_plot(self, idx: str):
if self.last_idx is not None: if self.last_idx is not None:
self.graph_opts[self.last_idx] = ( self.graph_opts[self.last_idx] = (
@ -175,27 +202,26 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
self.data_graph_imag.setData(x=res.x_data, y=res.y_data.imag) self.data_graph_imag.setData(x=res.x_data, y=res.y_data.imag)
self.fit_graph.setData(x=res.x, y=res.y.real) self.fit_graph.setData(x=res.x, y=res.y.real)
self.fit_graph_imag.setData(x=res.x, y=res.y.imag) self.fit_graph_imag.setData(x=res.x, y=res.y.imag)
self.resid_graph.setData(x=res.x_data, y=res.residual.real)
self.resid_graph_imag.setData(x=res.x_data, y=res.residual.imag)
for i, f in enumerate(sub_funcs): for f, c in zip(sub_funcs, self.func_colors[idx]):
item = PlotItem(x=f.x, y=f.y.real, pen=mkPen({'color': i, 'style': 2})) col = mkColor(*[c_i*255 for c_i in c])
item = PlotItem(x=f.x, y=f.y.real, pen=mkPen({'color': col, 'style': 2}))
self.fit_plot.addItem(item) self.fit_plot.addItem(item)
item = PlotItem(x=f.x, y=f.y.imag, pen=mkPen({'color': i, 'style': 2})) item = PlotItem(x=f.x, y=f.y.imag, pen=mkPen({'color': col, 'style': 2}))
self.fit_plot.addItem(item) self.fit_plot.addItem(item)
else: else:
self.resid_graph.setData(x=res.x_data, y=res.residual)
self.resid_graph_imag.setData(x=[], y=[])
self.data_graph.setData(x=res.x_data, y=res.y_data) self.data_graph.setData(x=res.x_data, y=res.y_data)
self.data_graph_imag.setData(x=[], y=[]) self.data_graph_imag.setData(x=[], y=[])
self.fit_graph.setData(x=res.x, y=res.y) self.fit_graph.setData(x=res.x, y=res.y)
self.fit_graph_imag.setData(x=[], y=[]) self.fit_graph_imag.setData(x=[], y=[])
for i, f in enumerate(sub_funcs): for f, c in zip(sub_funcs, self.func_colors[idx]):
item = PlotItem(x=f.x, y=f.y, pen=mkPen({'color': i, 'style': 2})) item = PlotItem(x=f.x, y=f.y, pen=mkPen({'color': mkColor(*[c_i*255 for c_i in c]), 'style': 2}))
self.fit_plot.addItem(item) self.fit_plot.addItem(item)
self._plot_residuals(idx)
self.logx_box.blockSignals(True) self.logx_box.blockSignals(True)
self.logx_box.setChecked(res.islog) self.logx_box.setChecked(res.islog)
self.logx_box.blockSignals(False) self.logx_box.blockSignals(False)
@ -213,6 +239,29 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
self.logy_box.blockSignals(True) self.logy_box.blockSignals(True)
self.logy_box.setChecked(logy) self.logy_box.setChecked(logy)
self.logy_box.blockSignals(False) self.logy_box.blockSignals(False)
else:
self.fit_plot.enableAutoRange()
def _plot_residuals(self, idx: str = None):
if idx is None or isinstance(idx, QtWidgets.QAbstractButton):
idx = self.sets_comboBox.currentData(QtCore.Qt.ItemDataRole.UserRole)
res = self._results[idx]
if res.iscomplex:
if self.rel_dev_button.isChecked():
self.resid_graph.setData(x=res.x_data, y=res.residual.real/np_abs(res.y_data.real))
if all(np_isfinite(res.y_data.imag)):
self.resid_graph_imag.setData(x=res.x_data, y=res.residual.imag/np_abs(res.y_data.imag))
else:
self.resid_graph.setData(x=res.x_data, y=res.residual.real)
self.resid_graph_imag.setData(x=res.x_data, y=res.residual.imag)
else:
if self.rel_dev_button.isChecked():
self.resid_graph.setData(x=res.x_data, y=res.residual / np_abs(res.y_data))
else:
self.resid_graph.setData(x=res.x_data, y=res.residual)
self.resid_graph_imag.setData(x=[], y=[])
def set_correlation(self, idx: str): def set_correlation(self, idx: str):
while self.corr_tableWidget.rowCount(): while self.corr_tableWidget.rowCount():
@ -247,20 +296,20 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
self.stats_tableWidget.setRowCount(len(res.statistics)+3) self.stats_tableWidget.setRowCount(len(res.statistics)+3)
it = QtWidgets.QTableWidgetItem(f'{res.dof}') it = QtWidgets.QTableWidgetItem(f'{res.dof}')
it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable) it.setFlags(it.flags() ^ QtCore.Qt.ItemFlag.ItemIsEditable)
self.stats_tableWidget.setVerticalHeaderItem(0, QtWidgets.QTableWidgetItem('DoF')) self.stats_tableWidget.setVerticalHeaderItem(0, QtWidgets.QTableWidgetItem('DoF'))
self.stats_tableWidget.setItem(0, 0, it) self.stats_tableWidget.setItem(0, 0, it)
for col, (name, _, dof) in enumerate(self._previous_fits[idx], start=1): for col, (name, _, dof) in enumerate(self._previous_fits[idx], start=1):
self.stats_tableWidget.setHorizontalHeaderItem(0, QtWidgets.QTableWidgetItem(name)) self.stats_tableWidget.setHorizontalHeaderItem(0, QtWidgets.QTableWidgetItem(name))
it = QtWidgets.QTableWidgetItem(f'{dof}') it = QtWidgets.QTableWidgetItem(f'{dof}')
it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable) it.setFlags(it.flags() ^ QtCore.Qt.ItemFlag.ItemIsEditable)
self.stats_tableWidget.setItem(0, col, it) self.stats_tableWidget.setItem(0, col, it)
for row, (k, v) in enumerate(res.statistics.items(), start=1): for row, (k, v) in enumerate(res.statistics.items(), start=1):
self.stats_tableWidget.setVerticalHeaderItem(row, QtWidgets.QTableWidgetItem(k)) self.stats_tableWidget.setVerticalHeaderItem(row, QtWidgets.QTableWidgetItem(k))
it = QtWidgets.QTableWidgetItem(f'{v:.4f}') it = QtWidgets.QTableWidgetItem(f'{v:.4f}')
it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable) it.setFlags(it.flags() ^ QtCore.Qt.ItemFlag.ItemIsEditable)
self.stats_tableWidget.setItem(row, 0, it) self.stats_tableWidget.setItem(row, 0, it)
best_idx = -1 best_idx = -1
@ -271,7 +320,7 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
else: else:
best_idx = col if best_val > stats[k] else max(0, best_idx) best_idx = col if best_val > stats[k] else max(0, best_idx)
it = QtWidgets.QTableWidgetItem(f'{stats[k]:.4f}') it = QtWidgets.QTableWidgetItem(f'{stats[k]:.4f}')
it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable) it.setFlags(it.flags() ^ QtCore.Qt.ItemFlag.ItemIsEditable)
self.stats_tableWidget.setItem(row, col, it) self.stats_tableWidget.setItem(row, col, it)
if best_idx > -1: if best_idx > -1:
@ -288,11 +337,11 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
for col, (_, stats, dof) in enumerate(self._previous_fits[idx], start=1): for col, (_, stats, dof) in enumerate(self._previous_fits[idx], start=1):
f_value, prob_f = res.f_test(stats['chi^2'], dof) f_value, prob_f = res.f_test(stats['chi^2'], dof)
it = QtWidgets.QTableWidgetItem(f'{f_value:.4g}') it = QtWidgets.QTableWidgetItem(f'{f_value:.4g}')
it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable) it.setFlags(it.flags() ^ QtCore.Qt.ItemFlag.ItemIsEditable)
self.corr_tableWidget.setItem(row, col, it) self.corr_tableWidget.setItem(row, col, it)
it = QtWidgets.QTableWidgetItem(f'{prob_f:.4g}') it = QtWidgets.QTableWidgetItem(f'{prob_f:.4g}')
it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable) it.setFlags(it.flags() ^ QtCore.Qt.ItemFlag.ItemIsEditable)
if prob_f < 0.05: if prob_f < 0.05:
it.setBackground(QtGui.QColor('green')) it.setBackground(QtGui.QColor('green'))
it.setForeground(QtGui.QColor('white')) it.setForeground(QtGui.QColor('white'))
@ -308,16 +357,16 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
elif button_type == self.buttonBox.Ok: elif button_type == self.buttonBox.Ok:
graph = '-1' graph = '-1'
if self.parameter_checkbox.isChecked(): if self.parameter_checkbox.isChecked():
if self.graph_checkBox.checkState() == QtCore.Qt.Checked: if self.graph_checkBox.checkState() == QtCore.Qt.CheckState.Checked:
graph = '' graph = ''
else: else:
graph = self.graph_comboBox.currentData() graph = self.graph_comboBox.currentData()
plot_fits = self.curve_checkbox.isChecked() plot_fits = self.curve_checkbox.isChecked()
parts = self.partial_checkBox.checkState() == QtCore.Qt.Checked parts = self.partial_checkBox.checkState() == QtCore.Qt.CheckState.Checked
extrapolate = [None, None, None] extrapolate = [None, None, None, None]
error = [] error = []
if self.extrapolate_box.isChecked(): if self.extrapolate_box.isChecked():
try: try:
@ -333,6 +382,8 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
except (TypeError, ValueError): except (TypeError, ValueError):
error.append('Number of points is missing') error.append('Number of points is missing')
extrapolate[3] = self.newx_log_checkbox.isChecked()
if error: if error:
msg = QtWidgets.QMessageBox.warning(self, 'Error', 'Extrapolation failed because:\n' + '\n'.join(error)) msg = QtWidgets.QMessageBox.warning(self, 'Error', 'Extrapolation failed because:\n' + '\n'.join(error))
return return
@ -370,10 +421,13 @@ class FitExtension(QtWidgets.QDialog):
self.num_pts.setValidator(QtGui.QIntValidator()) self.num_pts.setValidator(QtGui.QIntValidator())
gridLayout.addWidget(self.num_pts, 2, 1, 1, 1) gridLayout.addWidget(self.num_pts, 2, 1, 1, 1)
self.logx_checkbox = QtWidgets.QCheckBox('Log-spaced?')
gridLayout.addWidget(self.logx_checkbox, 3, 0, 1, 2)
self.buttonBox = QtWidgets.QDialogButtonBox() self.buttonBox = QtWidgets.QDialogButtonBox()
self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel | QtWidgets.QDialogButtonBox.Ok) self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel | QtWidgets.QDialogButtonBox.Ok)
gridLayout.addWidget(self.buttonBox, 3, 0, 1, 2) gridLayout.addWidget(self.buttonBox, 4, 0, 1, 2)
self.setLayout(gridLayout) self.setLayout(gridLayout)
@ -381,12 +435,13 @@ class FitExtension(QtWidgets.QDialog):
self.buttonBox.rejected.connect(self.reject) self.buttonBox.rejected.connect(self.reject)
@property @property
def values(self): def values(self) -> tuple[float, float, int, bool] | None:
try: try:
xmin = float(self.min_line.text()) xmin = float(self.min_line.text())
xmax = float(self.max_line.text()) xmax = float(self.max_line.text())
nums = int(self.num_pts.text()) nums = int(self.num_pts.text())
logx = self.logx_checkbox.isChecked()
except TypeError: except TypeError:
return None return None
return xmin, xmax, nums return xmin, xmax, nums, logx

View File

@ -27,6 +27,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
mouseDoubleClicked = QtCore.pyqtSignal() mouseDoubleClicked = QtCore.pyqtSignal()
positionClicked = QtCore.pyqtSignal(tuple, bool) positionClicked = QtCore.pyqtSignal(tuple, bool)
aboutToClose = QtCore.pyqtSignal(list) aboutToClose = QtCore.pyqtSignal(list)
newData = QtCore.pyqtSignal(list, str)
counter = itertools.count() counter = itertools.count()
@ -55,6 +56,16 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
self._external_items = [] self._external_items = []
self.closable = True self.closable = True
"""
TODO
this does not work properly and leads to lots of errors
because PlotDataItems do not have a viewBox anymore which is called in getData
# desperate attempts to improve memory usage during paintEvents
self.graphic.setAntialiasing(False)
self.plotItem.setDownsampling(auto=True)
self.plotItem.setClipToView(True)
"""
self._block = False self._block = False
self.log = [False, False] self.log = [False, False]
@ -62,7 +73,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
self.scene = self.plotItem.scene() self.scene = self.plotItem.scene()
self.scene.sigMouseMoved.connect(self.move_mouse) self.scene.sigMouseMoved.connect(self.move_mouse)
self.checkBox.stateChanged.connect(lambda x: self.legend.setVisible(x == QtCore.Qt.Checked)) self.checkBox.stateChanged.connect(lambda x: self.legend.setVisible(x == QtCore.Qt.CheckState.Checked))
self.label_button.toggled.connect(lambda x: self.label_widget.setVisible(x)) 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.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.gridbutton.toggled.connect(lambda x: self.graphic.showGrid(x=x, y=x))
@ -77,6 +88,9 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
self.bwbutton.toggled.connect(self.change_background) self.bwbutton.toggled.connect(self.change_background)
self.setAcceptDrops(True)
self.graphic.installEventFilter(self)
def _init_gui(self): def _init_gui(self):
self.setWindowTitle('Graph ' + str(next(QGraphWindow.counter))) self.setWindowTitle('Graph ' + str(next(QGraphWindow.counter)))
@ -109,6 +123,34 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
for lineedit in [self.xmin_lineedit, self.xmax_lineedit, self.ymin_lineedit, self.ymax_lineedit]: for lineedit in [self.xmin_lineedit, self.xmax_lineedit, self.ymin_lineedit, self.ymax_lineedit]:
lineedit.setValidator(QtGui.QDoubleValidator()) lineedit.setValidator(QtGui.QDoubleValidator())
def eventFilter(self, obj: QtCore.QObject, evt: QtCore.QEvent):
"""
Catch drag and Drop to prevent anything inside self.graphic to accept the events.
Without event filter, we cannot process it here and start file reading
"""
if evt.type() == QtCore.QEvent.Type.DragEnter:
evt.accept()
return True
elif evt.type() == QtCore.QEvent.Type.Drop:
return self._handle_drop(evt)
else:
return False
def dropEvent(self, evt: QtGui.QDropEvent):
return self._handle_drop(evt)
def _handle_drop(self, evt: QtGui.QDropEvent):
if evt.mimeData().hasUrls():
files = [str(url.toLocalFile()) for url in evt.mimeData().urls()]
self.newData.emit(files, self.id)
evt.accept()
return True
return False
def __contains__(self, item: str): def __contains__(self, item: str):
return item in self.sets return item in self.sets
@ -167,7 +209,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
self._block = state self._block = state
if not self._block: if not self._block:
self.graphic.enableAutoRange() # self.graphic.enableAutoRange()
self._update_zorder() self._update_zorder()
self.show_legend() self.show_legend()
else: else:
@ -178,6 +220,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
name = [name] name = [name]
plots = [plots] plots = [plots]
toplevel = len(self.sets) toplevel = len(self.sets)
self.listWidget.blockSignals(True) self.listWidget.blockSignals(True)
for (real_plot, imag_plot, err_plot), n in zip(plots, name): for (real_plot, imag_plot, err_plot), n in zip(plots, name):
self.sets.append(n) self.sets.append(n)
@ -194,9 +237,13 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
self.error_plots[n] = err_plot self.error_plots[n] = err_plot
list_item = QtWidgets.QListWidgetItem(real_plot.opts.get('name', '')) list_item = QtWidgets.QListWidgetItem(real_plot.opts.get('name', ''))
list_item.setData(QtCore.Qt.UserRole, n) list_item.setData(QtCore.Qt.ItemDataRole.UserRole, n)
list_item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable) list_item.setFlags(
list_item.setCheckState(QtCore.Qt.Checked) QtCore.Qt.ItemFlag.ItemIsEnabled |
QtCore.Qt.ItemFlag.ItemIsSelectable |
QtCore.Qt.ItemFlag.ItemIsUserCheckable
)
list_item.setCheckState(QtCore.Qt.CheckState.Checked)
self.listWidget.addItem(list_item) self.listWidget.addItem(list_item)
toplevel += 1 toplevel += 1
@ -228,7 +275,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
for i in range(self.listWidget.count()-1, 0, -1): for i in range(self.listWidget.count()-1, 0, -1):
item = self.listWidget.item(i) item = self.listWidget.item(i)
if item.data(QtCore.Qt.UserRole) in name: if item.data(QtCore.Qt.ItemDataRole.UserRole) in name:
self.listWidget.takeItem(i) self.listWidget.takeItem(i)
self.listWidget.blockSignals(False) self.listWidget.blockSignals(False)
@ -293,6 +340,8 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
if item in self.graphic.items(): if item in self.graphic.items():
self.graphic.removeItem(item) self.graphic.removeItem(item)
self.show_legend()
@QtCore.pyqtSlot(bool, name='on_imag_button_toggled') @QtCore.pyqtSlot(bool, name='on_imag_button_toggled')
@QtCore.pyqtSlot(bool, name='on_real_button_toggled') @QtCore.pyqtSlot(bool, name='on_real_button_toggled')
def set_imag_visible(self, visible: bool): def set_imag_visible(self, visible: bool):
@ -420,24 +469,37 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
_y = pos.y() _y = pos.y()
self.mousePositionChanged.emit(_x, _y) self.mousePositionChanged.emit(_x, _y)
@QtCore.pyqtSlot(name='on_title_lineedit_returnPressed') @QtCore.pyqtSlot(str, name='on_title_lineedit_textChanged')
@QtCore.pyqtSlot(name='on_xaxis_linedit_returnPressed') @QtCore.pyqtSlot(str, name='on_xaxis_linedit_textChanged')
@QtCore.pyqtSlot(name='on_yaxis_linedit_returnPressed') @QtCore.pyqtSlot(str, name='on_yaxis_linedit_textChanged')
def labels_changed(self): def labels_changed(self, text: str):
label = {self.title_lineedit: 'title', self.xaxis_linedit: 'x', self.yaxis_linedit: 'y'}[self.sender()] label = {
self.set_label(**{label: self.sender().text()}) self.title_lineedit: 'title',
self.xaxis_linedit: 'x',
self.yaxis_linedit: 'y',
}[self.sender()]
self.set_label(**{label: text})
def set_label(self, x=None, y=None, title=None): def set_label(self, x: str = None, y: str = None, title: str = None):
if title is not None: if title is not None:
self.plotItem.setTitle(convert(title, old='tex', new='html'), **{'size': '10pt', 'color': self._fgcolor}) self.plotItem.setTitle(
convert(title, old='tex', new='html'),
**{'size': '10pt', 'color': self._fgcolor},
)
if x is not None: if x is not None:
self.plotItem.setLabel('bottom', convert(x, old='tex', new='html'), self.plotItem.setLabel(
**{'font-size': '10pt', 'color': self._fgcolor.name()}) 'bottom',
convert(x, old='tex', new='html'),
**{'font-size': '10pt', 'color': self._fgcolor.name()},
)
if y is not None: if y is not None:
self.plotItem.setLabel('left', convert(y, old='tex', new='html'), self.plotItem.setLabel(
**{'font-size': '10pt', 'color': self._fgcolor.name()}) 'left',
convert(y, old='tex', new='html'),
**{'font-size': '10pt', 'color': self._fgcolor.name()},
)
def set_logmode(self, xmode: bool = None, ymode: bool = None): def set_logmode(self, xmode: bool = None, ymode: bool = None):
r = self.ranges r = self.ranges
@ -463,8 +525,6 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
self.plotItem.updateLogMode() self.plotItem.updateLogMode()
self.set_range(x=r[0], y=r[1]) self.set_range(x=r[0], y=r[1])
self.plotItem.enableAutoRange()
def enable_picking(self, enabled: bool): def enable_picking(self, enabled: bool):
if enabled: if enabled:
self.scene.sigMouseClicked.connect(self.position_picked) self.scene.sigMouseClicked.connect(self.position_picked)
@ -479,6 +539,10 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
if self.graphic.plotItem.sceneBoundingRect().contains(evt.scenePos()) and evt.button() == 1: if self.graphic.plotItem.sceneBoundingRect().contains(evt.scenePos()) and evt.button() == 1:
pos = vb.mapSceneToView(evt.scenePos()) pos = vb.mapSceneToView(evt.scenePos())
if not _inside_range(pos.x(), pos.y(), vb.viewRange()):
return
_x, _y = pos.x(), pos.y() _x, _y = pos.x(), pos.y()
if self.log[0]: if self.log[0]:
@ -545,7 +609,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
for i in range(self.listWidget.count()): for i in range(self.listWidget.count()):
item = self.listWidget.item(i) item = self.listWidget.item(i)
if item.data(QtCore.Qt.UserRole) == sid: if item.data(QtCore.Qt.ItemDataRole.UserRole) == sid:
item.setText(convert(name, old='tex', new='html')) item.setText(convert(name, old='tex', new='html'))
self.listWidget.blockSignals(False) self.listWidget.blockSignals(False)
@ -610,11 +674,14 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
else: else:
if os.path.exists(outfile): if os.path.exists(outfile):
if QtWidgets.QMessageBox.warning(self, 'Export graphic', if QtWidgets.QMessageBox.warning(
self,
'Export graphic',
f'{os.path.split(outfile)[1]} already exists.\n' f'{os.path.split(outfile)[1]} already exists.\n'
f'Do you REALLY want to replace it?', f'Do you REALLY want to replace it?',
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
QtWidgets.QMessageBox.No) == QtWidgets.QMessageBox.No: QtWidgets.QMessageBox.No
) == QtWidgets.QMessageBox.No:
return return
bg_color = self._bgcolor bg_color = self._bgcolor
@ -656,16 +723,20 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
logger.exception(f'{item} could not exported because {e.args}') logger.exception(f'{item} could not exported because {e.args}')
continue continue
if item_dic:
if len(item) == 2: if len(item) == 2:
# plot can show errorbars # plot can show errorbars
if len(item_dic['x']):
item_dic['yerr'] = item[1].opts['topData'] item_dic['yerr'] = item[1].opts['topData']
else:
if item_dic: item_dic['yerr'] = []
dic['items'].append(item_dic) dic['items'].append(item_dic)
for item in self._external_items: for item in self._external_items:
try: try:
dic['items'].append(item.get_data_opts()) item_dic = item.get_data_opts()
if item_dic:
dic['items'].append(item_dic)
except Exception as e: except Exception as e:
logger.exception(f'{item} could not be exported because {e.args}') logger.exception(f'{item} could not be exported because {e.args}')
continue continue
@ -691,6 +762,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
'plots': (self.real_button.isChecked(), self.imag_button.isChecked(), self.error_button.isChecked()), 'plots': (self.real_button.isChecked(), self.imag_button.isChecked(), self.error_button.isChecked()),
'children': self.sets, 'children': self.sets,
'active': self._active, 'active': self._active,
'invert': (self.plotItem.vb.state['xInverted'], self.plotItem.vb.state['yInverted']),
} }
in_legend = [] in_legend = []
@ -737,7 +809,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
graph.graphic.showGrid(x=state['grid'], y=state['grid']) graph.graphic.showGrid(x=state['grid'], y=state['grid'])
graph.checkBox.setCheckState(QtCore.Qt.Checked if state['legend'] else QtCore.Qt.Unchecked) graph.checkBox.setCheckState(QtCore.Qt.CheckState.Checked if state['legend'] else QtCore.Qt.CheckState.Unchecked)
graph.real_button.setChecked(state['plots'][0]) graph.real_button.setChecked(state['plots'][0])
graph.imag_button.setChecked(state['plots'][1]) graph.imag_button.setChecked(state['plots'][1])
@ -786,3 +858,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
self.set_color(foreground=self._prev_colors[0], background=self._prev_colors[1]) self.set_color(foreground=self._prev_colors[0], background=self._prev_colors[1])
self._prev_colors = temp self._prev_colors = temp
def _inside_range(x: float, y: float, ranges: list[list[float]]) -> bool:
x_range, y_range = ranges
return (x_range[0] <= x <= x_range[1]) and (y_range[0] <= y <= y_range[1])

View File

@ -3,7 +3,7 @@ from __future__ import annotations
import re import re
from nmreval.io.asciireader import AsciiReader from nmreval.io.asciireader import AsciiReader
from nmreval.utils import NUMBER_RE from nmreval.utils import NUMBER_RE, numbers_from_string
from ..Qt import QtGui, QtCore, QtWidgets from ..Qt import QtGui, QtCore, QtWidgets
from .._py.asciidialog import Ui_ascii_reader from .._py.asciidialog import Ui_ascii_reader
@ -38,7 +38,7 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
self.changestaggeredrange(0) self.changestaggeredrange(0)
self.ascii_table.contextMenuEvent = self.ctx_table self.ascii_table.contextMenuEvent = self.ctx_table
self.ascii_table.horizontalHeader().setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.ascii_table.horizontalHeader().setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.CustomContextMenu)
self.ascii_table.horizontalHeader().customContextMenuRequested.connect(self.ctx_table) self.ascii_table.horizontalHeader().customContextMenuRequested.connect(self.ctx_table)
self.skip = False self.skip = False
@ -65,7 +65,7 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
self.set_column_names(1) self.set_column_names(1)
self.skippy_checkbox.blockSignals(True) self.skippy_checkbox.blockSignals(True)
self.skippy_checkbox.setCheckState(QtCore.Qt.Unchecked) self.skippy_checkbox.setCheckState(QtCore.Qt.CheckState.Unchecked)
self.skippy_checkbox.blockSignals(False) self.skippy_checkbox.blockSignals(False)
return self return self
@ -132,7 +132,10 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
self.ascii_table.setHorizontalHeaderLabels(map(str, range(1, self.ascii_table.columnCount() + 1))) self.ascii_table.setHorizontalHeaderLabels(map(str, range(1, self.ascii_table.columnCount() + 1)))
if self.column_checkBox.isChecked() and self.line_spinBox.isEnabled(): if self.column_checkBox.isChecked() and self.line_spinBox.isEnabled():
header_line = self.reader.header[self.line_spinBox.value()-1] header_line = self.reader.header[self.line_spinBox.value()-1]
self.ascii_table.setHorizontalHeaderLabels(header_line.split()) header_line = header_line.strip('\n\t\r, ')
header_line = re.sub(r'[\t ;,]+', ';', header_line)
self.ascii_table.setHorizontalHeaderLabels(header_line.split(';'))
@QtCore.pyqtSlot(int, name='on_staggered_checkBox_stateChanged') @QtCore.pyqtSlot(int, name='on_staggered_checkBox_stateChanged')
def changestaggeredrange(self, state: int): def changestaggeredrange(self, state: int):
@ -178,11 +181,12 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def accept(self): def accept(self):
if self.apply(): if self.apply():
self.close() super().accept()
def apply(self): def apply(self):
# default row for x is the first row, it will be superseded if an integer number is given. # default row for x is the first row, it will be superseded if an integer number is given.
x = self.x_lineedit.text() x = self.x_lineedit.text()
is_valid = True
if x: if x:
try: try:
x = int(x)-1 x = int(x)-1
@ -191,16 +195,35 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
else: else:
x = None x = None
if not self.check_column_numbers(x, max(self.reader.width)):
_ = QtWidgets.QMessageBox.information(self, 'Improper input',
f'Input for x axis is invalid')
return False
try: try:
y = [int(t)-1 for t in self.y_lineedit.text().split(' ')] y = [int(t)-1 for t in self.y_lineedit.text().split(' ')]
except ValueError: except ValueError:
y = None y = None
if not self.check_column_numbers(y, max(self.reader.width)):
_ = QtWidgets.QMessageBox.information(self, 'Improper input',
f'Input for y axis is invalid')
return False
try: try:
y_err = [int(t)-1 for t in self.deltay_lineEdit.text().split(' ')] y_err = [int(t)-1 for t in self.deltay_lineEdit.text().split(' ')]
except ValueError: except ValueError:
y_err = None y_err = None
mode = self.buttonGroup.checkedButton().text()
if mode != 'Points':
y_err = None
if not self.check_column_numbers(y, max(self.reader.width)):
_ = QtWidgets.QMessageBox.information(self, 'Improper input',
f'Input for y_err axis is invalid')
return False
col_header = None col_header = None
if self.column_checkBox.isChecked(): if self.column_checkBox.isChecked():
col_header = [] col_header = []
@ -211,28 +234,29 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
col_header.append(i) col_header.append(i)
if y is not None and col_header is not None: if y is not None and col_header is not None:
col_header = [col_header[i] for i in range(len(col_header)) if i in y] col_header = [col_header[i] for i in range(len(col_header))]
try: try:
ret_dic = self.reader.export( ret_dic = self.reader.export(
x=x, x=x,
y=y, y=y,
yerr=y_err, yerr=y_err,
mode=self.buttonGroup.checkedButton().text(), mode=mode,
col_names=col_header, col_names=col_header,
num_value=self.get_numerical_value(), num_value=self.get_numerical_value(),
) )
self.data_read.emit(ret_dic) self.data_read.emit(ret_dic)
except ImportError as e: except Exception as e:
_ = QtWidgets.QMessageBox.information(self, 'Reading failed', _ = QtWidgets.QMessageBox.information(self, 'Reading failed',
f'Import data failed with {e.args}') f'Import data failed with\n {e.args[0]}')
return False
return True return True
@QtCore.pyqtSlot(int, name='on_buttonGroup_buttonClicked') @QtCore.pyqtSlot(int, name='on_buttonGroup_buttonClicked')
def show_error(self, val: int): def show_error(self, val: int):
self.deltay_lineEdit.setEnabled(val == -3) self.deltay_lineEdit.setEnabled(val == -2)
@QtCore.pyqtSlot(int, name='on_skippy_checkbox_stateChanged') @QtCore.pyqtSlot(int, name='on_skippy_checkbox_stateChanged')
def skip_next_dial(self, _: int): def skip_next_dial(self, _: int):
@ -243,19 +267,31 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
if self.reader is None: if self.reader is None:
return return
try: success = True
pattern = re.compile(pattern)
self.regex_input.setStyleSheet('color: rgb(0, 0, 0)')
self._matches = [m for m in pattern.finditer(str(self.reader.fname.stem))]
except re.error:
self._matches = [] self._matches = []
if self._matches: if pattern:
try:
re_pattern = re.compile(pattern)
except re.error:
success = False
else:
self._matches = [m for m in re_pattern.finditer(str(self.reader.fname.stem))]
else:
success = False
# matches exist and have numbers in them
if self._matches and all([len(numbers_from_string(m.group())) for m in self._matches]):
self.re_match_index.blockSignals(True) self.re_match_index.blockSignals(True)
self.re_match_index.setMaximum(len(self._matches)) self.re_match_index.setMaximum(len(self._matches))
self.re_match_index.blockSignals(False) self.re_match_index.blockSignals(False)
else: else:
self.regex_input.setStyleSheet('color: rgb(255, 0, 0)') success = False
if success:
self.regex_input.setStyleSheet('color: rgb(0, 0, 0)')
else:
self.regex_input.setStyleSheet('background-color: rgba(255, 0, 0, 50)')
self.show_match(self.re_match_index.value()) self.show_match(self.re_match_index.value())
@ -269,12 +305,32 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
else: else:
self.label_8.setText(fname) self.label_8.setText(fname)
def get_numerical_value(self): def get_numerical_value(self) -> float:
val = 0 val = 0
if self.re_button.isChecked() and self._matches: if self.re_button.isChecked() and self._matches:
m = self._matches[self.re_match_index.value()-1] m = self._matches[self.re_match_index.value()-1]
val = float(NUMBER_RE.search(m.group()).group().replace('p', '.')) val = numbers_from_string(m.group())
# numbers_from returns list of floats we use first match if available
val = val[0] if val else 0.0
elif self.custom_button.isChecked(): elif self.custom_button.isChecked():
val = float(self.custom_input.text()) val = float(self.custom_input.text())
return val return val
def check_column_numbers(self, values: int | list[int] | str | None, num_column: int) -> bool:
is_valid = False
if values is None:
is_valid = True
elif values == 'index':
is_valid = True
elif isinstance(values, int):
is_valid = values < num_column
elif isinstance(values, list):
try:
is_valid = all(v < num_column for v in values)
except TypeError:
is_valid = False
return is_valid

View File

@ -78,14 +78,22 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
for opts in self.sample.steps: for opts in self.sample.steps:
item = QtWidgets.QListWidgetItem() item = QtWidgets.QListWidgetItem()
item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable) item.setFlags(
item.setCheckState(QtCore.Qt.Unchecked) QtCore.Qt.ItemFlag.ItemIsEnabled |
QtCore.Qt.ItemFlag.ItemIsSelectable |
QtCore.Qt.ItemFlag.ItemIsUserCheckable
)
item.setCheckState(QtCore.Qt.CheckState.Unchecked)
if opts[0] == 'i': if opts[0] == 'i':
item.setFlags(QtCore.Qt.NoItemFlags) item.setFlags(QtCore.Qt.ItemFlag.NoItemFlags)
item.setText(f'{opts[1]:.2f} K for {opts[2] / 60:.0f} min') item.setText(f'{opts[1]:.2f} K for {opts[2] / 60:.0f} min')
else: else:
item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable) item.setFlags(
QtCore.Qt.ItemFlag.ItemIsEnabled |
QtCore.Qt.ItemFlag.ItemIsSelectable |
QtCore.Qt.ItemFlag.ItemIsUserCheckable
)
item.setText(f'{opts[2]:.2f} K to {opts[3]:.2f} K with {opts[1]} K/min') item.setText(f'{opts[2]:.2f} K to {opts[3]:.2f} K with {opts[1]} K/min')
self.step_listWidget.addItem(item) self.step_listWidget.addItem(item)
@ -97,7 +105,12 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
if empty: if empty:
self.empty = self.calibrator.set_measurement(empty, mode='empty') self.empty = self.calibrator.set_measurement(empty, mode='empty')
# avoid ValueError breaking data update
if self.empty.fname.is_relative_to(Path.home()):
self.empty_label.setText('~/' + str(self.empty.fname.relative_to(Path.home()))) self.empty_label.setText('~/' + str(self.empty.fname.relative_to(Path.home())))
else:
self.empty_label.setText(str(self.empty.fname))
self.update_plots() self.update_plots()
@ -118,8 +131,8 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
self.references.append(ref) self.references.append(ref)
item = QtWidgets.QTableWidgetItem(str(ref.fname.name)) item = QtWidgets.QTableWidgetItem(str(ref.fname.name))
item.setData(QtCore.Qt.UserRole, ref.fname) item.setData(QtCore.Qt.ItemDataRole.UserRole, ref.fname)
item.setFlags(QtCore.Qt.ItemIsEnabled) item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled)
rowcnt = self.reference_tableWidget.rowCount() rowcnt = self.reference_tableWidget.rowCount()
self.reference_tableWidget.setRowCount(rowcnt+1) self.reference_tableWidget.setRowCount(rowcnt+1)
@ -132,7 +145,7 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
@QtCore.pyqtSlot(name='on_ref_remove_pushButton_clicked') @QtCore.pyqtSlot(name='on_ref_remove_pushButton_clicked')
def remove_reference(self): def remove_reference(self):
idx = self.reference_tableWidget.currentRow() idx = self.reference_tableWidget.currentRow()
self.calibrator.remove_reference(self.reference_tableWidget.item(idx, 0).data(QtCore.Qt.UserRole)) self.calibrator.remove_reference(self.reference_tableWidget.item(idx, 0).data(QtCore.Qt.ItemDataRole.UserRole))
self.reference_tableWidget.removeRow(idx) self.reference_tableWidget.removeRow(idx)
self.update_plots() self.update_plots()
@ -145,10 +158,10 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
for row in range(self.step_listWidget.count()): for row in range(self.step_listWidget.count()):
if idx == row: if idx == row:
continue continue
self.step_listWidget.item(row).setCheckState(QtCore.Qt.Unchecked) self.step_listWidget.item(row).setCheckState(QtCore.Qt.CheckState.Unchecked)
self.step_listWidget.blockSignals(False) self.step_listWidget.blockSignals(False)
if item.checkState() == QtCore.Qt.Checked: if item.checkState() == QtCore.Qt.CheckState.Checked:
mode, rate, _, _ = self.sample.steps[idx] mode, rate, _, _ = self.sample.steps[idx]
self.current_run = (rate, mode) self.current_run = (rate, mode)
self.sample_idx = idx self.sample_idx = idx
@ -217,6 +230,8 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
if empty_data is not None: if empty_data is not None:
self.empty_sample.setData(x=empty_data[0], y=empty_data[1]) self.empty_sample.setData(x=empty_data[0], y=empty_data[1])
else:
self.empty_sample.setData(x=[], y=[])
self.calib_graph.clear() self.calib_graph.clear()
@ -249,11 +264,16 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
except TypeError: except TypeError:
return return
if self.cp_checkBox.isChecked() and self.references:
y_label = 'cp'
else:
y_label = 'q'
rate, mode = self.current_run rate, mode = self.current_run
new_val = DSC(sample_data[0], sample_data[1], value=rate, name=f'{self.fname.stem} {rate} ({mode})') new_val = DSC(sample_data[0], sample_data[1], value=rate, name=f'{self.fname.stem} {rate}K-min ({mode}, {y_label})')
if filesave: if filesave:
new_val.savetxt(self.fname.with_name(f'{self.fname.stem} {rate}K-min {mode}.dat'.replace(' ', '_'))) new_val.savetxt(self.fname.with_name(f'{self.fname.stem}_{rate}K-min_{y_label}{mode}.dat'.replace(' ', '_')))
close_after = False close_after = False
else: else:
self.data_read.emit([new_val]) self.data_read.emit([new_val])

View File

@ -28,14 +28,19 @@ class GraceExporter:
new_g.set_log(x=self.__opts['log'][0], y=self.__opts['log'][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_onoff('legend', self.__opts['legend'])
new_g.set_property(**{'title': f'"{convert(self.__opts["labels"][2], old="html", new="agr")}"', new_g.set_property(**{
'title': f'"{convert(self.__opts["labels"][2], old="html", new="agr")}"',
'legend loctype': 'view', 'legend loctype': 'view',
'legend': ', '.join(str(i) for i in new_g.world_to_view(self.__opts['legend_pos']))}) 'legend': ', '.join(str(i) for i in new_g.world_to_view(self.__opts['legend_pos']))
})
for i, ax in enumerate('xy'): for i, ax in enumerate('xy'):
new_g.set_axis_property(ax, **{'label': f'"{convert(self.__opts["labels"][i], old="html", new="agr")}"', 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 major': self.__opts['ticks'][i][0],
'tick minor ticks': self.__opts['ticks'][i][1],}) 'tick minor ticks': self.__opts['ticks'][i][1],
'invert': 'on' if self.__opts['invert'][i] else 'off',
})
new_g.set_axis_onoff(ax, 'tick major grid', self.__opts['grid']) new_g.set_axis_onoff(ax, 'tick major grid', self.__opts['grid'])
g_idx = new_g.idx g_idx = new_g.idx
else: else:
@ -59,8 +64,13 @@ class GraceExporter:
colors[c_num] = (f'color{c_num}', sc) colors[c_num] = (f'color{c_num}', sc)
new_colors.append((c_num, f'color{c_num}', sc)) new_colors.append((c_num, f'color{c_num}', sc))
new_s.set_symbol(**{'symbol': item['symbol'].value, 'size': item['symbolsize'] / 10., 'color': c_num, new_s.set_symbol(**{
'fill color': c_num, 'fill pattern': 1}) '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]) new_s.set_onoff('errorbar', self.__opts['plots'][2])
lc = item['linecolor'] lc = item['linecolor']
@ -74,12 +84,17 @@ class GraceExporter:
colors[c_num + 1] = () colors[c_num + 1] = ()
new_colors.append((c_num, f'color{c_num + 1}', sc)) new_colors.append((c_num, f'color{c_num + 1}', sc))
new_s.set_line(**{'color': c_num, 'linewidth': item['linewidth'], new_s.set_line(**{
'linestyle': item['linestyle'].to_agr()}) 'color': c_num,
'linewidth': item['linewidth'],
'linestyle': item['linestyle'].to_agr()
})
if plot_label: if plot_label:
new_s.set_property(comment=f'"{item["name"]}"', new_s.set_property(
legend=f'"{convert(item["name"], old="tex", new="agr")}"') comment=f'"{item["name"]}"',
legend=f'"{convert(item["name"], old="tex", new="agr")}"'
)
else: else:
new_s.set_property(comment=f'"{item["name"]}"') new_s.set_property(comment=f'"{item["name"]}"')

View File

@ -22,12 +22,22 @@ class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog):
self.start_lineedit.setValidator(QtGui.QDoubleValidator()) self.start_lineedit.setValidator(QtGui.QDoubleValidator())
self.stop_lineedit.setValidator(QtGui.QDoubleValidator()) self.stop_lineedit.setValidator(QtGui.QDoubleValidator())
self.graph_checkbox.stateChanged.connect(
lambda x: self.graph_comboBox.setEnabled(x == QtCore.Qt.CheckState.Unchecked)
)
self.listWidget.installEventFilter(self) self.listWidget.installEventFilter(self)
def __call__(self, path=None):
if path is None:
path = pathlib.Path().home()
self.path = path
self.listWidget.clear()
def eventFilter(self, src: QtCore.QObject, evt: QtCore.QEvent) -> bool: def eventFilter(self, src: QtCore.QObject, evt: QtCore.QEvent) -> bool:
# intercept key press in listwidget to allow deletion with Del # intercept key press in listwidget to allow deletion with Del
if evt.type() == QtCore.QEvent.KeyPress: if evt.type() == QtCore.QEvent.Type.KeyPress:
if evt.key() == QtCore.Qt.Key_Delete: if evt.key() == QtCore.Qt.Key.Key_Delete:
self.listWidget.takeItem(self.listWidget.currentRow()) self.listWidget.takeItem(self.listWidget.currentRow())
return True return True
@ -39,21 +49,25 @@ class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog):
@QtCore.pyqtSlot(int, name='on_region_checkBox_stateChanged') @QtCore.pyqtSlot(int, name='on_region_checkBox_stateChanged')
def use_region(self, state: int): def use_region(self, state: int):
self.start_lineedit.setEnabled(state == QtCore.Qt.Checked) self.start_lineedit.setEnabled(state == QtCore.Qt.CheckState.Checked)
self.stop_lineedit.setEnabled(state == QtCore.Qt.Checked) self.stop_lineedit.setEnabled(state == QtCore.Qt.CheckState.Checked)
@QtCore.pyqtSlot(name='on_file_pushbutton_clicked') @QtCore.pyqtSlot(name='on_file_pushbutton_clicked')
@QtCore.pyqtSlot(name='on_dir_pushbutton_clicked') @QtCore.pyqtSlot(name='on_dir_pushbutton_clicked')
def get_input(self): def get_input(self):
if self.sender() == self.file_pushbutton: if self.sender() == self.file_pushbutton:
infiles, _ = QtWidgets.QFileDialog.getOpenFileNames(caption='Select HDF files', infiles, _ = QtWidgets.QFileDialog.getOpenFileNames(
caption='Select HDF files',
directory=str(self.path), directory=str(self.path),
filter='HDF files (*.h5)') filter='HDF files (*.h5)',
)
else: else:
infiles = QtWidgets.QFileDialog.getExistingDirectory(caption='Select input directory', infiles = QtWidgets.QFileDialog.getExistingDirectory(
caption='Select input directory',
directory=str(self.path), directory=str(self.path),
options=QtWidgets.QFileDialog.ShowDirsOnly) options=QtWidgets.QFileDialog.ShowDirsOnly,
)
infiles = [infiles] if infiles else infiles infiles = [infiles] if infiles else infiles
if infiles: if infiles:
@ -63,9 +77,12 @@ class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog):
@QtCore.pyqtSlot(name='on_savebutton_clicked') @QtCore.pyqtSlot(name='on_savebutton_clicked')
def save_path(self): def save_path(self):
outfile = QtWidgets.QFileDialog.getExistingDirectory(self, caption='Select directory', outfile = QtWidgets.QFileDialog.getExistingDirectory(
self,
caption='Select directory',
directory=self.label.text(), directory=self.label.text(),
options=QtWidgets.QFileDialog.ShowDirsOnly) options=QtWidgets.QFileDialog.ShowDirsOnly,
)
if outfile: if outfile:
self.label.setText(outfile) self.label.setText(outfile)
@ -82,11 +99,11 @@ class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog):
if self.region_box.isChecked(): if self.region_box.isChecked():
start = None start = None
if self.start_lineedit.text(): if self.start_lineedit.text():
start = float(self.start_lineedit.text()) start = float(self.start_lineedit.text())*1e-6
stop = None stop = None
if self.stop_lineedit.text(): if self.stop_lineedit.text():
stop = float(self.stop_lineedit.text()) stop = float(self.stop_lineedit.text())*1e-6
region = (start, stop) region = (start, stop)
fc_eval = FCReader(items) fc_eval = FCReader(items)
@ -105,9 +122,10 @@ class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog):
ret_vals = [] ret_vals = []
ret_vals.extend(fc_eval.get_parameter(path=self.label.text(), kind='temp', parameter=save_variables)) ret_vals.extend(fc_eval.get_parameter(path=self.label.text(), kind='temp', parameter=save_variables))
print(ret_vals)
grp = '' grp = ''
if self.graph_checkbox.isChecked(): if not self.graph_checkbox.isChecked():
grp = self.graph_comboBox.currentData(QtCore.Qt.UserRole) grp = self.graph_comboBox.currentData(QtCore.Qt.ItemDataRole.UserRole)
self.data_read.emit(ret_vals, grp) self.data_read.emit(ret_vals, grp)

View File

@ -3,7 +3,7 @@ from __future__ import annotations
from pathlib import Path from pathlib import Path
import struct import struct
from ..Qt import QtCore from ..Qt import QtCore, QtWidgets
from .asciireader import QAsciiReader from .asciireader import QAsciiReader
from .hdfreader import QHdfViewer from .hdfreader import QHdfViewer
from .bdsreader import QBDSReader from .bdsreader import QBDSReader
@ -26,8 +26,12 @@ class QFileReader(QtCore.QObject):
self.reader = {} self.reader = {}
for ext, reader in [ for ext, reader in [
('txt', QAsciiReader), ('dsc', QDSCReader), ('agr', QGraceReader), ('txt', QAsciiReader),
('bds', QBDSReader), ('hdf', QHdfViewer), ('nmr', QNMRReader) ('dsc', QDSCReader),
('agr', QGraceReader),
('bds', QBDSReader),
('hdf', QHdfViewer),
('nmr', QNMRReader),
]: ]:
self.register(ext, reader) self.register(ext, reader)
@ -47,6 +51,7 @@ class QFileReader(QtCore.QObject):
if not isinstance(fname, list): if not isinstance(fname, list):
fname = [fname] fname = [fname]
status = QtWidgets.QDialog.Accepted
for f in fname: for f in fname:
f = Path(f) f = Path(f)
dtype = self.guess_type(f) dtype = self.guess_type(f)
@ -57,7 +62,10 @@ class QFileReader(QtCore.QObject):
try: try:
# If QAsciiReader.skip = True it accepts automatically and returns None # If QAsciiReader.skip = True it accepts automatically and returns None
r(f).exec() if status == QtWidgets.QDialog.DialogCode.Rejected and isinstance(r, QAsciiReader) and self.reader['txt'].skip:
break
status = r(f).exec()
except AttributeError: except AttributeError:
pass pass

207
src/gui_qt/lib/backup.py Normal file
View File

@ -0,0 +1,207 @@
import os
import sqlite3
from datetime import datetime
from pathlib import Path
from nmreval.configs import config_paths
from ..Qt import QtCore, QtWidgets
DB_FILE = '/tmp/nmreval.db'
class BackupManager(QtCore.QObject):
def __init__(self):
super().__init__()
self.create_table()
self._pid = None
def create_table(self):
con = sqlite3.connect(DB_FILE)
con.execute(
"CREATE TABLE IF NOT EXISTS sessions "
"(pid INTEGER NOT NULL, backup_file TEXT NOT NULL, last_save TEXT);"
)
def create_entry(self, pid: int):
backup_path = config_paths() / f'autosave_{datetime.now().strftime("%Y-%m-%d_%H%M%S")}_{pid}.nmr'
con = sqlite3.connect(DB_FILE)
con.execute('INSERT INTO sessions VALUES(?, ?, ?);',
(pid, str(backup_path), None))
con.commit()
con.close()
self._pid = pid
return backup_path
def update_last_save(self):
con = sqlite3.connect(DB_FILE)
con.execute(
'UPDATE sessions SET last_save = ? WHERE pid = ?',
(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), self._pid)
)
con.commit()
con.close()
def search_unsaved(self):
con = sqlite3.connect(DB_FILE)
cursor = con.cursor()
res = cursor.execute('SELECT sessions.* FROM sessions;')
con.commit()
data = res.fetchall()
con.close()
missing_processes = []
for pid, fname, save_date in data:
try:
os.kill(pid, 0)
except ProcessLookupError:
if Path(fname).exists():
missing_processes.append((pid, fname, save_date))
else:
# remove entries without valid file
self.remove_row(pid)
if missing_processes:
msg = QLonelyBackupWindow()
msg.add_files(missing_processes)
msg.exec()
toberead = msg.recover
for pid in toberead[0]:
self.remove_file(pid)
for pid in toberead[1]:
self.remove_row(pid)
return list(toberead[1].values())
return []
def remove_row(self, pid):
con = sqlite3.connect(DB_FILE)
con.execute('DELETE FROM sessions WHERE pid = ?', (pid,))
con.commit()
con.close()
def remove_file(self, pid: int = None):
if pid is None:
pid = self._pid
con = sqlite3.connect(DB_FILE)
cursor = con.cursor()
# remove backup file
res = cursor.execute('SELECT sessions.backup_file FROM sessions WHERE pid = ?', (pid,))
con.commit()
fname = Path(res.fetchone()[0])
con.close()
fname.unlink(missing_ok=True)
# because autosave backups in a *.nmr.0 file and then moves it to *.nmr we look also for this one.
fname.with_suffix('.nmr.0').unlink(missing_ok=True)
# after removal of file also remove entry
self.remove_row(pid)
def delete_db_if_empty(self):
con = sqlite3.connect(DB_FILE)
cursor = con.cursor()
res = cursor.execute('SELECT COUNT(sessions.pid) FROM sessions GROUP BY sessions.pid;')
con.commit()
remaining_processes = res.fetchone()
con.close()
if remaining_processes is None:
Path(DB_FILE).unlink()
def close(self):
self.remove_file()
self.delete_db_if_empty()
class QLonelyBackupWindow(QtWidgets.QDialog):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.recover = [[], {}] # list of pid to delete, dict of files to read {pid: file}
self.setWindowTitle('Adopt a file!')
self.resize(720, 320)
layout = QtWidgets.QVBoxLayout(self)
self.label = QtWidgets.QLabel(self)
self.label.setText('Abandoned backup file(s) looking for a loving home!\n'
'(Files will all be loaded to same instance)')
layout.addWidget(self.label)
self.table = QtWidgets.QTableWidget(self)
self.table.setColumnCount(4)
self.table.setHorizontalHeaderLabels(['File name', 'Last saved', 'File size', 'Action'])
self.table.setGridStyle(QtCore.Qt.PenStyle.DashLine)
# self.table.horizontalHeader().setStretchLastSection(True)
self.table.verticalHeader().setVisible(False)
self.table.horizontalHeader().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
layout.addWidget(self.table)
self.buttons = QtWidgets.QDialogButtonBox(self)
self.buttons.setOrientation(QtCore.Qt.Orientation.Horizontal)
self.buttons.setStandardButtons(QtWidgets.QDialogButtonBox.Ok)
layout.addWidget(self.buttons)
self.buttons.accepted.connect(self.accept)
def add_files(self, entries):
self.table.setRowCount(len(entries))
for i, (pid, path, date) in enumerate(entries):
path = Path(path)
item1 = QtWidgets.QTableWidgetItem(path.name)
item1.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled)
item1.setData(QtCore.Qt.ItemDataRole.UserRole, pid)
item1.setData(QtCore.Qt.ItemDataRole.UserRole+1, path)
self.table.setItem(i, 0, item1)
item2 = QtWidgets.QTableWidgetItem(date)
item2.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled)
self.table.setItem(i, 1, item2)
size = path.stat().st_size
size_cnt = 0
while size > 1024:
# make file size human-readable
size /= 1024
size_cnt += 1
if size_cnt == 5:
break
byte = ['bytes', 'kB', 'MiB', 'GiB', 'TiB', 'PiB'][size_cnt]
item3 = QtWidgets.QTableWidgetItem(f'{size:.2f} {byte}')
item3.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled)
self.table.setItem(i, 2, item3)
cw = QtWidgets.QComboBox(self)
cw.addItems(['Load', 'Delete', 'Keep for later'])
self.table.setCellWidget(i, 3, cw)
self.table.resizeColumnsToContents()
def accept(self):
for i in range(self.table.rowCount()):
decision = self.table.cellWidget(i, 3).currentIndex()
item = self.table.item(i, 0)
pid = item.data(QtCore.Qt.ItemDataRole.UserRole)
if decision == 0:
# load file
self.recover[1][pid] = item.data(QtCore.Qt.ItemDataRole.UserRole+1)
elif decision == 1:
# delete
self.recover[0].append(pid)
else:
# do nothing
pass
super().accept()

View File

@ -1,17 +1,21 @@
import sys import logging
from pathlib import Path from pathlib import Path
from .codeeditor import _make_textformats from PyQt5 import QtWidgets
from ..editors.codeeditor import _make_textformats
from ..Qt import QtWidgets, QtCore, QtGui from ..Qt import QtWidgets, QtCore, QtGui
from nmreval.configs import config_paths from nmreval.configs import config_paths
STYLES = {'INFO': _make_textformats('black'), STYLES = {
'INFO': _make_textformats('black'),
'WARNING': _make_textformats('blue'), 'WARNING': _make_textformats('blue'),
'ERROR': _make_textformats('red', 'bold'), 'ERROR': _make_textformats('red', 'bold'),
'DEBUG': _make_textformats('black', 'italic'), 'DEBUG': _make_textformats('black', 'italic'),
'file': _make_textformats('red', 'italic'), 'file': _make_textformats('red', 'italic'),
'PyError': _make_textformats('red', 'bold-italic')} 'PyError': _make_textformats('red', 'bold-italic'),
}
class LogHighlighter(QtGui.QSyntaxHighlighter): class LogHighlighter(QtGui.QSyntaxHighlighter):
@ -112,3 +116,28 @@ class QLog(QtWidgets.QDialog):
for lines in text[-100:]: for lines in text[-100:]:
self.plainTextEdit.appendPlainText(lines[:-1]) self.plainTextEdit.appendPlainText(lines[:-1])
class ConsoleDock(QtWidgets.QDockWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.code = QtWidgets.QPlainTextEdit(parent)
self.code.highlight = LogHighlighter(self.code.document())
self.code.setReadOnly(True)
self.code.setMaximumBlockCount(50)
self.setWidget(self.code)
class QTextHandler(logging.Handler):
def __init__(self, parent):
super().__init__()
self.console = ConsoleDock(parent)
self.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
self.setLevel(logging.WARNING)
def emit(self, record):
msg = self.format(record)
self.console.code.appendPlainText(msg)
self.console.show()

View File

@ -7,8 +7,11 @@ from ..graphs.graphwindow import QGraphWindow
class MdiAreaTile(QtWidgets.QMdiArea): class MdiAreaTile(QtWidgets.QMdiArea):
newData = QtCore.pyqtSignal(list)
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent=parent) super().__init__(parent=parent)
self.setAcceptDrops(True)
def tileSubWindowsVertically(self): def tileSubWindowsVertically(self):
window_list = self.subWindowList() window_list = self.subWindowList()
@ -47,3 +50,8 @@ class MdiAreaTile(QtWidgets.QMdiArea):
if isinstance(wdgt, QGraphWindow) and wdgt.id == key: if isinstance(wdgt, QGraphWindow) and wdgt.id == key:
self.setActiveSubWindow(win) self.setActiveSubWindow(win)
break break
def dropEvent(self, evt):
if evt.mimeData().hasUrls():
files = [str(url.toLocalFile()) for url in evt.mimeData().urls()]
self.newData.emit(files)

View File

@ -4,6 +4,8 @@ from collections import namedtuple
import numpy as np import numpy as np
import nmreval
from nmreval import models from nmreval import models
from nmreval.configs import config_paths from nmreval.configs import config_paths
from nmreval.lib.importer import find_models, import_ from nmreval.lib.importer import find_models, import_
@ -22,17 +24,20 @@ class Namespace:
if basic: if basic:
self.add_namespace( self.add_namespace(
{'x': (None, 'x values'), {
'x': (None, 'x values'),
'y': (None, 'x values'), 'y': (None, 'x values'),
'y_err': (None, 'y error values'), 'y_err': (None, 'y error values'),
'fit': (None, 'dictionary of fit parameter', 'fit["PIKA"]'), 'fit': (None, 'dictionary of fit parameter', 'fit["PIKA"]'),
'np': (np, 'numpy module'), 'np': (np, 'numpy module'),
'nmreval': (nmreval, 'built-in classes and stuff')
}, },
parents=('Basic', 'General'), parents=('Basic', 'General'),
) )
self.add_namespace( self.add_namespace(
{'sin': (np.sin, 'Sine', 'sin(PIKA)'), {
'sin': (np.sin, 'Sine', 'sin(PIKA)'),
'cos': (np.cos, 'Cosine', 'cos(PIKA)'), 'cos': (np.cos, 'Cosine', 'cos(PIKA)'),
'tan': (np.tan, 'Tangens', 'tan(PIKA)'), 'tan': (np.tan, 'Tangens', 'tan(PIKA)'),
'ln': (np.log, 'Natural Logarithm', 'ln(PIKA)'), 'ln': (np.log, 'Natural Logarithm', 'ln(PIKA)'),
@ -45,7 +50,8 @@ class Namespace:
parents=('Basic', 'Functions')) parents=('Basic', 'Functions'))
self.add_namespace( self.add_namespace(
{'max': (np.max, 'Maximum value', 'max(PIKA)'), {
'max': (np.max, 'Maximum value', 'max(PIKA)'),
'min': (np.min, 'Minimum value', 'min(PIKA)'), 'min': (np.min, 'Minimum value', 'min(PIKA)'),
'argmax': (np.argmax, 'Index of maximum value', 'argmax(PIKA)'), 'argmax': (np.argmax, 'Index of maximum value', 'argmax(PIKA)'),
'argmin': (np.argmax, 'Index of minimum value', 'argmin(PIKA)'), 'argmin': (np.argmax, 'Index of minimum value', 'argmin(PIKA)'),
@ -54,19 +60,24 @@ class Namespace:
if const: if const:
self.add_namespace( self.add_namespace(
{'e': (constants.e, 'e / As'), {
'e': (constants.e, 'e / As'),
'eps0': (constants.epsilon0, 'epsilon0 / As/Vm'), 'eps0': (constants.epsilon0, 'epsilon0 / As/Vm'),
'Eu': (constants.Eu,), 'h': (constants.h, 'h / eVs'), 'Eu': (constants.Eu,),
'hbar': (constants.hbar, 'hbar / eVs'), 'kB': (constants.kB, 'kB / eV/K'), 'h': (constants.h, 'h / eVs'),
'mu0': (constants.mu0, 'mu0 / Vs/Am'), 'NA': (constants.NA, 'NA / 1/mol'), 'hbar': (constants.hbar, 'hbar / eVs'),
'pi': (constants.pi,), 'R': (constants.R, 'R / eV'), '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'), parents=('Constants', 'Maybe useful'),
) )
self.add_namespace( self.add_namespace(
{f'gamma["{k}"]': (v, k, f'gamma["{k}"]') for k, v in constants.gamma.items()}, {f'gamma["{k}"]': (v, k, f'gamma["{k}"]') for k, v in constants.gamma.items()},
parents=('Constants', 'Magnetogyric ratios (in 1/(sT))') parents=('Constants', 'Gyromagnetic ratios (in 1/(sT))')
) )
if fitfuncs: if fitfuncs:
@ -199,7 +210,7 @@ class QNamespaceWidget(QtWidgets.QWidget, Ui_Form):
for entry in subspace: for entry in subspace:
key_item = QtWidgets.QTableWidgetItem(entry) key_item = QtWidgets.QTableWidgetItem(entry)
key_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) key_item.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled)
vals = self.namespace.namespace[entry] vals = self.namespace.namespace[entry]
@ -214,12 +225,12 @@ class QNamespaceWidget(QtWidgets.QWidget, Ui_Form):
display = vals[1] display = vals[1]
value_item = QtWidgets.QTableWidgetItem(display) value_item = QtWidgets.QTableWidgetItem(display)
value_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) value_item.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled)
key_item.setData(QtCore.Qt.UserRole, alias) key_item.setData(QtCore.Qt.ItemDataRole.UserRole, alias)
key_item.setData(QtCore.Qt.UserRole+1, entry) key_item.setData(QtCore.Qt.ItemDataRole.UserRole+1, entry)
value_item.setData(QtCore.Qt.UserRole, alias) value_item.setData(QtCore.Qt.ItemDataRole.UserRole, alias)
value_item.setData(QtCore.Qt.UserRole+1, entry) value_item.setData(QtCore.Qt.ItemDataRole.UserRole+1, entry)
row = self.namespace_table.rowCount() row = self.namespace_table.rowCount()
self.namespace_table.setRowCount(row+1) self.namespace_table.setRowCount(row+1)
@ -241,5 +252,5 @@ class QNamespaceWidget(QtWidgets.QWidget, Ui_Form):
@QtCore.pyqtSlot(QtWidgets.QTableWidgetItem, name='on_namespace_table_itemDoubleClicked') @QtCore.pyqtSlot(QtWidgets.QTableWidgetItem, name='on_namespace_table_itemDoubleClicked')
def item_selected(self, item: QtWidgets.QTableWidgetItem): def item_selected(self, item: QtWidgets.QTableWidgetItem):
self.selected.emit(item.data(QtCore.Qt.UserRole)) self.selected.emit(item.data(QtCore.Qt.ItemDataRole.UserRole))
self.sendKey.emit(item.data(QtCore.Qt.UserRole+1)) self.sendKey.emit(item.data(QtCore.Qt.ItemDataRole.UserRole+1))

View File

@ -183,6 +183,8 @@ class PlotItem(PlotDataItem):
brush = self.opts['symbolBrush'] brush = self.opts['symbolBrush']
if isinstance(brush, tuple): if isinstance(brush, tuple):
self.opts['symbolcolor'] = brush self.opts['symbolcolor'] = brush
elif isinstance(brush, str):
self.opts['symbolcolor'] = int(f'0x{brush[1:3]}', 16), int(f'0x{brush[3:5]}', 16), int(f'0x{brush[5:7]}', 16)
else: else:
c = brush.color() c = brush.color()
self.opts['symbolcolor'] = c.red(), c.green(), c.blue() self.opts['symbolcolor'] = c.red(), c.green(), c.blue()
@ -340,7 +342,8 @@ class PlotItem(PlotDataItem):
opts = self.opts opts = self.opts
item_dic = { item_dic = {
'x': x, 'y': y, 'x': x,
'y': y,
'name': opts.get('name', ''), 'name': opts.get('name', ''),
'symbolsize': opts['symbolSize'], 'symbolsize': opts['symbolSize'],
} }

556
src/gui_qt/lib/pokemon.py Normal file
View File

@ -0,0 +1,556 @@
import sqlite3
import urllib.request
from functools import cache
from PyQt5 import QtWidgets, QtGui, QtCore
from numpy.random import randint
from gui_qt._py.pokewindow import Ui_Dialog
from gui_qt._py.pokeentry import Ui_Form
def get_connection(db):
connection = sqlite3.connect(db)
connection.row_factory = sqlite3.Row
return connection
class QPoke(QtWidgets.QDialog, Ui_Dialog):
types = {None: ('', '')}
stats = {}
def __init__(self, parent=None):
super().__init__(parent=parent)
from importlib.resources import path
with path('resources', 'pokemon.sqlite') as fp:
self._db = str(fp)
PokemonEntry._db = str(fp)
self.setupUi(self)
self._fetch_names()
self.add_pokedexes()
self.tableWidget_2.itemSelectionChanged.connect(self.show_pokemon)
self.comboBox_2.currentIndexChanged.connect(self.collect_pokemon)
self.comboBox.currentIndexChanged.connect(self.collect_pokemon)
self.pushButton.clicked.connect(self.randomize)
self.collect_pokemon()
def _fetch_names(self):
connection = get_connection(self._db)
cursor = connection.cursor()
cursor.execute(
'SELECT * FROM types'
)
self.comboBox.addItem('All types', -1)
for entry in cursor.fetchall():
self.types[entry['id']] = (entry['name_de'], entry['name_en'])
self.comboBox.addItem(entry['name_en'], entry['id'])
connection.close()
def add_pokedexes(self):
connection = get_connection(self._db)
cursor = connection.cursor()
cursor.execute('SELECT * FROM pokedex_list')
for entry in cursor.fetchall():
self.comboBox_2.addItem(entry['name_en'], entry['id'])
connection.close()
def fill_list(self, idx: int = 0):
pokedex_id = self.comboBox_2.itemData(idx, QtCore.Qt.ItemDataRole.UserRole)
pokedex = self.get_pokedex(pokedex_id)
self.tableWidget_2.clear()
self.tableWidget_2.setColumnCount(4)
self.tableWidget_2.setRowCount(0)
for pokemon in pokedex:
poke_id = pokemon['species_id']
row_count = self.tableWidget_2.rowCount()
self.tableWidget_2.setRowCount(row_count + 1)
item = QtWidgets.QTableWidgetItem(f'#{pokemon["entry_number"]:04d}')
item.setData(QtCore.Qt.ItemDataRole.UserRole, poke_id)
self.tableWidget_2.setItem(row_count, 0, item)
item = QtWidgets.QTableWidgetItem(pokemon['name_en'])
item.setToolTip(pokemon['name_de'])
self.tableWidget_2.setItem(row_count, 1, item)
poke_type = self.types[pokemon['type1']]
item = QtWidgets.QTableWidgetItem(poke_type[1])
item.setToolTip(poke_type[0])
self.tableWidget_2.setItem(row_count, 2, item)
try:
poke_type = self.types[pokemon['type2']]
item = QtWidgets.QTableWidgetItem(poke_type[1])
item.setToolTip(poke_type[0])
self.tableWidget_2.setItem(row_count, 3, item)
except KeyError:
pass
self.tableWidget_2.resizeColumnsToContents()
def collect_pokemon(self, *args):
pokedex_id = self.comboBox_2.currentData(QtCore.Qt.ItemDataRole.UserRole)
type_id = self.comboBox.currentData(QtCore.Qt.ItemDataRole.UserRole)
connection = get_connection(self._db)
cursor = connection.cursor()
if pokedex_id == 1:
if type_id == -1:
cursor.execute(
'SELECT *, pokemon_list.species_id AS entry_number '
'FROM pokemon_list '
'ORDER BY entry_number'
)
else:
cursor.execute(
'SELECT *, pokemon_list.species_id AS entry_number '
'FROM pokemon_list '
'WHERE pokemon_list.type1 = ? OR pokemon_list.type2 = ? '
'ORDER BY entry_number',
(type_id, type_id)
)
else:
if type_id == -1:
cursor.execute(
'SELECT pokemon_list.*, pp.entry_number '
'FROM pokemon_list '
'JOIN pokedex_pokemon pp ON pp.species_id = pokemon_list.species_id '
'WHERE pp.pokedex_id = ? '
'ORDER BY pp.entry_number',
(pokedex_id,)
)
else:
cursor.execute(
'SELECT pokemon_list.*, pp.entry_number '
'FROM pokemon_list '
'JOIN pokedex_pokemon pp ON pp.species_id = pokemon_list.species_id '
'WHERE pp.pokedex_id = ? AND (pokemon_list.type1 = ? OR pokemon_list.type2 = ?) '
'ORDER BY pp.entry_number',
(pokedex_id, type_id, type_id)
)
result = cursor.fetchall()
connection.close()
self.fill_sorter(result)
def fill_sorter(self, result):
self.tableWidget_2.clearContents()
self.tableWidget_2.setRowCount(0)
self.tableWidget_2.setSortingEnabled(False)
for entry in result:
row = self.tableWidget_2.rowCount()
self.tableWidget_2.setRowCount(row+1)
item = QtWidgets.QTableWidgetItem(f"{entry['entry_number']:04d}")
item.setData(QtCore.Qt.ItemDataRole.UserRole, entry['species_id'])
item.setData(QtCore.Qt.ItemDataRole.UserRole+1, entry['pokemon_id'])
self.tableWidget_2.setItem(row, 0, item)
name_en = entry['name_en']
if entry['full_name_en']:
name_en = entry['full_name_en']
elif entry['form_en']:
name_en += f" {entry['form_en']}"
name_de = entry['name_de']
if entry['full_name_de']:
name_de = entry['full_name_de']
elif entry['form_de']:
name_de += f" {entry['form_de']}"
item = QtWidgets.QTableWidgetItem(name_en)
item.setToolTip(name_de)
self.tableWidget_2.setItem(row, 1, item)
type_en = []
type_de = []
for t_id in ('type1', 'type2'):
t_de, t_en = self.types[entry[t_id]]
if t_en:
type_en.append(t_en)
type_de.append(t_de)
item = QtWidgets.QTableWidgetItem(' / '.join(type_en))
item.setToolTip('\n'.join(type_en))
self.tableWidget_2.setItem(row, 2, item)
total = 0
for i, stat_name in enumerate(('hit_points', 'attack', 'defense', 'sp_atk', 'sp_def', 'speed')):
stat_value = entry[stat_name]
item = QtWidgets.QTableWidgetItem()
item.setData(QtCore.Qt.ItemDataRole.DisplayRole, stat_value)
item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignRight | QtCore.Qt.AlignmentFlag.AlignVCenter)
self.tableWidget_2.setItem(row, i+4, item)
total += stat_value
item = QtWidgets.QTableWidgetItem()
item.setData(QtCore.Qt.ItemDataRole.DisplayRole, total)
item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignRight | QtCore.Qt.AlignmentFlag.AlignVCenter)
self.tableWidget_2.setItem(row, 3, item)
item = QtWidgets.QTableWidgetItem()
item.setData(QtCore.Qt.ItemDataRole.DisplayRole, entry['height'] / 10)
item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignRight | QtCore.Qt.AlignmentFlag.AlignVCenter)
self.tableWidget_2.setItem(row, 10, item)
item = QtWidgets.QTableWidgetItem()
item.setData(QtCore.Qt.ItemDataRole.DisplayRole, entry['weight'] / 10)
item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignRight | QtCore.Qt.AlignmentFlag.AlignVCenter)
self.tableWidget_2.setItem(row, 11, item)
item = QtWidgets.QTableWidgetItem()
item.setData(QtCore.Qt.ItemDataRole.DisplayRole, round(entry['weight']/entry['height']**2 * 10, 2))
item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignRight | QtCore.Qt.AlignmentFlag.AlignVCenter)
self.tableWidget_2.setItem(row, 12, item)
self.tableWidget_2.resizeColumnToContents(1)
self.tableWidget_2.resizeColumnToContents(2)
self.tableWidget_2.setSortingEnabled(True)
def randomize(self):
select = randint(0, self.tableWidget_2.rowCount())
self.show_pokemon(select)
def show_pokemon(self, row: int = None):
table = self.tableWidget_2
if row is None:
row = table.currentRow()
species_id = table.item(row, 0).data(QtCore.Qt.ItemDataRole.UserRole)
poke_id = table.item(row, 0).data(QtCore.Qt.ItemDataRole.UserRole+1)
pokemon_name = table.item(row, 1).text()
connection = get_connection(self._db)
cursor = connection.cursor()
cursor.execute(
'SELECT p.id FROM pokemon p WHERE p.species_id = ?',
(species_id,)
)
pokemon = cursor.fetchall()
connection.close()
self.tabWidget.setCurrentIndex(0)
for i in range(1, self.tabWidget.count()):
self.tabWidget.setTabVisible(i, False)
widget_idx = 0
for i, p in enumerate(pokemon):
entry_widget = self.tabWidget.widget(i)
if poke_id == p[0]:
widget_idx = i
if entry_widget is None:
self.tabWidget.addTab(PokemonEntry(p[0]), '')
self.tabWidget.setTabText(i, pokemon_name)
self.tabWidget.setTabVisible(i, True)
name = self.tabWidget.widget(i).create_pokemon(p[0])
self.tabWidget.setTabText(i, name)
self.tabWidget.setCurrentIndex(widget_idx)
class PokemonEntry(QtWidgets.QWidget, Ui_Form):
_db = ''
def __init__(self, pokemon_id: int, parent=None):
super().__init__(parent=parent)
self.setupUi(self)
self.bars = [
None, self.hp_bar, self.attack_bar, self.defense_bar, self.spec_attack_bar, self.spec_defense_bar, self.speed_bar
]
self.ability_labels = [None, self.ability1_label, self.ability2_label, self.ability3_label]
self.type_labels = [None, self.type1_label, self.type2_label]
self.create_pokemon(pokemon_id)
def create_pokemon(self, poke_id):
pokemon = self.get_pokemon(poke_id)
species = self.get_species(pokemon['species_id'])
self.nationaldex_label.setText(f"{pokemon['species_id']:04d}")
self.species_label.setText(species['genus_en'])
self.species_label.setToolTip(species['genus_de'])
self.height_label.setText(f"{pokemon['height'] / 10} m")
self.weight_label.setText(f"{pokemon['weight'] / 10} kg")
if species['gender_ratio'] == -1:
gender = "Gender unknown"
else:
gender = f"{species['gender_ratio']*12.5:0.2f}% female, {100-species['gender_ratio']*12.5:0.2f}% male"
self.gender_label.setText(gender)
if not QtGui.QPixmapCache.find(str(poke_id)):
image = b''
if pokemon['artwork'] is not None:
try:
res = urllib.request.urlopen(pokemon['artwork'])
image = res.read()
except:
pass
pixmap = QtGui.QPixmap()
pixmap.loadFromData(image)
sc_pixmap = pixmap.scaled(400, 400, QtCore.Qt.AspectRatioMode.KeepAspectRatio)
QtGui.QPixmapCache.insert(str(poke_id), sc_pixmap)
self.artwork_label.setPixmap(QtGui.QPixmapCache.find(str(poke_id)))
stats = self.get_stats(poke_id)
for (stat_id, stat_value) in stats:
self.bars[stat_id].setValue(stat_value)
abilities = self.get_abilities(poke_id)
for lab in self.ability_labels[1:]:
lab.setVisible(False)
for (slot, is_hidden, name_de, name_en) in abilities:
self.ability_labels[slot].setVisible(True)
t = name_en
if is_hidden:
t += ' (hidden ability)'
self.ability_labels[slot].setText(t)
self.ability_labels[slot].setToolTip(name_de)
form, types = self.get_form_and_type(poke_id)
for lab in self.type_labels[1:]:
lab.setVisible(False)
for (type_id, slot) in types:
self.type_labels[slot].setVisible(True)
self.type_labels[slot].setText(QPoke.types[type_id][1])
self.type_labels[slot].setToolTip(QPoke.types[type_id][0])
evolutions = self.make_evolution(pokemon['evolution_id'])
self.tableWidget.clear()
self.tableWidget.setColumnCount(4)
self.tableWidget.setRowCount(len(evolutions))
for i, e in enumerate(evolutions):
item = QtWidgets.QTableWidgetItem(f'{e[0]} (#{e[1]:04d})')
self.tableWidget.setItem(i, 0, item)
item = QtWidgets.QTableWidgetItem('to')
self.tableWidget.setItem(i, 1, item)
item = QtWidgets.QTableWidgetItem(f'{e[2]} (#{e[3]:04d})')
self.tableWidget.setItem(i, 2, item)
item = QtWidgets.QTableWidgetItem(f'{e[4]}')
self.tableWidget.setItem(i, 3, item)
self.tableWidget.resizeColumnsToContents()
self.tableWidget.resizeRowsToContents()
if form['full_name_en'] is not None:
return form['full_name_en']
elif form['form_en'] is not None:
return f"{species['name_en']} ({form['form_en']})"
else:
return species['name_en']
@cache
def get_pokedex(self, pokedex_id):
connection = get_connection(self._db)
cursor = connection.cursor()
cursor.execute(
'SELECT pokemon_list.*, pp.entry_number FROM pokemon_list '
'JOIN pokedex_pokemon pp ON pp.species_id = pokemon_list.species_id '
'WHERE pp.pokedex_id = ? '
'ORDER BY pp.entry_number',
(pokedex_id,)
)
res = cursor.fetchall()
connection.close()
return res
@cache
def get_abilities(self, pokemon_id):
conn = get_connection(self._db)
cursor = conn.cursor()
cursor.execute(
'SELECT slot, ís_hidden, name_de, name_en FROM pokemon_ability '
'JOIN main.ability ON pokemon_ability.ability_id = ability.id '
'WHERE pokemon_id = ?',
(pokemon_id,)
)
abilities = cursor.fetchall()
conn.close()
return abilities
@cache
def get_pokemon(self, poke_id):
connection = get_connection(self._db)
cursor = connection.cursor()
cursor.execute(
'SELECT p.species_id, p.height, p.weight, p.artwork, p.evolution_id FROM pokemon p WHERE p.id = ?',
(poke_id,)
)
pokemon = cursor.fetchone()
connection.close()
return pokemon
@cache
def get_species(self, species_id):
connection = get_connection(self._db)
cursor = connection.cursor()
cursor.execute(
'SELECT s.id, s.name_en, s.name_de, s.genus_de, s.genus_en, s.color_id, s.is_mythical, s.is_legendary, s.generation, s.gender_ratio '
'FROM species s '
'WHERE s.id = ?',
(species_id,)
)
species = cursor.fetchone()
connection.close()
return species
@cache
def get_stats(self, pokemon_id):
conn = get_connection(self._db)
cursor = conn.cursor()
cursor.execute(
'SELECT stat_id, value FROM pokemon_stat WHERE pokemon_id = ?',
(pokemon_id,)
)
stats = cursor.fetchall()
conn.close()
return stats
@cache
def get_form_and_type(self, pokemon_id):
conn = get_connection(self._db)
cursor = conn.cursor()
cursor.execute(
'SELECT id, full_name_en, form_en FROM form WHERE pokemon_id = ? AND is_default = 1',
(pokemon_id,)
)
form = cursor.fetchone()
cursor.execute(
'SELECT type_id, slot FROM form_type WHERE form_id = ?',
(form['id'],)
)
types = cursor.fetchall()
conn.close()
return form, types
@cache
def make_evolution(self, poke_id: int):
steps = []
conn = get_connection(self._db)
cursor = conn.cursor()
cursor.execute('SELECT * FROM evolution_names WHERE id = ?', (poke_id,))
chain = cursor.fetchall()
conn.close()
trigger_texts = [
None,
'Level up',
'Trade',
'',
'Empty spot in party',
'Spin',
'Train in the Tower of Darkness',
'Train in the Tower of Water',
'Land three critical hits in a battle',
'Go somewhere after taking damage',
'',
'Use Psyshield Bash 20 times in Agile Style',
'Use Barb Barrage 20 times in Strong Style',
'Receive 294 recoil damage in battle',
]
special_pokemon = {
24: 'Use Rage Fist 20 times',
317: "Defeat 3 Bisharp that are holding Leader's Crest",
528: 'Collect 999 Coins from Roaming Form',
484: "Walk 1,000 steps in Let's Go mode",
485: "Walk 1,000 steps in Let's Go mode",
495: "Walk 1,000 steps in Let's Go mode",
499: "Walk 1,000 steps in Let's Go mode",
504: "Level up while in multiplayer"
}
condition_text = {
'min_level': lambda x: f"Lv. {x['min_level']}",
'min_happiness': lambda _: f"high Friendship",
'min_beauty': lambda _: f"needs max. Beauty",
'min_affection': lambda x: f"{x['min_affection']} Affection",
'location_en': lambda x: f"at {x['location_en']}",
'held_item_en': lambda x: f"hold {x['held_item_en']}",
'item_en': lambda x: f'Use {x["item_en"]}',
'known_move_en': lambda x: f"know {x['known_move_en']}",
'move_type_en': lambda x: f"know {x['move_type_en']} move",
'party_species_en': lambda x: f"{x['party_species_en']} in party",
'party_type_en': lambda x: f"{x['party_type_en']} in party",
'time_of_day': lambda x: f"at {x['time_of_day']}",
'trade_species_en': lambda x: f"with {x['trade_species_en']}",
'needs_rain': lambda _: 'during rain',
'upside_down': lambda _: 'hold controller upside-down',
'relative_stats': lambda x: {1: 'attack &gt; defense', 0: 'attack = defense', -1: 'attack &lt; defense'}[x['relative_stats']],
}
for c in chain:
lvl0 = c["name_en"]
if c['gender'] == 1:
lvl0 += ' (female)'
elif c['gender'] == 2:
lvl0 += ' (male)'
trig = c['trigger']
if trig == 10:
level_text = [special_pokemon[poke_id]]
else:
level_text = [trigger_texts[trig]]
for k, v in condition_text.items():
if c[k] is not None:
level_text.append(v(c))
steps.append(
(lvl0, c['evolves_from'], c['evolve_en'], c['species_id'], ', '.join(filter(lambda x: x, level_text)))
)
return steps

View File

@ -34,9 +34,9 @@ class SciSpinBox(QtWidgets.QDoubleSpinBox):
new_value = self._prev_value new_value = self._prev_value
if new_value != 0.0: if new_value != 0.0:
new_value *= 10**(step/19.) new_value *= 10**(step/99.)
else: else:
new_value = 0.001 new_value = 0.00001
self.setValue(new_value) self.setValue(new_value)
self.lineEdit().setText(f'{new_value:.3e}') self.lineEdit().setText(f'{new_value:.3e}')

288
src/gui_qt/lib/update.py Normal file
View File

@ -0,0 +1,288 @@
from __future__ import annotations
import hashlib
import os
import subprocess
import urllib.request
from datetime import datetime
from os import getenv, stat
from os.path import exists
from pathlib import Path
from urllib.error import HTTPError
from PyQt5 import QtWidgets, QtCore
from nmreval.lib.logger import logger
class UpdateDialog(QtWidgets.QDialog):
startDownload = QtCore.pyqtSignal(tuple)
restartSignal = QtCore.pyqtSignal()
def __init__(self, filename: str = None, parent=None):
super().__init__(parent=parent)
self._init_ui()
if filename is None:
filename = getenv('APPIMAGE')
self._appfile = filename
self.updater = Updater()
self.thread = QtCore.QThread(self)
self.thread.start()
self.helper = Downloader()
self.startDownload.connect(self.helper.run_download)
self.helper.progressChanged.connect(self.status.setText)
self.helper.finished.connect(self.finish_update)
self.helper.started.connect(self.status.show)
self.helper.moveToThread(self.thread)
self.look_for_updates(self._appfile)
def _init_ui(self):
self.setWindowTitle('Updates')
layout = QtWidgets.QVBoxLayout()
self.label = QtWidgets.QLabel()
layout.addWidget(self.label)
layout.addSpacing(10)
self.status = QtWidgets.QLabel()
self.status.hide()
layout.addWidget(self.status)
layout.addSpacing(10)
self.dialog_button = QtWidgets.QDialogButtonBox()
self.dialog_button.accepted.connect(self.update_appimage)
self.dialog_button.rejected.connect(self.close)
layout.addWidget(self.dialog_button)
self.setLayout(layout)
def look_for_updates(self, filename=None):
logger.info(f'Looking for updates, compare to file {filename}')
# Download zsync file of latest Appimage, look for SHA-1 hash and compare with hash of AppImage
is_updateble, m_time_file, m_time_zsync = self.updater.get_update_information(filename)
label_text = ''
if is_updateble is None:
label_text += '<p>Could not determine if this version is newer, please update manually (if necessary).</p>'
dialog_bttns = QtWidgets.QDialogButtonBox.Close
elif is_updateble:
label_text += '<p>Different version available. Press Ok to download this version, Cancel to ignore.</p>'
dialog_bttns = QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel
else:
label_text += '<p>Version may be already up-to-date.</p>'
dialog_bttns = QtWidgets.QDialogButtonBox.Close
if m_time_zsync is None:
label_text += '<p>Creation date of remote version is unknown.</p>'
else:
label_text += f'<p>Date of most recent AppImage: {m_time_zsync.strftime("%d %B %Y %H:%M")}</p>'
if m_time_file is None:
label_text += 'No AppImage file found, press Ok to download latest version.'
dialog_bttns = QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Close
else:
label_text += f'<p>Date of used AppImage: {m_time_file.strftime("%d %B %Y %H:%M")}</p>'
self.label.setText(label_text)
self.dialog_button.setStandardButtons(dialog_bttns)
@QtCore.pyqtSlot()
def update_appimage(self):
if self._appfile is None:
args = (self.updater.zsync_url,)
else:
# this breaks the download for some reason
args = (self.updater.zsync_url, self._appfile)
self.dialog_button.setEnabled(False)
self.startDownload.emit(args)
self.status.show()
@QtCore.pyqtSlot(int, str)
def finish_update(self, retcode: int, file_loc: str):
restart = QRestartWindow(state=retcode, file_loc=file_loc, parent=self)
res = restart.exec()
self.close()
if res == QtWidgets.QMessageBox.Ok:
self.restartSignal.emit()
def closeEvent(self, evt):
self.thread.quit()
self.thread.wait()
super().closeEvent(evt)
class QRestartWindow(QtWidgets.QMessageBox):
def __init__(self, state: int, file_loc: str, parent=None):
super().__init__(parent=parent)
self._appfile = file_loc
if state:
self.setText('Download failed')
self.setDetailedText(f'Status code of failure is {state}')
self.setStandardButtons(QtWidgets.QMessageBox.Close)
self.setIcon(QtWidgets.QMessageBox.Warning)
else:
self.setText('Download completed!')
self.setInformativeText("Press Restart to use new AppImage")
self.setDetailedText(f'Location of AppImage: {file_loc}')
self.setIcon(QtWidgets.QMessageBox.Information)
self.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Close)
restart_button = self.button(QtWidgets.QMessageBox.Ok)
restart_button.setText('Restart')
self.buttonClicked.connect(self.maybe_close)
def maybe_close(self):
if self.clickedButton() == self.button(QtWidgets.QMessageBox.Ok):
app = QtWidgets.QApplication.instance()
app.quit()
subprocess.Popen(self._appfile)
class Downloader(QtCore.QObject):
started = QtCore.pyqtSignal()
finished = QtCore.pyqtSignal(int, str)
progressChanged = QtCore.pyqtSignal(str)
@QtCore.pyqtSlot(tuple)
def run_download(self, args: tuple[str]):
status = 0
appimage_location = args[0][:-6]
logger.info(f'Download {appimage_location}')
if len(args) == 2:
new_file = Path(args[1])
else:
new_file = Path.home() / 'Downloads' / 'NMReval-latest-x86_64.AppImage'
if new_file.exists():
os.rename(new_file, new_file.with_suffix('.AppImage.old'))
try:
with urllib.request.urlopen(appimage_location) as response:
with new_file.open('wb') as f:
f.write(response.read())
new_file.chmod(0o755)
except HTTPError as e:
logger.exception(f'Download failed with {e}')
status = 3
except Exception as e:
logger.exception(f'Download failed with {e.args}')
status = 1
if status != 0:
logger.warning('Download failed, restore previous AppImage')
try:
os.rename(new_file.with_suffix('.AppImage.old'), new_file)
except FileNotFoundError:
pass
# zsync does not support https
self.finished.emit(status, str(new_file))
class Updater:
host = 'gitea.pkm.physik.tu-darmstadt.de/api/packages/IPKM/generic/NMReval/latest/'
version = 'NMReval-latest-x86_64'
@property
def zsync_url(self):
return f'https://{Updater.host}/{Updater.version}.AppImage.zsync'
@staticmethod
def get_zsync():
url_zsync = f'https://{Updater.host}/{Updater.version}.AppImage.zsync'
m_time_zsync = None
checksum_zsync = None
zsync_file = None
filename = None
try:
with urllib.request.urlopen(url_zsync) as response:
zsync_file = response.read()
except HTTPError as e:
logger.error(f'Request for zsync returned code {e}')
except Exception as e:
logger.exception(f'Download of zsync failed with exception {e.args}')
if zsync_file is not None:
for line in zsync_file.split(b'\n'):
try:
kw, val = line.split(b': ')
time_string = str(val, encoding='utf-8')
time_format = '%a, %d %b %Y %H:%M:%S %z'
if kw == b'MTime':
try:
m_time_zsync = datetime.strptime(time_string, time_format).astimezone(None)
except ValueError:
logger.warning(f'zsync time "{time_string}" does not match "{time_format}"')
elif kw == b'SHA-1':
checksum_zsync = str(val, encoding='utf-8')
elif kw == b'Filename':
filename = str(val, encoding='utf-8')
except ValueError:
# stop when empty line is reached
break
return m_time_zsync, checksum_zsync, filename
@staticmethod
def get_appimage_info(filename: str):
if filename is None:
return None, None
if not exists(filename):
return None, None
stat_mtime = stat(filename).st_mtime
m_time_file = datetime.fromtimestamp(stat_mtime).replace(microsecond=0)
with open(filename, 'rb') as f:
checksum_file = hashlib.sha1(f.read()).hexdigest()
if checksum_file is None:
logger.warning('No checksum for AppImage calculated')
return m_time_file, checksum_file
@staticmethod
def get_update_information(filename: str) -> tuple[(bool | None), datetime, datetime]:
m_time_zsync, checksum_zsync, appname = Updater.get_zsync()
m_time_file, checksum_file = Updater.get_appimage_info(filename)
logger.debug(f'zsync information {m_time_zsync}, {checksum_zsync}, {appname}')
logger.debug(f'file information {m_time_file}, {checksum_file}')
if not ((checksum_file is not None) and (checksum_zsync is not None)):
return None, m_time_file, m_time_zsync
else:
return checksum_file != checksum_zsync, m_time_file, m_time_zsync
if __name__ == '__main__':
import sys
from gui_qt import App
app = App(['Team Rocket FTW!'])
updater = UpdateDialog()
updater.show()
sys.exit(app.exec())

View File

@ -1,27 +1,16 @@
from __future__ import annotations from __future__ import annotations
import os
import urllib.request
from os import getenv, stat
from os.path import exists
import hashlib
import subprocess
from datetime import datetime
from contextlib import contextmanager from contextlib import contextmanager
from pathlib import Path
from urllib.error import HTTPError
from numpy import linspace from numpy import linspace
from scipy.interpolate import interp1d from scipy.interpolate import interp1d
from nmreval.lib.logger import logger
from ..Qt import QtGui, QtWidgets, QtCore from ..Qt import QtGui, QtWidgets, QtCore
@contextmanager @contextmanager
def busy_cursor(): def busy_cursor():
try: try:
cursor = QtGui.QCursor(QtCore.Qt.ForbiddenCursor) cursor = QtGui.QCursor(QtCore.Qt.CursorShape.ForbiddenCursor)
QtWidgets.QApplication.setOverrideCursor(cursor) QtWidgets.QApplication.setOverrideCursor(cursor)
yield yield
@ -63,225 +52,3 @@ class RdBuCMap:
return col return col
class UpdateDialog(QtWidgets.QDialog):
startDownload = QtCore.pyqtSignal(tuple)
def __init__(self, filename: str = None, parent=None):
super().__init__(parent=parent)
self._init_ui()
if filename is None:
filename = getenv('APPIMAGE')
self._appfile = filename
self.updater = Updater()
self.thread = QtCore.QThread(self)
self.thread.start()
self.helper = Downloader()
self.startDownload.connect(self.helper.run_download)
self.helper.progressChanged.connect(self.status.setText)
self.helper.finished.connect(self.finish_update)
self.helper.started.connect(self.status.show)
self.helper.moveToThread(self.thread)
self.look_for_updates(self._appfile)
def _init_ui(self):
self.setWindowTitle('Updates')
layout = QtWidgets.QVBoxLayout()
self.label = QtWidgets.QLabel()
layout.addWidget(self.label)
layout.addSpacing(10)
self.status = QtWidgets.QLabel()
self.status.hide()
layout.addWidget(self.status)
layout.addSpacing(10)
self.dialog_button = QtWidgets.QDialogButtonBox()
self.dialog_button.accepted.connect(self.update_appimage)
self.dialog_button.rejected.connect(self.close)
layout.addWidget(self.dialog_button)
self.setLayout(layout)
def look_for_updates(self, filename=None):
logger.info(f'Looking for updates, compare to file {filename}')
# Download zsync file of latest Appimage, look for SHA-1 hash and compare with hash of AppImage
is_updateble, m_time_file, m_time_zsync = self.updater.get_update_information(filename)
label_text = ''
if is_updateble is None:
label_text += '<p>Could not determine if this version is newer, please update manually (if necessary).</p>'
dialog_bttns = QtWidgets.QDialogButtonBox.Close
elif is_updateble:
label_text += '<p>Newer version available. Press Ok to download new version, Cancel to ignore.</p>'
dialog_bttns = QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel
else:
label_text += '<p>Version may be already up-to-date.</p>'
dialog_bttns = QtWidgets.QDialogButtonBox.Close
if m_time_zsync is None:
label_text += '<p>Creation date of remote version is unknown.</p>'
else:
label_text += f'<p>Date of most recent AppImage: {m_time_zsync.strftime("%d %B %Y %H:%M")}</p>'
if m_time_file is None:
label_text += 'No AppImage file found, press Ok to download latest version.'
dialog_bttns = QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Close
else:
label_text += f'<p>Date of used AppImage: {m_time_file.strftime("%d %B %Y %H:%M")}</p>'
self.label.setText(label_text)
self.dialog_button.setStandardButtons(dialog_bttns)
@QtCore.pyqtSlot()
def update_appimage(self):
if self._appfile is None:
args = (self.updater.zsync_url,)
else:
# this breaks the download for some reason
args = (self.updater.zsync_url, self._appfile)
self.dialog_button.setEnabled(False)
self.startDownload.emit(args)
self.status.show()
@QtCore.pyqtSlot(int, str)
def finish_update(self, retcode: int, file_loc: str):
if retcode == 0:
self.status.setText(f'Download complete.New AppImage lies in <p><em>{file_loc}</em>.</p>')
else:
self.status.setText(f'Download failed :( with return code {retcode}.')
self.dialog_button.setStandardButtons(QtWidgets.QDialogButtonBox.Close)
self.dialog_button.setEnabled(True)
def closeEvent(self, evt):
self.thread.quit()
self.thread.wait()
super().closeEvent(evt)
class Downloader(QtCore.QObject):
started = QtCore.pyqtSignal()
finished = QtCore.pyqtSignal(int, str)
progressChanged = QtCore.pyqtSignal(str)
@QtCore.pyqtSlot(tuple)
def run_download(self, args: tuple[str]):
status = 0
appimage_location = args[0][:-6]
logger.info(f'Download {appimage_location}')
if len(args) == 2:
new_file = Path(args[1])
else:
new_file = Path.home() / 'Downloads' / 'NMReval-latest-x86_64.AppImage'
if new_file.exists():
os.rename(new_file, new_file.with_suffix('.AppImage.old'))
try:
with urllib.request.urlopen(appimage_location) as response:
with new_file.open('wb') as f:
f.write(response.read())
new_file.chmod(0o755)
except HTTPError as e:
logger.exception(f'Download failed with {e}')
status = 3
except Exception as e:
logger.exception(f'Download failed with {e.args}')
status = 1
if status != 0:
logger.warning('Download failed, restore previous AppImage')
try:
os.rename(new_file.with_suffix('.AppImage.old'), new_file)
except FileNotFoundError:
pass
# zsync does not support https
self.finished.emit(status, str(new_file))
class Updater:
host = 'gitea.pkm.physik.tu-darmstadt.de/api/packages/IPKM/generic/NMReval/latest/'
version = 'NMReval-latest-x86_64'
@property
def zsync_url(self):
return f'https://{Updater.host}/{Updater.version}.AppImage.zsync'
@staticmethod
def get_zsync():
url_zsync = f'https://{Updater.host}/{Updater.version}.AppImage.zsync'
m_time_zsync = None
checksum_zsync = None
zsync_file = None
filename = None
try:
with urllib.request.urlopen(url_zsync) as response:
zsync_file = response.read()
except HTTPError as e:
logger.error(f'Request for zsync returned code {e}')
except Exception as e:
logger.exception(f'Download of zsync failed with exception {e.args}')
if zsync_file is not None:
for line in zsync_file.split(b'\n'):
try:
kw, val = line.split(b': ')
time_string = str(val, encoding='utf-8')
time_format = '%a, %d %b %Y %H:%M:%S %z'
if kw == b'MTime':
try:
m_time_zsync = datetime.strptime(time_string, time_format).astimezone(None)
except ValueError:
logger.warning(f'zsync time "{time_string}" does not match "{time_format}"')
elif kw == b'SHA-1':
checksum_zsync = str(val, encoding='utf-8')
elif kw == b'Filename':
filename = str(val, encoding='utf-8')
except ValueError:
# stop when empty line is reached
break
return m_time_zsync, checksum_zsync, filename
@staticmethod
def get_appimage_info(filename: str):
if filename is None:
return None, None
if not exists(filename):
return None, None
stat_mtime = stat(filename).st_mtime
m_time_file = datetime.fromtimestamp(stat_mtime).replace(microsecond=0)
with open(filename, 'rb') as f:
checksum_file = hashlib.sha1(f.read()).hexdigest()
if checksum_file is None:
logger.warning('No checksum for AppImage calculated')
return m_time_file, checksum_file
@staticmethod
def get_update_information(filename: str) -> tuple[(bool | None), datetime, datetime]:
m_time_zsync, checksum_zsync, appname = Updater.get_zsync()
m_time_file, checksum_file = Updater.get_appimage_info(filename)
logger.debug(f'zsync information {m_time_zsync}, {checksum_zsync}, {appname}')
logger.debug(f'file information {m_time_file}, {checksum_file}')
if not ((checksum_file is not None) and (checksum_zsync is not None)):
return None, m_time_file, m_time_zsync
else:
return checksum_file != checksum_zsync, m_time_file, m_time_zsync

View File

@ -0,0 +1,3 @@

View File

@ -1,6 +1,5 @@
from __future__ import annotations from __future__ import annotations
import datetime
import os import os
import re import re
from pathlib import Path from pathlib import Path
@ -13,17 +12,18 @@ from nmreval.lib.logger import logger
from nmreval.io.sessionwriter import NMRWriter from nmreval.io.sessionwriter import NMRWriter
from .management import UpperManagement from .management import UpperManagement
from ..lib.logger import QTextHandler
from ..Qt import QtGui, QtPrintSupport from ..Qt import QtGui, QtPrintSupport
from ..data.shift_graphs import QShift from ..data.shift_graphs import QShift
from ..data.signaledit import QPreviewDialog, QBaselineDialog from ..data.signaledit import QPreviewDialog, QBaselineDialog
from ..dsc.glass_dialog import TgCalculator from ..dsc.glass_dialog import TgCalculator
from ..fit.fit_toolbar import FitToolbar
from ..fit.result import FitExtension, QFitResult from ..fit.result import FitExtension, QFitResult
from ..graphs.graphwindow import QGraphWindow from ..graphs.graphwindow import QGraphWindow
from ..graphs.movedialog import QMover from ..graphs.movedialog import QMover
from ..io.fcbatchreader import QFCReader from ..io.fcbatchreader import QFCReader
from ..io.filedialog import * from ..io.filedialog import *
from ..lib.iconloading import make_action_icons, get_icon from ..lib.iconloading import make_action_icons, get_icon
from ..lib.pg_objects import RegionItem
from ..lib.starter import make_starter from ..lib.starter import make_starter
from ..math.binning import BinningWindow from ..math.binning import BinningWindow
from ..math.evaluation import QEvalDialog from ..math.evaluation import QEvalDialog
@ -33,7 +33,7 @@ from ..math.smooth import QSmooth
from ..nmr.coupling_calc import QCoupCalcDialog from ..nmr.coupling_calc import QCoupCalcDialog
from ..nmr.t1_from_tau import QRelaxCalc from ..nmr.t1_from_tau import QRelaxCalc
from .._py.basewindow import Ui_BaseWindow from .._py.basewindow import Ui_BaseWindow
from ..lib.utils import UpdateDialog, Updater from ..lib.update import UpdateDialog
class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
@ -42,7 +42,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
save_ses_sig = QtCore.pyqtSignal(str) save_ses_sig = QtCore.pyqtSignal(str)
rest_ses_sig = QtCore.pyqtSignal(str) rest_ses_sig = QtCore.pyqtSignal(str)
def __init__(self, parents=None, path=None): def __init__(self, parents=None, path=None, bck_file=None):
super().__init__(parent=parents) super().__init__(parent=parents)
if path is None: if path is None:
@ -62,6 +62,13 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.fitresult_dialog = None self.fitresult_dialog = None
self.eval = None self.eval = None
self.editor = None self.editor = None
self._interpol_dialog = None
self.fc_reader = None
self.logtext = QTextHandler(self)
logger.addHandler(self.logtext)
self.addDockWidget(QtCore.Qt.DockWidgetArea.BottomDockWidgetArea, self.logtext.console)
self.logtext.console.hide()
self.movedialog = QMover(self) self.movedialog = QMover(self)
@ -75,21 +82,16 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self._init_gui() self._init_gui()
self._init_signals() self._init_signals()
if os.getenv('APPIMAGE') is not None:
if Updater.get_update_information(os.getenv('APPIMAGE'))[0]:
self.look_for_update()
self.check_for_backup()
self.__timer = QtCore.QTimer()
self.__backup_path = config_paths() / f'{datetime.datetime.now().strftime("%Y-%m-%d_%H%M%S")}.nmr'
self.__timer.start(3*60*1000) # every three minutes
self.__timer.timeout.connect(self._autosave)
self.fit_timer = QtCore.QTimer() self.fit_timer = QtCore.QTimer()
self.fit_timer.setInterval(500) self.fit_timer.setInterval(500)
self.fit_timer.timeout.connect( self.fit_timer.timeout.connect(
lambda: self.status.setText(f'Fit running... ({self.management.fitter.step} evaluations)')) lambda: self.status.setText(f'Fit running... ({self.management.fitter.step} evaluations)')
)
self.__backup_path = pathlib.Path(bck_file)
if os.getenv('APPIMAGE'):
# ignore AppImages if not running from AppImage
self.look_for_update()
def _init_gui(self): def _init_gui(self):
self.setupUi(self) self.setupUi(self)
@ -102,14 +104,6 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.norm_toolbutton.setIcon(get_icon('normal')) self.norm_toolbutton.setIcon(get_icon('normal'))
self.toolbar_edit.addWidget(self.norm_toolbutton) 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: while self.tabWidget.count() > 2:
self.tabWidget.removeTab(self.tabWidget.count()-1) self.tabWidget.removeTab(self.tabWidget.count()-1)
@ -127,26 +121,26 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
self.statusBar.addWidget(self.mousepos) self.statusBar.addWidget(self.mousepos)
self.fitregion = RegionItem()
self._fit_plot_id = None self._fit_plot_id = None
self.setGeometry(QtWidgets.QStyle.alignedRect(QtCore.Qt.LeftToRight, QtCore.Qt.AlignCenter, self.fit_toolbar = FitToolbar(self.action_FitWidget, self.menuLimits, self)
self.size(), QtWidgets.qApp.desktop().availableGeometry())) self.addToolBar(self.fit_toolbar)
self.setGeometry(QtWidgets.QStyle.alignedRect(
QtCore.Qt.LayoutDirection.LeftToRight,
QtCore.Qt.AlignmentFlag.AlignCenter,
self.size(),
QtWidgets.qApp.desktop().availableGeometry()),
)
self.datawidget.management = self.management self.datawidget.management = self.management
self.integralwidget.management = self.management self.integralwidget.management = self.management
# self.drawingswidget.graphs = self.management.graphs
self.ac_group = QtWidgets.QActionGroup(self) self.ac_group = QtWidgets.QActionGroup(self)
self.ac_group.addAction(self.action_lm_fit) self.ac_group.addAction(self.action_lm_fit)
self.ac_group.addAction(self.action_nm_fit) self.ac_group.addAction(self.action_nm_fit)
self.ac_group.addAction(self.action_odr_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): def _init_signals(self):
self.actionRedo = self.management.undostack.createRedoAction(self) self.actionRedo = self.management.undostack.createRedoAction(self)
icon = QtGui.QIcon.fromTheme("edit-redo") icon = QtGui.QIcon.fromTheme("edit-redo")
@ -161,7 +155,8 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.menuData.insertAction(self.actionRedo, self.actionUndo) self.menuData.insertAction(self.actionRedo, self.actionUndo)
self.action_save_fit_parameter.triggered.connect(self.save_fit_parameter) self.action_save_fit_parameter.triggered.connect(self.save_fit_parameter)
self.ac_group2.triggered.connect(self.change_fit_limits) # noinspection PyUnresolvedReferences
self.fit_toolbar.limit_group.triggered.connect(self.change_fit_limits)
self.t1action.triggered.connect(lambda: self._show_tab('t1_temp')) self.t1action.triggered.connect(lambda: self._show_tab('t1_temp'))
self.action_edit.triggered.connect(self.do_preview) self.action_edit.triggered.connect(self.do_preview)
@ -201,6 +196,8 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.management.unset_state.connect(lambda x: self.datawidget.uncheck_sets(x)) self.management.unset_state.connect(lambda x: self.datawidget.uncheck_sets(x))
self.management.fitFinished.connect(self.show_fit_results) self.management.fitFinished.connect(self.show_fit_results)
self.area.newData.connect(lambda x: self.management.load_files(x))
self.fit_dialog._management = self.management self.fit_dialog._management = self.management
self.fit_dialog.preview_emit.connect(self.show_fit_preview) self.fit_dialog.preview_emit.connect(self.show_fit_preview)
self.fit_dialog.fitStartSig.connect(self.start_fit) self.fit_dialog.fitStartSig.connect(self.start_fit)
@ -233,14 +230,13 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.actionNext_window.triggered.connect(lambda: self.area.activateNextSubWindow()) self.actionNext_window.triggered.connect(lambda: self.area.activateNextSubWindow())
self.actionPrevious.triggered.connect(lambda: self.area.activatePreviousSubWindow()) 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.triggered.connect(lambda: self.management.apply('norm', ('max',)))
self.action_norm_max_abs.triggered.connect(lambda: self.management.apply('norm', ('maxabs',))) 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_first.triggered.connect(lambda: self.management.apply('norm', ('first',)))
self.action_norm_last.triggered.connect(lambda: self.management.apply('norm', ('last',))) 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_norm_area.triggered.connect(lambda: self.management.apply('norm', ('area',)))
self.action_cut.triggered.connect(lambda: self.management.cut()) self.action_cut_xaxis.triggered.connect(lambda: self.management.cut(True, False))
self.action_cut_yaxis.triggered.connect(lambda: self.management.cut(False, True))
self.actionConcatenate_sets.triggered.connect(lambda: self.management.cat()) self.actionConcatenate_sets.triggered.connect(lambda: self.management.cat())
@ -259,8 +255,11 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
filedialog.set_graphs(self.management.graphs.list()) filedialog.set_graphs(self.management.graphs.list())
filedialog.exec() accepted = filedialog.exec()
if accepted:
fname = filedialog.selectedFiles() fname = filedialog.selectedFiles()
else:
fname = []
if fname: if fname:
self.path = Path(fname[0]).parent self.path = Path(fname[0]).parent
@ -268,12 +267,15 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
@QtCore.pyqtSlot(name='on_actionOpen_FC_triggered') @QtCore.pyqtSlot(name='on_actionOpen_FC_triggered')
def read_fc(self): def read_fc(self):
reader = QFCReader(path=self.path, parent=self) if self.fc_reader is None:
reader.add_graphs(self.management.graphs.list()) self.fc_reader = QFCReader(path=self.path, parent=self)
reader.data_read.connect(self.management.add_new_data) self.fc_reader.data_read.connect(self.management.add_new_data)
reader.exec() else:
self.fc_reader(path=self.path)
self.fc_reader.add_graphs(self.management.graphs.list())
self.fc_reader.exec()
del reader self.path = self.fc_reader.path
@QtCore.pyqtSlot(name='on_actionPrint_triggered') @QtCore.pyqtSlot(name='on_actionPrint_triggered')
def print(self): def print(self):
@ -371,25 +373,29 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
val_figure = self.valuewidget.connected_figure val_figure = self.valuewidget.connected_figure
self.valuewidget.remove_graph() self.valuewidget.remove_graph()
_remove_pts = False
_remove_t1 = False
_move_to_data_tab = False
w = None w = None
for w in self.area.subWindowList(): for w in self.area.subWindowList():
wdgt = w.widget() wdgt = w.widget()
if wdgt.id == gid: if wdgt.id == gid:
wdgt.disconnect() wdgt.disconnect()
wdgt.scene.disconnect() wdgt.scene.disconnect()
if wdgt == self.current_graph_widget: if wdgt == self.current_graph_widget:
if self.ptsselectwidget.connected_figure == gid: if self.ptsselectwidget.connected_figure == gid:
self.ptsselectwidget.connected_figure = None self.ptsselectwidget.connected_figure = None
for line in self.ptsselectwidget.pts_lines: for line in self.ptsselectwidget.pts_lines:
self.current_graph_widget.remove_external(line) self.current_graph_widget.remove_external(line)
_remove_pts = True
self.tabWidget.removeTab(self.tabWidget.indexOf(self.ptsselectwidget))
if self.t1tauwidget.connected_figure == gid: if self.t1tauwidget.connected_figure == gid:
self.t1tauwidget.connected_figure = None self.t1tauwidget.connected_figure = None
self.current_graph_widget.add_external(self.t1tauwidget.min_pos) self.current_graph_widget.remove_external(self.t1tauwidget.min_pos)
self.current_graph_widget.add_external(self.t1tauwidget.parabola) self.current_graph_widget.remove_external(self.t1tauwidget.parabola)
self.tabWidget.removeTab(self.tabWidget.indexOf(self.t1tauwidget)) _remove_t1 = True
if self.fit_dialog.connected_figure == gid: if self.fit_dialog.connected_figure == gid:
self.fit_dialog.connected_figure = None self.fit_dialog.connected_figure = None
@ -397,9 +403,10 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.current_graph_widget.remove_external(item) self.current_graph_widget.remove_external(item)
if val_figure == gid: if val_figure == gid:
self.valuewidget.connected_figure = None
self.current_graph_widget.remove_external(self.valuewidget.selection_real) self.current_graph_widget.remove_external(self.valuewidget.selection_real)
self.current_graph_widget.remove_external(self.valuewidget.selection_imag) self.current_graph_widget.remove_external(self.valuewidget.selection_imag)
self.tabWidget.setCurrentIndex(0) _move_to_data_tab = True
self.current_graph_widget.enable_picking(False) self.current_graph_widget.enable_picking(False)
@ -415,6 +422,13 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
break break
if _remove_t1:
self.tabWidget.removeTab(self.tabWidget.indexOf(self.t1tauwidget))
if _remove_pts:
self.tabWidget.removeTab(self.tabWidget.indexOf(self.ptsselectwidget))
if _move_to_data_tab:
self.tabWidget.setCurrentIndex(0)
if w is not None: if w is not None:
self.area.removeSubWindow(w) self.area.removeSubWindow(w)
w.close() w.close()
@ -452,6 +466,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
w.mousePositionChanged.connect(self.mousemoved) w.mousePositionChanged.connect(self.mousemoved)
w.aboutToClose.connect(self.management.delete_sets) w.aboutToClose.connect(self.management.delete_sets)
w.positionClicked.connect(self.point_selected) w.positionClicked.connect(self.point_selected)
w.newData.connect(lambda x, y: self.management.load_files(x, new_plot=y))
w.show() w.show()
graph_list = self.management.graphs.list() graph_list = self.management.graphs.list()
@ -564,10 +579,12 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.ptsselectwidget.connected_figure = self.management.current_graph self.ptsselectwidget.connected_figure = self.management.current_graph
pick_required = True pick_required = True
else: else:
if self.ptsselectwidget.connected_figure: if self.ptsselectwidget.connected_figure in self.management.graphs:
g = self.management.graphs[self.ptsselectwidget.connected_figure] g = self.management.graphs[self.ptsselectwidget.connected_figure]
for line in self.ptsselectwidget.pts_lines: for line in self.ptsselectwidget.pts_lines:
g.remove_external(line) g.remove_external(line)
else:
self.ptsselectwidget.connected_figure = None
# self.ptsselectwidget.clear() # self.ptsselectwidget.clear()
return pick_required, block_window return pick_required, block_window
@ -580,10 +597,12 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.management.graphs[current_graph].add_external(self.valuewidget.selection_real) self.management.graphs[current_graph].add_external(self.valuewidget.selection_real)
self.management.graphs[current_graph].add_external(self.valuewidget.selection_imag) self.management.graphs[current_graph].add_external(self.valuewidget.selection_imag)
else: else:
if self.valuewidget.connected_figure is not None: if self.valuewidget.connected_figure in self.management.graphs:
conn_fig = self.valuewidget.connected_figure conn_fig = self.valuewidget.connected_figure
self.management.graphs[conn_fig].remove_external(self.valuewidget.selection_real) self.management.graphs[conn_fig].remove_external(self.valuewidget.selection_real)
self.management.graphs[conn_fig].remove_external(self.valuewidget.selection_imag) self.management.graphs[conn_fig].remove_external(self.valuewidget.selection_imag)
else:
self.valuewidget.connected_figure = None
def _select_integralwidget(self, onoff: bool, pick_required: bool, block_window: bool) -> tuple[bool, bool]: def _select_integralwidget(self, onoff: bool, pick_required: bool, block_window: bool) -> tuple[bool, bool]:
if self.current_graph_widget is None: if self.current_graph_widget is None:
@ -597,11 +616,13 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
pick_required = True pick_required = True
block_window = True block_window = True
else: else:
if self.integralwidget.connected_figure: if self.integralwidget.connected_figure in self.management.graphs:
g = self.management.graphs[self.integralwidget.connected_figure] g = self.management.graphs[self.integralwidget.connected_figure]
for line in self.integralwidget.lines: for line in self.integralwidget.lines:
g.remove_external(line[0]) g.remove_external(line[0])
g.remove_external(line[1]) g.remove_external(line[1])
else:
self.integralwidget.connected_figure = None
self.integralwidget.clear() self.integralwidget.clear()
return pick_required, block_window return pick_required, block_window
@ -630,10 +651,12 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
pick_required = True pick_required = True
block_window = True block_window = True
else: else:
if self.t1tauwidget.connected_figure: if self.t1tauwidget.connected_figure in self.management.graphs:
g = self.management.graphs[self.t1tauwidget.connected_figure] g = self.management.graphs[self.t1tauwidget.connected_figure]
g.remove_external(self.t1tauwidget.min_pos) g.remove_external(self.t1tauwidget.min_pos)
g.remove_external(self.t1tauwidget.parabola) g.remove_external(self.t1tauwidget.parabola)
else:
self.t1tauwidget.connected_figure = None
return pick_required, block_window return pick_required, block_window
@ -654,8 +677,9 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
from ..math.skipping import QSkipDialog from ..math.skipping import QSkipDialog
dial = QSkipDialog(self) dial = QSkipDialog(self)
dial.exec() res = dial.exec()
if res:
self.management.skip_points(**dial.get_arguments()) self.management.skip_points(**dial.get_arguments())
@QtCore.pyqtSlot(name='on_action_coup_calc_triggered') @QtCore.pyqtSlot(name='on_action_coup_calc_triggered')
@ -682,10 +706,13 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
return return
gnames = self.management.graphs.tree() gnames = self.management.graphs.tree()
dialog = InterpolDialog(parent=self) if self._interpol_dialog is None:
dialog.set_data(gnames, self.current_graph_widget.id) self._interpol_dialog = InterpolDialog(parent=self)
dialog.new_data.connect(self.management.interpolate_data) self._interpol_dialog.new_data.connect(self.management.interpolate_data)
dialog.show() else:
self._interpol_dialog()
self._interpol_dialog.set_data(gnames, self.current_graph_widget.id)
self._interpol_dialog.show()
@QtCore.pyqtSlot(name='on_action_calc_triggered') @QtCore.pyqtSlot(name='on_action_calc_triggered')
def open_eval_dialog(self): def open_eval_dialog(self):
@ -836,10 +863,6 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
def item_from_graph(self, item, graph_id): def item_from_graph(self, item, graph_id):
self.management.graphs[graph_id].remove_external(item) self.management.graphs[graph_id].remove_external(item)
def closeEvent(self, evt):
# self._write_settings()
self.close()
@QtCore.pyqtSlot(int) @QtCore.pyqtSlot(int)
def request_data(self, idx): def request_data(self, idx):
idd = self.datawidget.get_indexes(idx=idx-1) idd = self.datawidget.get_indexes(idx=idx-1)
@ -879,30 +902,29 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.fit_dialog.load(self.management.active_sets) self.fit_dialog.load(self.management.active_sets)
for item in self.fit_dialog.preview_lines: for item in self.fit_dialog.preview_lines:
self.current_graph_widget.add_external(item) self.current_graph_widget.add_external(item)
if self.action_custom_range.isChecked(): if self.action_custom_range.isChecked() or self.actionExclude_region.isChecked():
self.current_graph_widget.add_external(self.fitregion) self.current_graph_widget.add_external(self.fit_toolbar.region)
block_window = True block_window = True
else: else:
for item in self.fit_dialog.preview_lines: for item in self.fit_dialog.preview_lines:
self.current_graph_widget.remove_external(item) self.current_graph_widget.remove_external(item)
self.current_graph_widget.remove_external(self.fitregion) self.current_graph_widget.remove_external(self.fit_toolbar.region)
return block_window return block_window
@QtCore.pyqtSlot(QtWidgets.QAction) @QtCore.pyqtSlot(QtWidgets.QAction)
def change_fit_limits(self, action: QtWidgets.QAction): def change_fit_limits(self, action: QtWidgets.QAction):
if action == self.action_custom_range and self.fit_dialog.isVisible(): if self.current_graph_widget is None:
self.current_graph_widget.add_external(self.fitregion) return
if action in [self.action_custom_range, self.actionExclude_region] and self.fit_dialog.isVisible():
self.current_graph_widget.add_external(self.fit_toolbar.region)
else: else:
self.current_graph_widget.remove_external(self.fitregion) self.current_graph_widget.remove_external(self.fit_toolbar.region)
def start_fit(self, parameter, links, fit_options): def start_fit(self, parameter, links, fit_options):
fit_options['limits'] = { fit_options['limits'] = self.fit_toolbar.get_limit()
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'] = { fit_options['fit_mode'] = {
self.action_lm_fit: 'lsq', self.action_lm_fit: 'lsq',
@ -938,19 +960,19 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
for item in self.fit_dialog.preview_lines: for item in self.fit_dialog.preview_lines:
g.add_external(item) g.add_external(item)
@QtCore.pyqtSlot(list) @QtCore.pyqtSlot(list, dict)
def show_fit_results(self, results: list): def show_fit_results(self, results: list, sub_colors: dict[str, tuple[float, float, float]]):
self.fit_dialog.fit_button.setEnabled(True) self.fit_dialog.fit_button.setEnabled(True)
self.fit_timer.stop() self.fit_timer.stop()
self.status.setText('') self.status.setText('')
if results: if results:
if self.fitresult_dialog is None: if self.fitresult_dialog is None:
self.fitresult_dialog = QFitResult(results, self.management, parent=self) self.fitresult_dialog = QFitResult(results, sub_colors, self.management, parent=self)
self.fitresult_dialog.add_graphs(self.management.graphs.list()) self.fitresult_dialog.add_graphs(self.management.graphs.list())
self.fitresult_dialog.closed.connect(self.accepts_fit) self.fitresult_dialog.closed.connect(self.accepts_fit)
self.fitresult_dialog.redoFit.connect(self.management.redo_fits) self.fitresult_dialog.redoFit.connect(self.management.redo_fits)
else: else:
self.fitresult_dialog(results) self.fitresult_dialog(results, sub_colors)
self.fitresult_dialog.add_graphs(self.management.graphs.list()) self.fitresult_dialog.add_graphs(self.management.graphs.list())
self.fitresult_dialog.show() self.fitresult_dialog.show()
@ -963,21 +985,34 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
@QtCore.pyqtSlot(name='on_actionFunction_editor_triggered') @QtCore.pyqtSlot(name='on_actionFunction_editor_triggered')
def edit_models(self): def edit_models(self):
if self.editor is None: if self.editor is None:
from ..lib.usermodeleditor import QUsermodelEditor from ..editors.usermodeleditor import QUsermodelEditor
self.editor = QUsermodelEditor(config_paths() / 'usermodels.py', parent=self) self.editor = QUsermodelEditor(config_paths() / 'usermodels.py', parent=self)
self.editor.modelsChanged.connect(lambda: self.fit_dialog.read_and_load_functions()) self.editor.modelsChanged.connect(lambda: self.fit_dialog.read_and_load_functions())
self.editor.setWindowModality(QtCore.Qt.ApplicationModal) self.editor.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal)
self.editor.show() self.editor.show()
@QtCore.pyqtSlot(list) @QtCore.pyqtSlot(name='on_actionUse_script_triggered')
def extend_fit(self, sets: list): def open_editor(self):
from ..editors.script_editor import QEditor
editor = QEditor(self.path, parent=self)
editor.runSignal.connect(self.management.run_script)
editor.show()
@QtCore.pyqtSlot(list, bool)
def extend_fit(self, sets: list, only_subplots: bool):
if only_subplots:
self.management.extend_fits(sets, None, True)
return
w = FitExtension(self) w = FitExtension(self)
res = w.exec() res = w.exec()
if res: if res:
p = w.values p = w.values
x = linspace(p[0], p[1], num=p[2]) spacefunc = geomspace if p[3] else linspace
self.management.extend_fits(sets, x) x = spacefunc(p[0], p[1], num=p[2])
self.management.extend_fits(sets, x, False)
@QtCore.pyqtSlot(name='on_action_create_fit_function_triggered') @QtCore.pyqtSlot(name='on_action_create_fit_function_triggered')
def open_fitmodel_wizard(self): def open_fitmodel_wizard(self):
@ -1001,9 +1036,10 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
@staticmethod @staticmethod
@QtCore.pyqtSlot(name='on_actionDocumentation_triggered') @QtCore.pyqtSlot(name='on_actionDocumentation_triggered')
def open_doc(): def open_doc():
docpath = '/autohome/dominik/auswerteprogramm3/doc/_build/html/index.html' pass
import webbrowser # docpath = '/autohome/dominik/auswerteprogramm3/doc/_build/html/index.html'
webbrowser.open(docpath) # import webbrowser
# webbrowser.open(docpath)
def dropEvent(self, evt): def dropEvent(self, evt):
if evt.mimeData().hasUrls(): if evt.mimeData().hasUrls():
@ -1032,13 +1068,13 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
if self.sender() == self.actionLife: if self.sender() == self.actionLife:
from ..lib.gol import QGameOfLife from ..lib.gol import QGameOfLife
game = QGameOfLife(parent=self) game = QGameOfLife(parent=self)
game.setWindowModality(QtCore.Qt.NonModal) game.setWindowModality(QtCore.Qt.WindowModality.NonModal)
game.show() game.show()
elif self.sender() == self.actionMine: elif self.sender() == self.actionMine:
from ..lib.stuff import QMines from ..lib.stuff import QMines
game = QMines(parent=self) game = QMines(parent=self)
game.setWindowModality(QtCore.Qt.NonModal) game.setWindowModality(QtCore.Qt.WindowModality.NonModal)
game.show() game.show()
else: else:
@ -1066,9 +1102,6 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
def close(self): def close(self):
write_state({'History': {'recent path': str(self.path)}}) write_state({'History': {'recent path': str(self.path)}})
# remove backup file when closing
self.__backup_path.unlink(missing_ok=True)
super().close() super().close()
def read_state(self): def read_state(self):
@ -1092,40 +1125,16 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
QLog(parent=self).show() QLog(parent=self).show()
def _autosave(self): def autosave(self) -> bool:
# TODO better separate thread may it takes some time to save
self.status.setText('Autosave...') self.status.setText('Autosave...')
success = NMRWriter(self.management.graphs, self.management.data).export(self.__backup_path.with_suffix('.nmr.0')) success = NMRWriter(self.management.graphs, self.management.data).export(self.__backup_path.with_suffix('.nmr.0'))
if success: if success:
self.__backup_path.with_suffix('.nmr.0').rename(self.__backup_path) self.__backup_path.with_suffix('.nmr.0').rename(self.__backup_path)
self.status.setText('') self.status.setText('')
def check_for_backup(self): return success
backups = []
for filename in config_paths().glob('*.nmr'):
try:
backups.append((filename, datetime.datetime.strptime(filename.stem, "%Y-%m-%d_%H%M%S")))
except ValueError:
continue
if backups:
backup_by_date = sorted(backups, key=lambda x: x[1])
msg = QtWidgets.QMessageBox.information(
self, 'Backup found',
f'{len(backups)} backup files in directory {backup_by_date[-1][0].parent} found.\n\n'
f'Latest backup date: {backup_by_date[-1][1]}\n\n'
f'Press Ok to load, Cancel to delete backup, Close to do nothing.',
QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel | QtWidgets.QMessageBox.Close
)
if msg == QtWidgets.QMessageBox.Ok:
self.management.load_files([str(backup_by_date[-1][0])])
backup_by_date[-1][0].unlink()
elif msg == QtWidgets.QMessageBox.Cancel:
backup_by_date[-1][0].unlink()
else:
pass
@QtCore.pyqtSlot(name='on_actionCreate_starter_triggered') @QtCore.pyqtSlot(name='on_actionCreate_starter_triggered')
def create_starter(self): def create_starter(self):

View File

@ -85,7 +85,7 @@ class UpperManagement(QtCore.QObject):
newData = QtCore.pyqtSignal([list, str], [list, str, bool]) newData = QtCore.pyqtSignal([list, str], [list, str, bool])
deleteData = QtCore.pyqtSignal(list) deleteData = QtCore.pyqtSignal(list)
dataChanged = QtCore.pyqtSignal(str) dataChanged = QtCore.pyqtSignal(str)
fitFinished = QtCore.pyqtSignal(list) fitFinished = QtCore.pyqtSignal(list, dict)
stopFit = QtCore.pyqtSignal() stopFit = QtCore.pyqtSignal()
properties_collected = QtCore.pyqtSignal(dict) properties_collected = QtCore.pyqtSignal(dict)
unset_state = QtCore.pyqtSignal(list) unset_state = QtCore.pyqtSignal(list)
@ -279,13 +279,16 @@ class UpperManagement(QtCore.QObject):
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
@QtCore.pyqtSlot(list, str) @QtCore.pyqtSlot(list, str)
def copy_sets(self, sets: list = None, src: str = None): def copy_sets(self, sets: list = None, src: str = None, dest: str = None):
if sets is None:
sets = self.graphs[self.current_graph].active[:]
if src is None: if src is None:
src = self.current_graph src = self.current_graph
if sets is None:
sets = self.graphs[src].active[:]
if dest is None:
dest = src
new_ids = [] new_ids = []
for s in sets: for s in sets:
copy_of_s = self.data[s].copy(full=True) copy_of_s = self.data[s].copy(full=True)
@ -293,10 +296,23 @@ class UpperManagement(QtCore.QObject):
new_ids.append(copy_of_s.id) new_ids.append(copy_of_s.id)
self.data[copy_of_s.id] = copy_of_s self.data[copy_of_s.id] = copy_of_s
self.newData.emit(new_ids, src) self.newData.emit(new_ids, dest)
return new_ids return new_ids
def copy_graph(self, gid):
# Use state of old graph but removes actual references to old graph
src_state = self.graphs[gid].get_state()
src_state.pop('id')
src_state['children'] = []
src_state['active'] = []
new_graph = QGraphWindow.set_state(src_state)
self.graphs[new_graph.id] = new_graph
self.restoreGraph.emit(new_graph.id)
self.copy_sets(src=gid, dest=new_graph.id)
@QtCore.pyqtSlot(list) @QtCore.pyqtSlot(list)
@QtCore.pyqtSlot(str) @QtCore.pyqtSlot(str)
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
@ -314,18 +330,22 @@ class UpperManagement(QtCore.QObject):
if k in self.data: if k in self.data:
parent_graph = self.data[k].graph parent_graph = self.data[k].graph
if parent_graph not in rm_set_by_graph: if parent_graph not in rm_set_by_graph:
rm_set_by_graph[parent_graph] = [] rm_set_by_graph[parent_graph] = set()
rm_set_by_graph[parent_graph].append(k) rm_set_by_graph[parent_graph].add(k)
elif k in self.graphs: elif k in self.graphs:
rm_graphs.append(k) rm_graphs.append(k)
if k not in rm_set_by_graph:
rm_set_by_graph[k] = set()
for ss in self.graphs[k].sets:
rm_set_by_graph[k].add(ss)
else: else:
logger.warning(f'delete_sets: {k} is not in data or graph found') logger.warning(f'delete_sets: {k} is not in data or graph found')
for gid, sid_list in rm_set_by_graph.items(): for gid, sid_list in rm_set_by_graph.items():
cmd = DeleteCommand(self.data, sid_list, self.graphs, gid, self.newData, self.deleteData) cmd = DeleteCommand(self.data, list(sid_list), self.graphs, gid, self.newData, self.deleteData)
self.undostack.push(cmd) self.undostack.push(cmd)
for k in rm_graphs: for k in rm_graphs:
@ -343,6 +363,7 @@ class UpperManagement(QtCore.QObject):
group_set = set() group_set = set()
name_set = set() name_set = set()
value_set = set() value_set = set()
graph_set = set()
if src_sets is None: if src_sets is None:
if self.current_graph: if self.current_graph:
@ -360,6 +381,7 @@ class UpperManagement(QtCore.QObject):
name_set.add(data_i.name) name_set.add(data_i.name)
group_set.add(data_i.group) group_set.add(data_i.group)
value_set.add(data_i.value) value_set.add(data_i.value)
graph_set.add(data_i.graph)
if joined is not None: if joined is not None:
joined.group = '+'.join(group_set) joined.group = '+'.join(group_set)
@ -370,7 +392,9 @@ class UpperManagement(QtCore.QObject):
else: else:
joined.value = 0.0 joined.value = 0.0
self.newData.emit([self.add(joined)], self.current_graph) dest_graph = graph_set.pop() if len(graph_set) == 1 else self.current_graph
self.newData.emit([self.add(joined)], dest_graph)
def get_data(self, sid: str, xy_only: bool = False): def get_data(self, sid: str, xy_only: bool = False):
""" """
@ -404,6 +428,7 @@ class UpperManagement(QtCore.QObject):
self.graphs[d.graph].update_legend(identifier, name) self.graphs[d.graph].update_legend(identifier, name)
elif identifier in self.graphs: elif identifier in self.graphs:
self.graphs[identifier].title = name self.graphs[identifier].title = name
self.graphs.valueChanged.emit()
else: else:
raise KeyError('Unknown ID ' + str(identifier)) raise KeyError('Unknown ID ' + str(identifier))
@ -425,10 +450,17 @@ class UpperManagement(QtCore.QObject):
self.undostack.push(single_undo) self.undostack.push(single_undo)
self.undostack.endMacro() self.undostack.endMacro()
def cut(self): def cut(self, x: bool = False, y: bool = False) -> None:
if self.current_graph: if self.current_graph:
xlim, _ = self.graphs[self.current_graph].ranges xlim, ylim = self.graphs[self.current_graph].ranges
self.apply('cut', xlim)
if x is False:
xlim = (None, None)
if y is False:
ylim = (None, None)
self.apply('cut', (*xlim, *ylim))
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def unmask(self): def unmask(self):
@ -449,6 +481,11 @@ class UpperManagement(QtCore.QObject):
self.fitter.fitmethod = fit_mode self.fitter.fitmethod = fit_mode
# sets are not in active order but in order they first appeared in fit dialog
# iterate over order of set id in active order and access parameter inside loop
# instead of directly looping
list_ids = list(self.active_id)
# all-encompassing error catch # all-encompassing error catch
try: try:
for model_id, model_p in parameter.items(): for model_id, model_p in parameter.items():
@ -457,27 +494,16 @@ class UpperManagement(QtCore.QObject):
m_complex = model_p['complex'] m_complex = model_p['complex']
# sets are not in active order but in order they first appeared in fit dialog for set_id in list_ids:
# iterate over order of set id in active order and access parameter inside loop if set_id not in model_p['data_parameter']:
# instead of directly looping continue
try:
list_ids = list(self.active_id)
set_order = [self.active_id.index(i) for i in model_p['data_parameter'].keys()]
except ValueError as e:
raise Exception('Getting order failed') from e
for pos in set_order:
set_id = list_ids[pos]
try: try:
data_i = self.data[set_id] data_i = self.data[set_id]
except KeyError as e: except KeyError as e:
raise KeyError(f'{set_id} not found') from e raise KeyError(f'{set_id} not found') from e
try:
set_params = model_p['data_parameter'][set_id] set_params = model_p['data_parameter'][set_id]
except KeyError as e:
raise KeyError(f'No parameter found for {set_id}') from e
if we_option.lower() == 'deltay': if we_option.lower() == 'deltay':
we = data_i.y_err**2 we = data_i.y_err**2
@ -485,29 +511,46 @@ class UpperManagement(QtCore.QObject):
we = we_option we = we_option
if m_complex is None or m_complex == 1: if m_complex is None or m_complex == 1:
# model is not complex: m_complex = None
# model is complex, fit real part: m_complex = 1
_y = data_i.y.real _y = data_i.y.real
elif m_complex == 2 and np.iscomplexobj(data_i.y): data_complex = 1
elif m_complex == 2:
# model is complex, fit imag part: m_complex = 2
if np.iscomplexobj(data_i.y):
# data is complex, use imag part
_y = data_i.y.imag _y = data_i.y.imag
data_complex = 2
else: else:
# data is real
_y = data_i.y _y = data_i.y
data_complex = 1
else:
# model is complex, fit complex: m_complex = 0
# use data as given (complex or not)
_y = data_i.y
data_complex = 0
_x = data_i.x _x = data_i.x
# options for fit limits 'none', 'x', ('in', custom region), ('out', excluded region)
if fit_limits == 'none': if fit_limits == 'none':
inside = slice(None) inside = slice(None)
elif fit_limits == 'x': elif fit_limits == 'x':
x_lim, _ = self.graphs[self.current_graph].ranges x_lim, _ = self.graphs[self.current_graph].ranges
inside = np.where((_x >= x_lim[0]) & (_x <= x_lim[1])) inside = np.where((_x >= x_lim[0]) & (_x <= x_lim[1]))
elif fit_limits[0] == 'in':
inside = np.where((_x >= fit_limits[1][0]) & (_x <= fit_limits[1][1]))
else: else:
inside = np.where((_x >= fit_limits[0]) & (_x <= fit_limits[1])) inside = np.where((_x < fit_limits[1][0]) | (_x > fit_limits[1][1]))
try: try:
if isinstance(we, str): if isinstance(we, str):
d = fit_d.Data(_x[inside], _y[inside], we=we, idx=set_id) d = fit_d.Data(_x[inside], _y[inside], we=we, idx=set_id, complex_type=data_complex)
else: else:
d = fit_d.Data(_x[inside], _y[inside], we=we[inside], idx=set_id) d = fit_d.Data(_x[inside], _y[inside], we=we[inside], idx=set_id, complex_type=data_complex)
except Exception as e: except Exception as e:
raise Exception(f'Setting data failed for {set_id}') raise Exception(f'Setting data failed for {data_i.name}') from e
d.set_model(m) d.set_model(m)
try: try:
@ -523,7 +566,7 @@ class UpperManagement(QtCore.QObject):
return True return True
except Exception as e: except Exception as e:
logger.error('Fit preparation failed', *e.args) logger.error(f'Fit preparation failed with error: {e.args}')
QtWidgets.QMessageBox.warning(QtWidgets.QWidget(), QtWidgets.QMessageBox.warning(QtWidgets.QWidget(),
'Fit prep failed', 'Fit prep failed',
f'Fit preparation failed:\n' f'Fit preparation failed:\n'
@ -551,13 +594,23 @@ class UpperManagement(QtCore.QObject):
def end_fit(self, result: list, success: bool): def end_fit(self, result: list, success: bool):
if success: if success:
logger.info('Successful fit') logger.info('Successful fit')
self.fitFinished.emit(result)
sub_colors = {}
for k, v in self.__fit_options[0].items():
sub_colors.update({set_id: v['color'] for set_id in v['data_parameter']})
self.fitFinished.emit(result, sub_colors)
else: else:
e = result[0] e = result[0]
logger.exception(e, exc_info=True) logger.exception(e, exc_info=True)
QtWidgets.QMessageBox.warning(QtWidgets.QWidget(), 'Fit failed', QtWidgets.QMessageBox.warning(
f'Fit kaput with exception: \n\n{e!r}') QtWidgets.QWidget(),
self.fitFinished.emit([]) 'Fit failed',
f'Fit kaput with exception: \n\n{e!r}'
)
self.fitFinished.emit([], {})
self._fit_active = False self._fit_active = False
@QtCore.pyqtSlot(dict) @QtCore.pyqtSlot(dict)
@ -595,7 +648,7 @@ class UpperManagement(QtCore.QObject):
continue continue
if not all(e is None for e in extrapolate): if not all(e is None for e in extrapolate):
spacefunc = np.geomspace if fit.islog else np.linspace spacefunc = np.geomspace if extrapolate[3] else np.linspace
xmin = fit.x.min() xmin = fit.x.min()
xmax = fit.x.max() xmax = fit.x.max()
@ -658,16 +711,20 @@ class UpperManagement(QtCore.QObject):
self.newData.emit(f_id_list, gid) self.newData.emit(f_id_list, gid)
def extend_fits(self, set_id: list, x_range: np.ndarray): def extend_fits(self, set_id: list, x_range: np.ndarray | None, only_subplots: bool):
graphs = {} graphs = {}
for sid in set_id: for sid in set_id:
data = self[sid] data = fit = self[sid]
fit = data.copy(full=True, keep_color=True)
fit.data = fit.data.with_new_x(x_range)
graph_id = data.graph graph_id = data.graph
if graph_id not in graphs: if graph_id not in graphs:
graphs[graph_id] = [] graphs[graph_id] = []
if not only_subplots:
fit = data.copy(full=True, keep_color=True)
if x_range is not None:
fit.data = fit.data.with_new_x(x_range)
graphs[graph_id].append(self.add(fit)) graphs[graph_id].append(self.add(fit))
color_scheme = available_cycles['colorblind'] color_scheme = available_cycles['colorblind']
@ -734,14 +791,27 @@ class UpperManagement(QtCore.QObject):
_active = self.graphs[self.current_graph].active _active = self.graphs[self.current_graph].active
new_datasets = {} new_datasets = {}
groupby = params.pop('groupby')
for sid in _active: for sid in _active:
data_i = self.data[sid] data_i = self.data[sid]
pts = data_i.points(params)
if groupby == 'group':
if data_i.group not in new_datasets: if data_i.group not in new_datasets:
new_datasets[data_i.group] = [], [] new_datasets[data_i.group] = [], []
new_x_axis, _temp = new_datasets[data_i.group] new_x_axis, _temp = new_datasets[data_i.group]
if pts:
new_x_axis.append(data_i.value) new_x_axis.append(data_i.value)
_temp.append(data_i.points(params)) _temp.append(pts)
else:
for (_x, _y, _yerr) in pts:
if _x not in new_datasets:
new_datasets[_x] = [], []
new_x_axis, _temp = new_datasets[_x]
new_x_axis.append(data_i.value)
_temp.append([[_x, _y, _yerr]])
key_list = [] key_list = []
for label, (new_x_axis, _temp) in new_datasets.items(): for label, (new_x_axis, _temp) in new_datasets.items():
@ -822,9 +892,23 @@ class UpperManagement(QtCore.QObject):
new_x = self.data[new_axis[0]].x new_x = self.data[new_axis[0]].x
new_key = [] new_key = []
missed = []
for ids in data_ids: for ids in data_ids:
try:
k = self.add(interpolate(self.data[ids], new_x, xlog=xlog, ylog=ylog, kind=mode, extrapolate=True)) k = self.add(interpolate(self.data[ids], new_x, xlog=xlog, ylog=ylog, kind=mode, extrapolate=True))
new_key.append(k) new_key.append(k)
except ValueError:
missed.append(self.data[ids].name)
if missed:
missed_str = '\n'.join(missed)
_ = QtWidgets.QMessageBox.warning(
QtWidgets.QWidget(),
'Interpolation failed',
f'Interpolation failed for the following sets:\n\n'
f'{missed_str}\n\n'
f'(Probably because of duplicate x values)'
)
self.newData.emit(new_key, dest_graph) self.newData.emit(new_key, dest_graph)
@ -960,7 +1044,7 @@ class UpperManagement(QtCore.QObject):
else: else:
data = self.data[sets[0]] data = self.data[sets[0]]
if isinstance(data.data, new_type): if isinstance(data.data, new_type):
error_list.append(f'{data.name} is alreade of type {new_type.__name__}') error_list.append(f'{data.name} is already of type {new_type.__name__}')
continue continue
new_data = new_type(data.x, np.zeros(data.x.size)) new_data = new_type(data.x, np.zeros(data.x.size))
@ -994,6 +1078,8 @@ class UpperManagement(QtCore.QObject):
@QtCore.pyqtSlot(list, list, bool) @QtCore.pyqtSlot(list, list, bool)
def eval_expression(self, cmds: list, set_ids: list, overwrite: bool): def eval_expression(self, cmds: list, set_ids: list, overwrite: bool):
if self.namespace is None:
self.namespace = self.get_namespace()
ns = self.namespace.flatten() ns = self.namespace.flatten()
if overwrite: if overwrite:
@ -1026,13 +1112,28 @@ class UpperManagement(QtCore.QObject):
if failures: if failures:
err_msg = QtWidgets.QMessageBox(parent=self.sender()) err_msg = QtWidgets.QMessageBox(parent=self.sender())
err_msg.setText('One or more errors occured during evaluation.') err_msg.setText('One or more errors occurred during evaluation.')
err_msg.setDetailedText('\n'.join(f'{d.name} failed with error: {err.args}' for d, err in failures)) err_msg.setDetailedText('\n'.join(f'{d.name} failed with error: {err.args}' for d, err in failures))
err_msg.exec() err_msg.exec()
self.sender().success = not failures self.sender().success = not failures
self.sender().add_data(self.active_sets) self.sender().add_data(self.active_sets)
@QtCore.pyqtSlot(str)
def run_script(self, text):
self.namespace = self.get_namespace()
ns = self.namespace.flatten()
ns['return_list'] = []
# custom namespace must be available in global namespace of exec, otherwise imports do not work in functions
exec(text, ns, ns)
new_sets = []
for new_data in ns['return_list']:
new_sets.append(self.add(new_data))
self.newData.emit(new_sets, '')
@QtCore.pyqtSlot(list, dict) @QtCore.pyqtSlot(list, dict)
def create_from_function(self, cmds: list, opts: dict): def create_from_function(self, cmds: list, opts: dict):
ns = dict(self.namespace.flatten()) ns = dict(self.namespace.flatten())

View File

@ -16,6 +16,12 @@ class InterpolDialog(QtWidgets.QDialog, Ui_Dialog):
self.step_lineEdit.setValidator(QtGui.QIntValidator()) self.step_lineEdit.setValidator(QtGui.QIntValidator())
self._data = {} self._data = {}
self._src_id = None
self._dest_graph = ''
def __call__(self):
self.listWidget.clear()
self._data = {}
@QtCore.pyqtSlot(int, name='on_xaxis_comboBox_currentIndexChanged') @QtCore.pyqtSlot(int, name='on_xaxis_comboBox_currentIndexChanged')
def change_x_source(self, idx: int): def change_x_source(self, idx: int):
@ -25,29 +31,41 @@ class InterpolDialog(QtWidgets.QDialog, Ui_Dialog):
def set_data(self, data, current_gid): def set_data(self, data, current_gid):
self.graph_combobox.blockSignals(True) self.graph_combobox.blockSignals(True)
self._data = {} self._data = {}
dest_idx = 0
for (gid, graph_name), sets in data.items(): for (gid, graph_name), sets in data.items():
self.graph_combobox.addItem(graph_name, userData=gid) self.graph_combobox.addItem(graph_name, userData=gid)
self.dest_combobox.addItem(graph_name, userData=gid) self.dest_combobox.addItem(graph_name, userData=gid)
if self._dest_graph == gid:
dest_idx = self.dest_combobox.currentIndex()
if gid == current_gid: if gid == current_gid:
self.make_list(sets) self.make_list(sets)
self._data[gid] = sets self._data[gid] = sets
self.graph_combobox.blockSignals(False) self.graph_combobox.blockSignals(False)
self.change_graph(0) self.change_graph(dest_idx)
def make_list(self, current_sets): def make_list(self, current_sets):
for sid, set_name in current_sets: for sid, set_name in current_sets:
item = QtWidgets.QListWidgetItem(set_name) item = QtWidgets.QListWidgetItem(set_name)
item.setData(QtCore.Qt.UserRole, sid) item.setData(QtCore.Qt.ItemDataRole.UserRole, sid)
item.setCheckState(QtCore.Qt.Checked) item.setCheckState(QtCore.Qt.CheckState.Checked)
self.listWidget.addItem(item) self.listWidget.addItem(item)
@QtCore.pyqtSlot(int, name='on_graph_combobox_currentIndexChanged') @QtCore.pyqtSlot(int, name='on_graph_combobox_currentIndexChanged')
def change_graph(self, idx: int): def change_graph(self, idx: int):
self.set_combobox.clear() self.set_combobox.clear()
gid = self.graph_combobox.itemData(idx, QtCore.Qt.UserRole) gid = self.graph_combobox.itemData(idx, QtCore.Qt.ItemDataRole.UserRole)
set_idx = -1
if gid is not None: if gid is not None:
for set_key, set_name in self._data[gid]: for i, (set_key, set_name) in enumerate(self._data[gid]):
print(self._src_id, set_key, set_name, i)
self.set_combobox.addItem(set_name, userData=set_key) self.set_combobox.addItem(set_name, userData=set_key)
print(self.set_combobox.currentIndex())
if self._src_id == set_key:
set_idx = i
print(set_idx)
if set_idx > -1:
self.set_combobox.setCurrentIndex(set_idx)
def collect_parameter(self): def collect_parameter(self):
xlog = self.xlog_checkBox.isChecked() xlog = self.xlog_checkBox.isChecked()
@ -71,21 +89,35 @@ class InterpolDialog(QtWidgets.QDialog, Ui_Dialog):
x_src = (start, stop, step, loggy) x_src = (start, stop, step, loggy)
else: else:
x_src = (self.set_combobox.currentData(QtCore.Qt.UserRole),) self._src_id = self.set_combobox.currentData(QtCore.Qt.ItemDataRole.UserRole)
x_src = (self._src_id,)
dest_graph = self.dest_combobox.currentData(QtCore.Qt.UserRole) self._dest_graph = self.dest_combobox.currentData(QtCore.Qt.ItemDataRole.UserRole)
use_data = [] use_data = []
for i in range(self.listWidget.count()): for i in range(self.listWidget.count()):
item = self.listWidget.item(i) item = self.listWidget.item(i)
if item.checkState() == QtCore.Qt.Checked: if item.checkState() == QtCore.Qt.CheckState.Checked:
use_data.append(item.data(QtCore.Qt.UserRole)) use_data.append(item.data(QtCore.Qt.ItemDataRole.UserRole))
self.new_data.emit(use_data, mode, xlog, ylog, x_src, dest_graph) self.new_data.emit(use_data, mode, xlog, ylog, x_src, self._dest_graph)
return True return True
def accept(self): def _save_state(self):
self._src_id = self.set_combobox.currentData(QtCore.Qt.ItemDataRole.UserRole)
self._dest_graph = self.dest_combobox.currentData(QtCore.Qt.ItemDataRole.UserRole)
@QtCore.pyqtSlot(QtWidgets.QAbstractButton, name='on_buttonBox_clicked')
def check_next_actions(self, bttn: QtWidgets.QAbstractButton):
role = self.buttonBox.buttonRole(bttn)
self._save_state()
if role == self.buttonBox.ButtonRole.RejectRole:
self.close()
else:
success = self.collect_parameter() success = self.collect_parameter()
if success:
super().accept() if success and role == self.buttonBox.ButtonRole.AcceptRole:
self.close()

View File

@ -12,10 +12,10 @@ class QSmooth(QtWidgets.QDialog, Ui_SmoothDialog):
@QtCore.pyqtSlot(int, name='on_comboBox_currentIndexChanged') @QtCore.pyqtSlot(int, name='on_comboBox_currentIndexChanged')
def change_mode(self, idx: int): def change_mode(self, idx: int):
if idx == 2: if idx == 1:
self.widget.show() self.widget.show()
self.widget_2.hide() self.widget_2.hide()
elif idx == 3: elif idx == 2:
self.widget.show() self.widget.show()
self.widget_2.show() self.widget_2.show()
else: else:
@ -29,12 +29,24 @@ class QSmooth(QtWidgets.QDialog, Ui_SmoothDialog):
idx = self.comboBox.currentIndex() idx = self.comboBox.currentIndex()
# this order must match the combobox # this order must match the combobox
para['mode'] = ['mean', 'savgol', 'loess', 'median', 'std', 'var', 'max', 'min', 'sum'][idx] para['mode'] = [
'mean',
'savgol',
'loess',
'median',
'std',
'var',
'max',
'min',
'sum',
][idx]
if idx == 2: # Savitzky-Golay needs also polynomial degree
if idx == 1:
para['deg'] = self.polynom_spinBox.value() para['deg'] = self.polynom_spinBox.value()
if idx == 3: # LOESS needs also polynomial degree and number of iterations
if idx == 2:
para['deg'] = self.polynom_spinBox.value() para['deg'] = self.polynom_spinBox.value()
para['it'] = self.iter_spinBox.value() para['it'] = self.iter_spinBox.value()

View File

@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
from nmreval.nmr.coupling import * from nmreval.nmr.coupling import *
from nmreval.distributions import ColeCole, ColeDavidson, HavriliakNegami, KWW, LogGaussian from nmreval.distributions import ColeCole, ColeDavidson, HavriliakNegami, KWW, LogGaussian, GGAlpha
from nmreval.utils import pi from nmreval.utils import pi
from nmreval.utils.text import convert from nmreval.utils.text import convert
@ -19,7 +19,7 @@ class QRelaxCalc(QtWidgets.QDialog, Ui_Dialog):
self.graphs = {} self.graphs = {}
self.specdens = [ColeCole, ColeDavidson, HavriliakNegami, KWW, LogGaussian] self.specdens = [ColeCole, ColeDavidson, HavriliakNegami, KWW, LogGaussian, GGAlpha]
self.coupling = [Quadrupolar, HomoDipolar, Czjzek] self.coupling = [Quadrupolar, HomoDipolar, Czjzek]
self.tau_parameter = [] self.tau_parameter = []

View File

@ -31,7 +31,7 @@ class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
self.t1calculator = RelaxationEvaluation() self.t1calculator = RelaxationEvaluation()
self.sd_parameter = [] self.sd_parameter = []
self.sdmodels = [Debye, ColeCole, ColeDavidson, KWW, HavriliakNegami, LogGaussian] self.sdmodels = [Debye, ColeCole, ColeDavidson, KWW, HavriliakNegami, LogGaussian, GGAlpha]
for i in self.sdmodels: for i in self.sdmodels:
self.specdens_combobox.addItem(i.name) self.specdens_combobox.addItem(i.name)
self.specdens_combobox.currentIndexChanged.connect(self.update_specdens) self.specdens_combobox.currentIndexChanged.connect(self.update_specdens)
@ -51,8 +51,14 @@ class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
self.conv_y = QT1Widget.time_conversion[self.t1_combobox.currentIndex()] self.conv_y = QT1Widget.time_conversion[self.t1_combobox.currentIndex()]
self.minimum = (1, np.inf) self.minimum = (1, np.inf)
self.min_pos = PlotItem(x=np.array([]), y=np.array([]), self.min_pos = PlotItem(
symbol='+', symbolBrush=mkBrush(color='r'), symbolPen=mkPen(color='r'), symbolSize=14) 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.parabola = PlotItem(x=np.array([]), y=np.array([]))
self.lineEdit_2.setValidator(QtGui.QDoubleValidator()) self.lineEdit_2.setValidator(QtGui.QDoubleValidator())
@ -83,10 +89,10 @@ class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
right_b = min(np.argmin(y)+3, len(x)-1) right_b = min(np.argmin(y)+3, len(x)-1)
self.lineEdit_2.blockSignals(True) self.lineEdit_2.blockSignals(True)
self.lineEdit_2.setText('{:.2f}'.format(x[left_b])) self.lineEdit_2.setText(f'{x[left_b]:.2f}')
self.lineEdit_2.blockSignals(False) self.lineEdit_2.blockSignals(False)
self.lineEdit_3.blockSignals(True) self.lineEdit_3.blockSignals(True)
self.lineEdit_3.setText('{:.2f}'.format(x[right_b])) self.lineEdit_3.setText(f'{x[right_b]:.2f}')
self.lineEdit_3.blockSignals(False) self.lineEdit_3.blockSignals(False)
self.t1calculator.set_data(x, y) self.t1calculator.set_data(x, y)
@ -110,6 +116,7 @@ class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
if self.sdmodels[idx].parameter is not None: if self.sdmodels[idx].parameter is not None:
for name in self.sdmodels[idx].parameter: for name in self.sdmodels[idx].parameter:
print(name)
_temp = FormWidget(parent=self, name=name, fixable=True) _temp = FormWidget(parent=self, name=name, fixable=True)
_temp.value = 1 _temp.value = 1
_temp.setChecked(True) _temp.setChecked(True)
@ -133,7 +140,7 @@ class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
try: try:
for i, v, in enumerate(values): for i, v, in enumerate(values):
self.sd_parameter[i].blockSignals(True) self.sd_parameter[i].blockSignals(True)
self.sd_parameter[i].value = '{:.3g}'.format(round(v, 3)) self.sd_parameter[i].value = f'{v:.3g}'
self.sd_parameter[i].blockSignals(False) self.sd_parameter[i].blockSignals(False)
except IndexError: except IndexError:
pass pass
@ -219,7 +226,7 @@ class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
self.update_model() self.update_model()
@QtCore.pyqtSlot(int, name='on_interpol_combobox_currentIndexChanged') @QtCore.pyqtSlot(int, name='on_interpol_combobox_currentIndexChanged')
def determine_minimum(self, idx): def determine_minimum(self, idx: int):
if idx == 0: if idx == 0:
self.checkBox_interpol.setChecked(False) self.checkBox_interpol.setChecked(False)
self.checkBox_interpol.hide() self.checkBox_interpol.hide()
@ -229,9 +236,10 @@ class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
self.checkBox_interpol.show() self.checkBox_interpol.show()
self.frame.show() self.frame.show()
try: try:
m, i_func = self.t1calculator.calculate_t1_min(interpolate=idx, m, i_func = self.t1calculator.calculate_t1_min(
trange=(float(self.lineEdit_2.text()), interpolate=idx,
float(self.lineEdit_3.text()))) trange=(float(self.lineEdit_2.text()), float(self.lineEdit_3.text())),
)
except ValueError: except ValueError:
m, i_func = self.t1calculator.calculate_t1_min(interpolate=None) m, i_func = self.t1calculator.calculate_t1_min(interpolate=None)
@ -273,11 +281,13 @@ class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
return return
with busy_cursor(): with busy_cursor():
calc_stretching, mini = self.t1calculator.get_increase(height=self.minimum[1], calc_stretching, mini = self.t1calculator.get_increase(
height=self.minimum[1],
idx=var_idx, mode=notfix, idx=var_idx, mode=notfix,
omega=2*np.pi*self.frequency, omega=2*np.pi*self.frequency,
dist_parameter=sd_args, prefactor=cp_args, dist_parameter=sd_args, prefactor=cp_args,
coupling_kwargs=cp_kwargs) coupling_kwargs=cp_kwargs
)
self.label_t1min.setText(f'{mini:.4g} s') self.label_t1min.setText(f'{mini:.4g} s')
@ -292,9 +302,13 @@ class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
sd_args, _ = self.get_sd_values() sd_args, _ = self.get_sd_values()
cp_args, cp_kwargs, _ = self.get_cp_values() cp_args, cp_kwargs, _ = self.get_cp_values()
tau_mode = ['fit', 'peak', 'mean', 'logmean'][self.tau_combox.currentIndex()] 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, 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, coupling_param=cp_args, coupling_kwargs=cp_kwargs,
mode=tau_mode, interpolate=self.checkBox_interpol.isChecked()) mode=tau_mode,
interpolate=self.checkBox_interpol.isChecked()
)
name = self.name + '-' + str(self.t1calculator) + '(' name = self.name + '-' + str(self.t1calculator) + '('
name += ','.join([f'{a:.3g}' for a in sd_args]) name += ','.join([f'{a:.3g}' for a in sd_args])
@ -332,4 +346,4 @@ class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
@QtCore.pyqtSlot(int, name='on_graph_checkbox_stateChanged') @QtCore.pyqtSlot(int, name='on_graph_checkbox_stateChanged')
def changed_state(self, checked): def changed_state(self, checked):
self.graph_combobox.setEnabled(checked != QtCore.Qt.Checked) self.graph_combobox.setEnabled(checked != QtCore.Qt.CheckState.Checked)

View File

@ -156,3 +156,20 @@ double energyDistCorrelation(double x, void *user_data) {
return normalDist(x, e_m, e_b) * exp(-t * r); return normalDist(x, e_m, e_b) * exp(-t * r);
} }
// Generalised Gamma Function
double genGammaAlphaDist(double x, void *user_data) {
double *c = (double *)user_data;
double omega = c[0];
double tau0 = c[1];
double alpha = c[2];
double beta = c[3];
double b_to_a = beta / alpha;
double tau_to_tau0 = tau / tau0;
double norm = exp(-lgamma(b_to_a) + b_to_a * log(b_to_a)) * alpha;
return norm * exp(-b_to_a * pow(tau_to_tau0, alpha)) * pow(tau_to_tau0, beta);
}

View File

@ -273,9 +273,14 @@ class Points:
def length(self): def length(self):
return len(self._x) return len(self._x)
def points(self, idx: list = None, special: str = None, def points(
avg_range: tuple[int, int] = (0, 0), avg_mode: str = 'mean', self,
pts: list = None) -> list[tuple]: idx: list = None,
special: str = None,
avg_range: tuple[int, int] = (0, 0),
avg_mode: str = 'mean',
pts: list = None,
) -> list[tuple]:
""" """
Return (x, y) values at specified positions. Return (x, y) values at specified positions.
@ -294,7 +299,7 @@ class Points:
avg_range (tuple of int) : 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). 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) : avg_mode (str {'mean', 'sum', 'integral', 'std'} , optional) :
Averaging type Averaging type
`mean` : Arithmetic average `mean` : Arithmetic average
@ -303,6 +308,8 @@ class Points:
'integral`: Integration over range using Simpson's rule 'integral`: Integration over range using Simpson's rule
'std': Standard deviation
pts (list, optional) : pts (list, optional) :
If given, points will be appended. If given, points will be appended.
@ -313,66 +320,90 @@ class Points:
if (idx is None) and (special is None): if (idx is None) and (special is None):
raise ValueError('Either `idx` or `special` must be given') raise ValueError('Either `idx` or `special` must be given')
if avg_mode not in ['mean', 'sum', 'integral']: if avg_mode not in ['mean', 'sum', 'integral', 'std']:
raise ValueError(f'Parameter `avg_mode` is `mean`, `sum`, `integral`, not `{avg_mode}`' ) raise ValueError(f'Parameter `avg_mode` is `mean`, `sum`, `integral`, `std`, not `{avg_mode}`' )
if pts is None: if pts is None:
pts = [] pts = []
_tmp_x = self._x[self.mask]
x_order = np.argsort(_tmp_x)
_tmp_x = _tmp_x[x_order]
_tmp_y = self._y[self.mask][x_order]
_tmp_yerr = self._y_err[self.mask][x_order]
if idx is not None: if idx is not None:
for x in idx: for idx_i in idx:
if isinstance(x, tuple): if isinstance(idx_i, tuple):
x_idx = np.argmin(np.abs(self._x[self.mask] - (x[0]+x[1])/2)) in_region = np.where((_tmp_x - idx_i[0] > 0) & (idx_i[1] - _tmp_x > 0))[0]
left_b = np.argmin(np.abs(self._x[self.mask] - x[0])) if len(in_region) > 0:
right_b = np.argmin(np.abs(self._x[self.mask] - x[1])) x_idx = in_region[in_region.size//2]
left_b = in_region[0]
right_b = in_region[-1] + 1
else: else:
x_idx = np.argmin(np.abs(self._x[self.mask]-x)) continue
else:
x_idx = np.argmin(np.abs(_tmp_x-idx_i))
left_b = int(max(0, x_idx - avg_range[0])) left_b = int(max(0, x_idx - avg_range[0]))
right_b = int(min(len(self), x_idx + avg_range[1] + 1)) right_b = int(min(len(self), x_idx + avg_range[1] + 1))
if left_b < right_b: if left_b < right_b:
pts.append([self._x[x_idx], *self._average(avg_mode, x_idx, left_b, right_b)]) pts.append([_tmp_x[x_idx], *self._average(_tmp_x, _tmp_y, _tmp_yerr, avg_mode, x_idx, left_b, right_b)])
else: else:
pts.append([self._x[x_idx], self._y[x_idx], self._y_err[x_idx]]) pts.append([_tmp_x[x_idx], _tmp_y[x_idx], self._y_err[x_idx]])
if special is not None: if special is not None:
if special not in ['max', 'min', 'absmax', 'absmin']: if special not in ['max', 'min', 'absmax', 'absmin']:
raise ValueError('Parameter "special" must be "max", "min", "absmax", "absmin".') raise ValueError('Parameter "special" must be "max", "min", "absmax", "absmin".')
if special == 'max': if special == 'max':
x_idx = np.argmax(self._y.real[self.mask]) x_idx = np.argmax(_tmp_y.real)
elif special == 'min': elif special == 'min':
x_idx = np.argmax(self._y.real[self.mask]) x_idx = np.argmax(_tmp_y.real)
elif special == 'absmax': elif special == 'absmax':
x_idx = np.argmax(np.abs(self._y[self.mask])) x_idx = np.argmax(np.abs(_tmp_y))
else: else:
x_idx = np.argmin(np.abs(self._y[self.mask])) x_idx = np.argmin(np.abs(_tmp_y))
left_b = int(max(0, x_idx - avg_range[0])) left_b = int(max(0, x_idx - avg_range[0]))
right_b = int(min(len(self), x_idx + avg_range[1] + 1)) 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)]) pts.append([_tmp_x[x_idx], *self._average(_tmp_x, _tmp_y, _tmp_yerr, avg_mode, x_idx, left_b, right_b)])
return pts return pts
def _average(self, mode: str, idx, left: int, right: int) -> tuple[float, float]: @staticmethod
def _average(
x: np.ndarray,
y: np.ndarray,
y_err: np.ndarray,
mode: str,
idx: int,
left: int,
right: int,
) -> tuple[float, float]:
if mode == 'mean': if mode == 'mean':
y_mean = np.mean(self._y[self.mask][left:right].real) y_mean = np.mean(y[left:right].real)
y_err = np.linalg.norm(self._y_err[self.mask][left:right]) / (right - left) y_err_mean = np.linalg.norm(y_err[left:right]) / (right - left)
elif mode == 'sum': elif mode == 'sum':
y_mean = np.sum(self._y[self.mask][left:right].real) y_mean = np.sum(y[left:right].real)
y_err = np.linalg.norm(self._y_err[self.mask][left:right]) y_err_mean = np.linalg.norm(y_err[left:right])
elif mode == 'integral': elif mode == 'integral':
y_mean = simpson(self._y[self.mask][left:right].real, x=self._x[left:right]) y_mean = simpson(y[left:right].real, x=x[left:right])
y_err = np.linalg.norm(cumulative_trapezoid(self._y_err[self.mask][left:right].real, x=self._x[left:right])) y_err_mean = np.linalg.norm(cumulative_trapezoid(y_err[left:right].real, x=x[left:right]))
elif mode == 'std':
y_mean = np.std(y[left:right])
y_err_mean = 0.
else: else:
y_mean = self._y[self.mask][idx].real y_mean = y[idx].real
y_err = self._y_err[self.mask][idx] y_err_mean = y_err[idx]
return y_mean, y_err return y_mean, y_err_mean
def concatenate(self, other): def concatenate(self, other):
""" """
@ -520,26 +551,37 @@ class Points:
return self return self
def cut(self, low_lim: float = None, high_lim: float = None): def cut(self, x_low: float = None, x_high: float = None, y_low: float = None, y_high: float = None):
""" """
Cut Cut
Args: Args:
low_lim: x_low: Lower limit
high_lim: x_high: Upper limit for x values
y_low: Lower limit
y_high: Upper limit for x valuew
Returns: Returns:
""" """
if low_lim is None and high_lim is None:
if x_low is None and x_high is None and y_low is None and y_high is None:
return self return self
if low_lim is None: if x_low is None:
low_lim = np.min(self._x) x_low = np.min(self._x)-1
if high_lim is None: if x_high is None:
high_lim = np.max(self._x) x_high = np.max(self._x)+1
_mask = np.ma.masked_inside(self._x, low_lim, high_lim).mask if y_low is None:
y_low = np.min(self._y.real)-1
if y_high is None:
y_high = np.max(self._y.real)+1
x_mask = (self._x >= x_low) & (self._x <= x_high)
y_mask = (self._y.real >= y_low) & (self._y.real <= y_high)
_mask = x_mask & y_mask
self._x = self._x[_mask] self._x = self._x[_mask]
self._y = self._y[_mask] self._y = self._y[_mask]
@ -690,17 +732,20 @@ class Points:
np.savetxt(path, self.toarray(err=err), header=header, fmt='%.10e') np.savetxt(path, self.toarray(err=err), header=header, fmt='%.10e')
else: else:
with path.open('w') as f: with path.open('w') as f:
f.write(header) f.write('# ' + '\n# '.join(header.split('\n')))
f.write('\n')
for i, l in enumerate(self.toarray(err=err)): for i, l in enumerate(self.toarray(err=err)):
if self.mask[i]: if self.mask[i]:
f.write('\t'.join(map(lambda _x: f'{_x:.10e}', l.tolist())) + '\n') f.write('\t'.join(map(lambda _x: f'{_x:.10e}', l.tolist())) + '\n')
else: else:
f.write('#' + '\t'.join(map(lambda _x: f'{_x:.10e}', l.tolist())) + '\n') f.write('# ' + '\t'.join(map(lambda _x: f'{_x:.10e}', l.tolist())) + '\n')
def get_state(self) -> dict: def get_state(self) -> dict:
ret_dic = {'x': self._x.tolist(), ret_dic = {
'x': self._x.tolist(),
'y': self._y.tolist(), 'y': self._y.tolist(),
'mask': (np.where(~self.mask)[0]).tolist()} 'mask': (np.where(~self.mask)[0]).tolist()
}
if np.all(self._y_err == 0): if np.all(self._y_err == 0):
ret_dic['y_err'] = 0.0 ret_dic['y_err'] = 0.0

View File

@ -26,3 +26,4 @@ from .coledavidson import ColeDavidson
from .debye import Debye from .debye import Debye
from .kww import KWW from .kww import KWW
from .loggaussian import LogGaussian from .loggaussian import LogGaussian
from .gengamma import GGAlpha

View File

@ -3,7 +3,12 @@ from ctypes import c_double, cast, pointer, c_void_p
import numpy as np import numpy as np
from scipy import LowLevelCallable from scipy import LowLevelCallable
from scipy.integrate import quad, simps as simpson
from scipy.integrate import quad
try:
from scipy.integrate import simps as simpson
except ImportError:
from scipy.integrate import simpson
from .base import Distribution from .base import Distribution
from ..lib.utils import ArrayLike from ..lib.utils import ArrayLike
@ -90,7 +95,7 @@ def _integrate_c(func, omega: np.ndarray, temperature: np.ndarray, tau0: float,
for o, t in product(omega, temperature): for o, t in product(omega, temperature):
c = (c_double * 5)(o, tau0, e_m, e_b, t) c = (c_double * 5)(o, tau0, e_m, e_b, t)
user_data = cast(pointer(c), c_void_p) user_data = cast(pointer(c), c_void_p)
area = quad(LowLevelCallable(func, user_data), 0, np.infty, epsabs=1e-13)[0] area = quad(LowLevelCallable(func, user_data), 0, np.inf, epsabs=1e-13)[0]
res.append(area) res.append(area)
@ -106,7 +111,7 @@ def _integrate_py(func, axis, temp, tau0, e_m, e_b):
e_axis = np.linspace(max(0., e_m - 50*e_b), e_m + 50*e_b, num=5001) e_axis = np.linspace(max(0., e_m - 50*e_b), e_m + 50*e_b, num=5001)
ret_val = [] ret_val = []
for o, tt in product(x, temperature): for o, tt in product(x, temperature):
ret_val.append(simpson(func(e_axis, o, tau0, e_m, e_b, tt), e_axis)) ret_val.append(simpson(y=func(e_axis, o, tau0, e_m, e_b, tt), x=e_axis))
ret_val = np.array(ret_val).reshape(x.shape[0], temperature.shape[0]) ret_val = np.array(ret_val).reshape(x.shape[0], temperature.shape[0])

View File

@ -7,7 +7,7 @@ try:
from scipy.integrate import simpson from scipy.integrate import simpson
except ImportError: except ImportError:
from scipy.integrate import simps as simpson from scipy.integrate import simps as simpson
from scipy.special import gammaln from scipy.special import gammaln, gamma as scipy_gamma
from nmreval.distributions.base import Distribution from nmreval.distributions.base import Distribution
from nmreval.math.logfourier import logft from nmreval.math.logfourier import logft
@ -20,10 +20,10 @@ class AbstractGG(Distribution, ABC):
@classmethod @classmethod
def correlation(cls, t, tau0, *args): def correlation(cls, t, tau0, *args):
tt = np.asanyarray(t) tt = np.atleast_1d(t)
taus, ln_tau = AbstractGG._prepare_integration(tau0) taus, ln_tau = AbstractGG._prepare_integration(tau0)
g_tau = cls.distribution(taus, tau0, *args) g_tau = cls.distribution(taus, tau0, *args)
ret_val = np.array([simpson(np.exp(-t_i/taus) * g_tau, ln_tau) for t_i in tt]) ret_val = np.array([simpson(np.exp(-t_i/taus) * g_tau, x=ln_tau) for t_i in tt]).squeeze()
return ret_val return ret_val
@ -32,30 +32,41 @@ class AbstractGG(Distribution, ABC):
r""" r"""
Calculate spectral density \int G(ln(tau) tau/(1+(w*tau)^2) dln(tau) Calculate spectral density \int G(ln(tau) tau/(1+(w*tau)^2) dln(tau)
""" """
w = np.asanyarray(omega) _w = np.atleast_1d(omega)
taus, ln_tau = AbstractGG._prepare_integration(tau0) _t = np.atleast_1d(tau0)
g_tau = cls.distribution(taus, tau0, *args) ret_val = np.zeros((_w.size, _t.size), dtype=complex)
ret_val = np.array([simpson(g_tau / (1 - 1j*w_i*taus), ln_tau) for w_i in w]) for (i, tau_i) in enumerate(_t):
taus, ln_tau = AbstractGG._prepare_integration(tau_i)
g_tau = cls.distribution(taus, tau_i, *args)
return ret_val ret_val[:, i].real = np.array([simpson(g_tau * taus / (1 + (w_i*taus)**2), x=ln_tau) for w_i in _w])
ret_val[:, i].imag = np.array([simpson(g_tau * w_i * taus / (1 + (w_i*taus)**2), x=ln_tau) for w_i in _w])
return ret_val.squeeze()
@classmethod @classmethod
def specdens(cls, omega, tau0, *args): def specdens(cls, omega: float | np.ndarray, tau0: float | np.ndarray, *args) -> float | np.ndarray:
r""" r"""
Calculate spectral density \int G(ln(tau) tau/(1+(w*tau)^2) dln(tau) Calculate spectral density \int G(ln(tau) tau/(1+(w*tau)^2) dln(tau)
""" """
w = np.asanyarray(omega) _w = np.atleast_1d(omega)
taus, ln_tau = AbstractGG._prepare_integration(tau0) _t = np.atleast_1d(tau0)
g_tau = cls.distribution(taus, tau0, *args) ret_val = np.zeros((_w.size, _t.size))
ret_val = np.array([simpson(g_tau * taus / (1 + (w_i*taus)**2), ln_tau) for w_i in w]) for (i, tau_i) in enumerate(_t):
taus, ln_tau = AbstractGG._prepare_integration(tau_i)
g_tau = cls.distribution(taus, tau_i, *args)
return ret_val ret_val[:, i] = np.array([simpson(g_tau * taus / (1 + (w_i*taus)**2), x=ln_tau) for w_i in _w])
return ret_val.squeeze()
@staticmethod @staticmethod
def _prepare_integration( def _prepare_integration(
tau0: float, limits: tuple[int, int] = (20, 20), num_steps: int = 4001 tau0: float,
limits: tuple[int, int] = (20, 20),
num_steps: int = 4001,
) -> tuple[np.ndarray, np.ndarray]: ) -> tuple[np.ndarray, np.ndarray]:
""" """
Create array of correlation times for integration over ln(tau) Create array of correlation times for integration over ln(tau)
@ -66,7 +77,6 @@ class AbstractGG(Distribution, ABC):
Returns: Returns:
array of taus and array of ln(tau) array of taus and array of ln(tau)
""" """
ln_tau0 = np.log(tau0) ln_tau0 = np.log(tau0)
@ -77,23 +87,29 @@ class AbstractGG(Distribution, ABC):
# noinspection PyMethodOverriding # noinspection PyMethodOverriding
class GGAlpha(AbstractGG): class GGAlpha(AbstractGG):
name = r'General \Gamma (\alpha)' name = r'General Gamma (alpha)'
parameter = [r'\tau', r'\alpha', r'\beta'] parameter = [r'\alpha', r'\beta']
@staticmethod @staticmethod
def distribution(taus: float | np.ndarray, tau: float, alpha: float, beta: float) -> float | np.ndarray: def distribution(taus: float | np.ndarray, tau: float, alpha: float, beta: float) -> float | np.ndarray:
b_to_a = beta / alpha b_to_a = beta / alpha
norm = np.exp(gammaln(b_to_a) - b_to_a * np.log(b_to_a)) / alpha inv_norm = alpha * np.exp(-gammaln(b_to_a) + b_to_a * np.log(b_to_a))
t_to_t0 = taus / tau t_to_t0 = taus / tau
ret_val = np.exp(-b_to_a * t_to_t0**alpha) * t_to_t0**beta ret_val = np.exp(-b_to_a * t_to_t0**alpha) * t_to_t0**beta
return ret_val / norm return ret_val * inv_norm
@staticmethod
def mean(tau: float | np.ndarray, alpha: float, beta: float) -> float | np.ndarray:
beta_by_alpha = beta/alpha
inv_alpha = 1/alpha
return tau * (1/beta_by_alpha)**inv_alpha * scipy_gamma(beta_by_alpha + inv_alpha) / scipy_gamma(beta_by_alpha)
# noinspection PyMethodOverriding # noinspection PyMethodOverriding
class GGAlphaEW(AbstractGG): class GGAlphaEW(AbstractGG):
name = r'General \Gamma (\alpha + EW)' name = r'General Gamma (alpha + EW)'
parameter = [r'\tau', r'\alpha', r'\beta', r'\sigma', r'\gamma'] parameter = [r'\alpha', r'\beta', r'\sigma', r'\gamma']
@staticmethod @staticmethod
def distribution(tau: float | np.ndarray, tau0: float, def distribution(tau: float | np.ndarray, tau0: float,
@ -117,8 +133,8 @@ class GGAlphaEW(AbstractGG):
# noinspection PyMethodOverriding # noinspection PyMethodOverriding
class GGBeta(AbstractGG): class GGBeta(AbstractGG):
name = r'General \Gamma (\beta)' name = r'General Gamma (beta)'
parameter = [r'\tau', 'a', 'b'] parameter = ['a', 'b']
@staticmethod @staticmethod
def distribution(tau: float | np.ndarray, tau0: float, a: float, b: float) -> float | np.ndarray: def distribution(tau: float | np.ndarray, tau0: float, a: float, b: float) -> float | np.ndarray:

View File

@ -9,7 +9,13 @@ from inspect import signature, Parameter
class ModelFactory: class ModelFactory:
@staticmethod @staticmethod
def create_from_list(funcs: list, left=None, func_order=None, param_len=None, left_cnt=None): def create_from_list(
funcs: list,
left=None,
func_order: list[int] = None,
param_len: list[int] = None,
left_cnt: int = 0,
):
if func_order is None: if func_order is None:
func_order = [] func_order = []
@ -20,32 +26,50 @@ class ModelFactory:
if not func['active']: if not func['active']:
continue continue
func_order.append(func['cnt'])
param_len.append(len(func['func'].params))
if func['children']: if func['children']:
right, _, _ = ModelFactory.create_from_list(func['children'], left_cnt=func['pos'], f = func.copy()
func_order=func_order, param_len=param_len) f['children'] = []
right_cnt = None right, _, _, right_cnt = ModelFactory.create_from_list(
right = MultiModel(func['func'], right, func['children'][0]['op'], left_idx=func['cnt'], right_idx=None) [f] + func['children'],
left_cnt=func['pos'],
func_order=func_order,
param_len=param_len,
)
else: else:
right = func['func'] right = func['func']
right_cnt = func['cnt'] right_cnt = func['cnt']
func_order.append(func['cnt'])
param_len.append(len(func['func'].params))
if left is None: if left is None:
left = right left = right
left_cnt = right_cnt left_cnt = right_cnt
else: else:
left = MultiModel(left, right, func['op'], left = MultiModel(left, right, func['op'], left_idx=left_cnt, right_idx=right_cnt)
left_idx=left_cnt, right_idx=right_cnt)
return left, func_order, param_len return left, func_order, param_len, left_cnt
class MultiModel: class MultiModel:
op_repr = {operator.add: ' + ', operator.mul: ' * ', operator.sub: ' - ', operator.truediv: ' / '} op_repr = {
str_op = {'+': operator.add, '*': operator.mul, '-': operator.sub, '/': operator.truediv} operator.add: ' + ',
int_op = {0: operator.add, 1: operator.mul, 2: operator.sub, 3: operator.truediv} 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, def __init__(self,
left: Any, left: Any,
@ -69,6 +93,9 @@ class MultiModel:
if self._op is None: if self._op is None:
raise ValueError('Invalid binary operator.') raise ValueError('Invalid binary operator.')
if right_idx is None:
right_idx = left_idx + 1
self.name = '(' self.name = '('
self.params = [] self.params = []
self.bounds = [] self.bounds = []

View File

@ -6,10 +6,13 @@ from .model import Model
from .parameter import Parameters, Parameter from .parameter import Parameters, Parameter
class Data(object): class Data:
def __init__(self, x, y, we=None, idx=None): def __init__(self, x, y, we=None, idx=None, complex_type: int = 0):
self.x = np.asarray(x) self.x = np.asarray(x)
self.y = np.asarray(y) self.y = np.asarray(y)
if self.x.size == 0 or self.y.size == 0:
raise ValueError("Data is empty")
if self.y.shape[0] != self.x.shape[0]: if self.y.shape[0] != self.x.shape[0]:
raise ValueError(f'x and y have different lengths {self.x.shape[0]} and {self.y.shape[0]}') raise ValueError(f'x and y have different lengths {self.x.shape[0]} and {self.y.shape[0]}')
@ -20,6 +23,7 @@ class Data(object):
self.parameter = Parameters() self.parameter = Parameters()
self.para_keys: list = [] self.para_keys: list = []
self.fun_kwargs = {} self.fun_kwargs = {}
self.complex_type = complex_type
def __len__(self): def __len__(self):
return self.y.shape[0] return self.y.shape[0]

View File

@ -234,6 +234,10 @@ class FitRoutine(object):
# get variable parameter for fitter # get variable parameter for fitter
p0_k, lb_k, ub_k, var_pars_k = self._prep_data(data) p0_k, lb_k, ub_k, var_pars_k = self._prep_data(data)
if p0_k is None:
self.make_results(data, data.para_keys, var_pars_k, data.para_keys, (len(data.para_keys), len(data.para_keys)),
err=None, corr=None, partial_corr=None)
else:
if mode == 'lsq': if mode == 'lsq':
self._least_squares_single(data, p0_k, lb_k, ub_k, var_pars_k) self._least_squares_single(data, p0_k, lb_k, ub_k, var_pars_k)
@ -247,6 +251,12 @@ class FitRoutine(object):
else: else:
data_pars, p0, lb, ub, var_pars = self._prep_global(data_groups, linked_parameter) data_pars, p0, lb, ub, var_pars = self._prep_global(data_groups, linked_parameter)
if not p0:
for data_k, p_k in zip(data_groups, data_pars):
self.make_results(data_k, p_k, [], p_k, (len(p_k), len(p_k)),
err=None, corr=None, partial_corr=None)
else:
if mode == 'lsq': if mode == 'lsq':
self._least_squares_global(data_groups, p0, lb, ub, var_pars, data_pars) self._least_squares_global(data_groups, p0, lb, ub, var_pars, data_pars)
@ -291,7 +301,10 @@ class FitRoutine(object):
vals.append([v_k.scaled_value, v_k.lb / v_k.scale, v_k.ub / v_k.scale]) vals.append([v_k.scaled_value, v_k.lb / v_k.scale, v_k.ub / v_k.scale])
var_pars.append(p_k) var_pars.append(p_k)
if vals:
pp, lb, ub = zip(*vals) pp, lb, ub = zip(*vals)
else:
pp = lb = ub = None
return pp, lb, ub, var_pars return pp, lb, ub, var_pars
@ -319,10 +332,10 @@ class FitRoutine(object):
actual_pars.append(p_k_used) actual_pars.append(p_k_used)
# parameter is variable and was not found before as shared parameter # parameter is variable and was not found before as shared parameter
if v_k_used.var and p_k_used not in var: if v_k_used.var and p_k_used not in var:
var.append(p_k_used)
p0.append(v_k_used.scaled_value) p0.append(v_k_used.scaled_value)
lb.append(v_k_used.lb / v_k_used.scale) lb.append(v_k_used.lb / v_k_used.scale)
ub.append(v_k_used.ub / v_k_used.scale) ub.append(v_k_used.ub / v_k_used.scale)
var.append(p_k_used)
data_pars.append(actual_pars) data_pars.append(actual_pars)
@ -348,7 +361,7 @@ class FitRoutine(object):
with np.errstate(all='ignore'): with np.errstate(all='ignore'):
res = optimize.least_squares(cost, p0, bounds=(lb, ub), max_nfev=500 * len(p0)) res = optimize.least_squares(cost, p0, bounds=(lb, ub), max_nfev=500 * len(p0))
err, corr, partial_corr = self._calc_error(res.jac, np.sum(res.fun**2), *res.jac.shape) err, corr, partial_corr = _calc_error(res.jac, np.sum(res.fun**2), *res.jac.shape)
self.make_results(data, res.x, var, data.para_keys, res.jac.shape, self.make_results(data, res.x, var, data.para_keys, res.jac.shape,
err=err, corr=corr, partial_corr=partial_corr) err=err, corr=corr, partial_corr=partial_corr)
@ -362,7 +375,7 @@ class FitRoutine(object):
with np.errstate(all='ignore'): with np.errstate(all='ignore'):
res = optimize.least_squares(cost, p0, bounds=(lb, ub), max_nfev=500 * len(p0)) res = optimize.least_squares(cost, p0, bounds=(lb, ub), max_nfev=500 * len(p0))
err, corr, partial_corr = self._calc_error(res.jac, np.sum(res.fun**2), *res.jac.shape) err, corr, partial_corr = _calc_error(res.jac, np.sum(res.fun**2), *res.jac.shape)
for v, var_pars_k in zip(data, data_pars): for v, var_pars_k in zip(data, data_pars):
self.make_results(v, res.x, var, var_pars_k, res.jac.shape, self.make_results(v, res.x, var, var_pars_k, res.jac.shape,
err=err, corr=corr, partial_corr=partial_corr) err=err, corr=corr, partial_corr=partial_corr)
@ -445,9 +458,17 @@ class FitRoutine(object):
self.make_results(v, res.beta, var, var_pars_k, (sum(len(d) for d in data), len(p0)), self.make_results(v, res.beta, var, var_pars_k, (sum(len(d) for d in data), len(p0)),
err=res.sd_beta, corr=corr, partial_corr=partial_corr) err=res.sd_beta, corr=corr, partial_corr=partial_corr)
def make_results(self, data, p, var_pars, used_pars, shape, def make_results(
err=None, corr=None, partial_corr=None): self,
data: Data,
p: list[float],
var_pars: list[str],
used_pars: list[str],
shape: tuple[int, int],
err: list[float] = None,
corr: np.ndarray = None,
partial_corr: np.ndarray = None,
):
if err is None: if err is None:
err = [0] * len(p) err = [0] * len(p)
@ -472,7 +493,7 @@ class FitRoutine(object):
pass pass
# reshape the correlation matrices # reshape the correlation matrices
if corr is None: if corr is None or not corr_idx:
actual_corr = None actual_corr = None
actual_pcorr = None actual_pcorr = None
else: else:
@ -485,22 +506,24 @@ class FitRoutine(object):
model = data.get_model() model = data.get_model()
self.result[idx] = FitResultCreator.make_with_model( self.result[idx] = FitResultCreator.make_with_model(
model, model=model,
data.x, x_orig=data.x,
data.y, y_orig=data.y,
actual_parameters, p=actual_parameters,
data.fun_kwargs, fun_kwargs=data.fun_kwargs,
data.we_string, we=data.we_string,
data.idx, idx=data.idx,
*shape, nobs=shape[0],
nvar=shape[1],
corr=actual_corr, corr=actual_corr,
pcorr=actual_pcorr, pcorr=actual_pcorr,
data_mode=data.complex_type,
) )
return self.result return self.result
@staticmethod
def _calc_error(jac, chi, nobs, nvars): def _calc_error(jac, chi, nobs, nvars):
# copy of scipy.curve_fit to calculate covariance # copy of scipy.curve_fit to calculate covariance
# noinspection PyTupleAssignmentBalance # noinspection PyTupleAssignmentBalance
try: try:

View File

@ -9,7 +9,7 @@ from ._meta import MultiModel
from .parameter import Parameters, Parameter from .parameter import Parameters, Parameter
class Model(object): class Model:
def __init__(self, model, *args, **kwargs): def __init__(self, model, *args, **kwargs):
self.idx = kwargs.pop('idx', None) self.idx = kwargs.pop('idx', None)

View File

@ -102,12 +102,10 @@ class Parameters(dict):
p._expr = expression p._expr = expression
def prepare_bounds(self): def prepare_bounds(self):
print('prepare_bounds')
original_values = list(self.values()) original_values = list(self.values())
for param in original_values: for param in original_values:
already_with_expression = False already_with_expression = False
for mode, value in (('lower', param.lb), ('upper', param.ub)): for mode, value in (('lower', param.lb), ('upper', param.ub)):
print(mode, value)
if already_with_expression: if already_with_expression:
raise ValueError('Only one boundary can be an expression') raise ValueError('Only one boundary can be an expression')
already_with_expression = self.parse(param, value, bnd=mode) already_with_expression = self.parse(param, value, bnd=mode)

View File

@ -11,6 +11,7 @@ from scipy.stats import f as fdist
from scipy.interpolate import interp1d from scipy.interpolate import interp1d
from ._meta import MultiModel from ._meta import MultiModel
from .model import Model
from .parameter import Parameter from .parameter import Parameter
from ..data.points import Points from ..data.points import Points
from ..data.signals import Signal from ..data.signals import Signal
@ -36,17 +37,30 @@ class FitResultCreator:
else: else:
resid = kwargs['y'] - y_orig resid = kwargs['y'] - y_orig
stats = FitResultCreator.calc_statistics(resid, _y) stats = calc_statistics(resid, _y)
return FitResult(kwargs['x'], kwargs['y'], x_orig, y_orig, params, dict(kwargs['choice']), resid, 0, 0, return FitResult(
kwargs['name'], stats, idx) x=kwargs['x'],
y=kwargs['y'],
x_data=x_orig,
y_data=y_orig,
params=params,
fun_kwargs=dict(kwargs['choice']),
resid=resid,
nobs=0,
nvar=0,
we='',
name=kwargs['name'],
stats=stats,
idx=idx,
)
@staticmethod @staticmethod
def make_with_model( def make_with_model(
model: 'Model', model: 'Model',
x_orig: np.ndarray, x_orig: np.ndarray,
y_orig: np.ndarray, y_orig: np.ndarray,
p: 'Parameters', p: list,
fun_kwargs: dict, fun_kwargs: dict,
we: str, we: str,
idx: str | None, idx: str | None,
@ -54,6 +68,7 @@ class FitResultCreator:
nvar: int, nvar: int,
corr: np.ndarray, corr: np.ndarray,
pcorr: np.ndarray, pcorr: np.ndarray,
data_mode: int,
) -> FitResult: ) -> FitResult:
if np.all(x_orig > 0) and (np.max(x_orig) > 100 * np.min(x_orig)): if np.all(x_orig > 0) and (np.max(x_orig) > 100 * np.min(x_orig)):
islog = True islog = True
@ -83,17 +98,11 @@ class FitResultCreator:
actual_mode = fun_kwargs['complex_mode'] actual_mode = fun_kwargs['complex_mode']
fun_kwargs['complex_mode'] = 0 fun_kwargs['complex_mode'] = 0
_y = model.func(p_final, _x, **fun_kwargs) _y = check_complex(model.func(p_final, _x, **fun_kwargs), actual_mode, data_mode)
if not actual_mode < 0:
if actual_mode == 1:
_y.imag = 0
elif actual_mode == 2:
_y.real = 0
fun_kwargs['complex_mode'] = actual_mode fun_kwargs['complex_mode'] = actual_mode
stats = FitResultCreator.calc_statistics(_y, resid, nobs, nvar) stats = calc_statistics(_y, resid, nobs, nvar)
varied = [p.var for p in parameters.values()] varied = [p.var for p in parameters.values()]
if corr is None: if corr is None:
@ -134,41 +143,9 @@ class FitResultCreator:
pcorr=partial_correlation, pcorr=partial_correlation,
islog=islog, islog=islog,
func=model, func=model,
data_complex=data_mode,
) )
@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): class FitResult(Points):
@ -191,7 +168,8 @@ class FitResult(Points):
pcorr: np.ndarray = None, pcorr: np.ndarray = None,
islog: bool = False, islog: bool = False,
func=None, func=None,
**kwargs data_complex: int = 1,
**kwargs,
): ):
self.parameter, name = self._prepare_names(params, name) self.parameter, name = self._prepare_names(params, name)
@ -213,6 +191,7 @@ class FitResult(Points):
self.y_data = y_data self.y_data = y_data
self._model_name = name self._model_name = name
self._func = func self._func = func
self._data_complex = data_complex
@staticmethod @staticmethod
def _prepare_names(parameter: dict, modelname: str): def _prepare_names(parameter: dict, modelname: str):
@ -393,8 +372,7 @@ class FitResult(Points):
def get_state(self): def get_state(self):
state = super().get_state() state = super().get_state()
for attr in ['idx', 'fun_kwargs', 'nobs', 'nvar', for attr in ['idx', 'fun_kwargs', 'nobs', 'nvar', 'we', 'islog', 'iscomplex', 'x_data', 'y_data']:
'islog', 'iscomplex', 'x_data', 'y_data']:
state[attr] = getattr(self, attr) state[attr] = getattr(self, attr)
state['name'] = self._model_name state['name'] = self._model_name
@ -412,6 +390,8 @@ class FitResult(Points):
@staticmethod @staticmethod
def set_state(state, **kwargs): def set_state(state, **kwargs):
state['params'] = {k: Parameter.set_state(v) for k, v in state.pop('params').items()} state['params'] = {k: Parameter.set_state(v) for k, v in state.pop('params').items()}
if 'we' not in state:
state['we'] = '?'
data = FitResult(**state) data = FitResult(**state)
return data return data
@ -420,41 +400,32 @@ class FitResult(Points):
if self.func is None: if self.func is None:
raise ValueError('no fit function available to calculate new y values') raise ValueError('no fit function available to calculate new y values')
actual_mode = -1
if 'complex_mode' in self.fun_kwargs:
actual_mode = self.fun_kwargs['complex_mode']
self.fun_kwargs['complex_mode'] = 0
new_fit = self.copy() new_fit = self.copy()
y_values = self.func.func(self.p_final, x_values, **self.fun_kwargs) fun_kwargs = {k: v for k, v in self.fun_kwargs.items()}
if not actual_mode < 0: if self.fun_kwargs.get('complex_mode', -1) == -1:
if actual_mode == 1: fun_kwargs.pop('complex_mode', None)
y_values.imag = 0 y_values = self.func.func(self.p_final, x_values, **fun_kwargs)
elif actual_mode == 2: y_values = check_complex(y_values, self.fun_kwargs.get('complex_mode', -1), self._data_complex)
y_values.real = 0
self.fun_kwargs['complex_mode'] = actual_mode
new_fit.set_data(x_values, y_values, y_err=0.0) new_fit.set_data(x_values, y_values, y_err=0.0)
return new_fit return new_fit
def sub(self, x_values): def sub(self, x_values):
if self.func is None:
raise ValueError('no fit function available to calculate new y values')
part_functions = [] part_functions = []
actual_mode = -1 actual_mode = self.fun_kwargs.get('complex_mode', -1)
if 'complex_mode' in self.fun_kwargs: fun_kwargs = {k: v for k, v in self.fun_kwargs.items()}
actual_mode = self.fun_kwargs['complex_mode'] if self.fun_kwargs.get('complex_mode', -1) == -1:
self.fun_kwargs['complex_mode'] = 0 fun_kwargs.pop('complex_mode', None)
for sub_name, sub_y in zip(self.func.sub_name(), self.func.sub(self.p_final, x_values, **self.fun_kwargs)): for sub_name, sub_y in zip(self.func.sub_name(), self.func.sub(self.p_final, x_values, **self.fun_kwargs)):
if not actual_mode < 0: sub_y = check_complex(sub_y, actual_mode, self._data_complex)
if actual_mode == 1:
sub_y.imag = 0
elif actual_mode == 2:
sub_y.real = 0
if np.iscomplexobj(sub_y):
part_functions.append(Signal(x_values, sub_y, name=sub_name)) part_functions.append(Signal(x_values, sub_y, name=sub_name))
else: else:
part_functions.append(Points(x_values, sub_y, name=sub_name)) part_functions.append(Points(x_values, sub_y, name=sub_name))
@ -462,3 +433,49 @@ class FitResult(Points):
self.fun_kwargs['complex_mode'] = actual_mode self.fun_kwargs['complex_mode'] = actual_mode
return part_functions return part_functions
def check_complex(y, model_complex, data_complex):
if not np.iscomplexobj(y):
return y
if model_complex == 1:
y.imag = 0
if data_complex == 1:
y = y.real
elif model_complex == 2:
y.real = 0
if data_complex == 1:
y = y.imag
return y
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,
'adj. R^2': 1 - (nobs-1) / (dof+1e-13) * (1-r),
'red. chi^2': chi / (dof + 1e-13),
}
stats['AICc'] = stats['AIC'] + 2*(nvar+1)*nvar / (dof - 1 + 1e-13)
return stats

View File

@ -49,7 +49,7 @@ class AsciiReader:
with self.fname.open('r') as f: with self.fname.open('r') as f:
for i, line in enumerate(islice(f, len(self.header)+len(self.lines), num_lines)): for i, line in enumerate(islice(f, len(self.header)+len(self.lines), num_lines)):
line = line.strip('\n\t\r, ') line = line.strip('\n\t\r, ')
line = re.sub(r'[\t ;,] *', ';', line) line = re.sub(r'[\t ;,]+', ';', line)
line = line.split(';') line = line.split(';')
try: try:
@ -121,6 +121,9 @@ class AsciiReader:
if y is None: if y is None:
y = list(range(int(len(x) != 0), max(self.width))) y = list(range(int(len(x) != 0), max(self.width)))
if col_names is not None:
col_names = [col_names[y_i] for y_i in y]
cols = x + y + yerr cols = x + y + yerr
with self.fname.open('rb') as fh: with self.fname.open('rb') as fh:
tmp_ = re.sub(b'[;,]', b' ', fh.read()) tmp_ = re.sub(b'[;,]', b' ', fh.read())
@ -180,7 +183,13 @@ class AsciiReader:
single_len = 2 single_len = 2
stepsize = 2 stepsize = 2
cls = {'points': Points, 'fid': FID, 'spectrum': Spectrum, 'bds': BDS, 'dsc': DSC}[mode] cls = {
'points': Points,
'fid': FID,
'spectrum': Spectrum,
'bds': BDS,
'dsc': DSC,
}[mode]
for j in range(1, num_y+1, stepsize): for j in range(1, num_y+1, stepsize):
if col_names is not None: if col_names is not None:
@ -188,7 +197,7 @@ class AsciiReader:
kwargs['name'] = col_names[j-1] kwargs['name'] = col_names[j-1]
elif num_y > single_len: elif num_y > single_len:
# more than one axis, append column number # more than one axis, append column number
kwargs['name'] = filename + '_' + str(y[j-1]) kwargs['name'] = f'{filename}_{y[j-1]+1}'
if j+num_y < raw_data.shape[2]: if j+num_y < raw_data.shape[2]:
kwargs['y_err'] = raw_data[i, :, j+num_y] kwargs['y_err'] = raw_data[i, :, j+num_y]

View File

@ -11,7 +11,8 @@ try:
from scipy.integrate import simpson from scipy.integrate import simpson
except ImportError: except ImportError:
from scipy.integrate import simps as simpson from scipy.integrate import simps as simpson
from scipy.interpolate import interp1d from scipy.interpolate import CubicSpline
ReferenceValue = namedtuple('Reference', ['name', 'transitions']) ReferenceValue = namedtuple('Reference', ['name', 'transitions'])
Cyclohexane = ReferenceValue('Cyclohexane', [(-87.06+273.15, 79.58), (6.54+273.15, None)]) Cyclohexane = ReferenceValue('Cyclohexane', [(-87.06+273.15, 79.58), (6.54+273.15, None)])
@ -38,7 +39,7 @@ class DSCSample:
def read_file(self, fname: str | Path) -> None: def read_file(self, fname: str | Path) -> None:
fname = Path(fname) fname = Path(fname)
# file contains weird deg C character in stupiod ISO encoding # file contains weird deg C character in stupid ISO encoding
with fname.open('r', encoding='iso-8859-15') as f: with fname.open('r', encoding='iso-8859-15') as f:
ii = 1 ii = 1
for line in f: for line in f:
@ -144,9 +145,12 @@ class DSCCalibrator:
self.reference = [] self.reference = []
self.ref_list = [] self.ref_list = []
def set_measurement(self, def set_measurement(
fname: str | Path | DSCSample, mode: str = 'sample', self: DSCCalibrator,
reference: ReferenceValue = Cyclohexane): fname: str | Path | DSCSample,
mode: str = 'sample',
reference: ReferenceValue = Cyclohexane
):
if mode not in ['sample', 'empty', 'reference']: if mode not in ['sample', 'empty', 'reference']:
raise ValueError(f'Unknown mode {mode}, not "sample", "empty", "reference"') raise ValueError(f'Unknown mode {mode}, not "sample", "empty", "reference"')
if mode == 'reference' and not isinstance(reference, ReferenceValue): if mode == 'reference' and not isinstance(reference, ReferenceValue):
@ -266,7 +270,12 @@ class DSCCalibrator:
return sol return sol
def get_data(self, idx: int, slope: str = 'iso', limits: tuple[float, float] = None): def get_data(
self: DSCCalibrator,
idx: int,
slope: str = 'iso',
limits: tuple[float, float] = None
) -> tuple[np.ndarray, np.ndarray, np.ndarray,np.ndarray | None, np.ndarray]:
if self.sample.steps[idx][0] == 'i': if self.sample.steps[idx][0] == 'i':
raise ValueError('baseline correction is not implemented for isotherms') raise ValueError('baseline correction is not implemented for isotherms')
@ -292,7 +301,7 @@ class DSCCalibrator:
empty_y = empty_data[1] empty_y = empty_data[1]
if self.sample.length(idx) != self.empty.length(idx_empty): if self.sample.length(idx) != self.empty.length(idx_empty):
with np.errstate(all='ignore'): with np.errstate(all='ignore'):
empty_y = interp1d(empty_data[2]-empty_data[2, 0], empty_data[1], fill_value='extrapolate')(sample_data[2, 0]) empty_y = CubicSpline(empty_data[2]-empty_data[2, 0], empty_data[1], extrapolate=True)(sample_data[2] - sample_data[2, 0])
sample_data[1] -= empty_y sample_data[1] -= empty_y
drift_value = sample_data.copy()[(2, 1), :] drift_value = sample_data.copy()[(2, 1), :]
@ -346,9 +355,10 @@ class DSCCalibrator:
offset = region[0, 0] offset = region[0, 0]
sample_data[1] -= m * (sample_data[2] - region[1, 0]) + offset sample_data[1] -= m * (sample_data[2] - region[1, 0]) + offset
line = np.array([[sample_data[2, 0], sample_data[2, -1]], line = np.array([
[m * (sample_data[2, 0] - region[1, 0]) + offset, [sample_data[2, 0], sample_data[2, -1]],
m * (sample_data[2, -1] - region[1, 0]) + offset]]) [m * (sample_data[2, 0] - region[1, 0]) + offset, m * (sample_data[2, -1] - region[1, 0]) + offset]
])
else: else:
line = np.array([[sample_data[2, 0], sample_data[2, -1]], [0, 0]]) line = np.array([[sample_data[2, 0], sample_data[2, -1]], [0, 0]])

View File

@ -48,7 +48,7 @@ class FCReader:
found_temperature = filename.stem found_temperature = filename.stem
if filename.is_file(): if filename.is_file():
if region is None: if (region is None) or (region == (None, None)):
_temp = self._read_from_hdf(filename) _temp = self._read_from_hdf(filename)
else: else:
_temp = self._read_signals(filename, region) _temp = self._read_signals(filename, region)

View File

@ -115,6 +115,7 @@ class GraceEditor:
return s return s
def parse(self, filename: str | pathlib.Path): def parse(self, filename: str | pathlib.Path):
self.clear()
self.file = pathlib.Path(filename) self.file = pathlib.Path(filename)
# we start always with the header # we start always with the header
@ -215,9 +216,8 @@ class GraceEditor:
def _make_graph(self, line: str): def _make_graph(self, line: str):
m = self._RE_GRAPH_START.match(line) m = self._RE_GRAPH_START.match(line)
g_idx = int(m.group(1)) g_idx = int(m.group(1))
if g_idx < len(self.graphs): while g_idx < len(self.graphs):
# this assumes that graphs are ordered in agr file even if missing, e.g. we read gß, g1, g3, ... # this assumes that graphs are ordered in agr file even if missing, e.g. we read gß, g1, g3, ...
while g_idx != len(self.graphs):
self.graphs.append(GraceGraph(len(self.graphs))) self.graphs.append(GraceGraph(len(self.graphs)))
self.graphs.append(GraceGraph(g_idx)) self.graphs.append(GraceGraph(g_idx))
@ -354,8 +354,8 @@ class GraceHeader(list):
class GraceProperties(list): class GraceProperties(list):
_RE_ENTRY = re.compile(r'(?!.*(on|off)$)' # ignore lines that end with on or off _RE_ENTRY = re.compile(r'(?!.*(on|off)$)' # ignore lines that end with on or off
r'@\s*(?P<graph>[gs]\d+)?\s*' # @ maybe followed by g0 or s12 r'@\s*(?P<graph>[gs]\d+)?\s*' # @ maybe followed by g0 or s12
r'(?P<key>[\w\s]*)\s+' # key: stops at last space unless comma-separated values r'(?P<key>[\w\s]*\w)*\s+' # key: stops at last space unless comma-separated values
r'(?P<val>(?:\s*[\d\w.+-]+\s*,)*\s*[\\{}\"\w.+\- ]+)', # one value, maybe more with commas r'(?P<val>(?<!^)(?:\s*[\d\w.+-]+\s*,)*\s*[\S ]+)', # one value, maybe more with commas
re.IGNORECASE | re.VERBOSE) re.IGNORECASE | re.VERBOSE)
_RE_ONOFF = re.compile(r'@\s(?P<graph>[gs]\d+)*\s*(?P<val>[\w\s]*)\s+(on|off)') _RE_ONOFF = re.compile(r'@\s(?P<graph>[gs]\d+)*\s*(?P<val>[\w\s]*)\s+(on|off)')
@ -730,7 +730,11 @@ class GraceRegion(list):
def _convert_to_value(parse_string): def _convert_to_value(parse_string):
if re.match(r'\".*\"', parse_string):
tuples = [parse_string]
else:
tuples = parse_string.split(',') tuples = parse_string.split(',')
for i, v in enumerate(tuples): for i, v in enumerate(tuples):
v = v.strip() v = v.strip()

View File

@ -3,14 +3,21 @@ import numpy.polynomial.polynomial as poly
from scipy import signal as signal from scipy import signal as signal
__all__ = ['smooth', 'loess', 'savgol', __all__ = [
'running_max', 'running_min', 'smooth',
'running_var', 'running_std', 'loess',
'running_median', 'running_mean', 'savgol',
'running_sum'] 'running_max',
'running_min',
'running_var',
'running_std',
'running_median',
'running_mean',
'running_sum',
]
def loess(x, y, window_size, it=0, deg=2): def loess(x, y, window_size: int, it: int = 0, deg: int = 2):
# ULTRA LANGSAM !!! # ULTRA LANGSAM !!!
it = max(it, 0) it = max(it, 0)
@ -81,19 +88,19 @@ def savgol(x, y, window_size: int, deg: int = 2, mode: str = 'mirror'):
return new_y return new_y
def running_mean(x, y, window_size): def running_mean(x, y, window_size: int):
return _running_func(np.nanmean, x, y, window_size) return _running_func(np.nanmean, x, y, window_size)
def running_median(x, y, window_size): def running_median(x, y, window_size: int):
return _running_func(np.nanmedian, x, y, window_size) return _running_func(np.nanmedian, x, y, window_size)
def running_std(x, y, window_size): def running_std(x, y, window_size: int):
return _running_func(np.nanstd, x, y, window_size) return _running_func(np.nanstd, x, y, window_size)
def running_var(x, y, window_size): def running_var(x, y, window_size: int):
return _running_func(np.nanvar, x, y, window_size) return _running_func(np.nanvar, x, y, window_size)
@ -132,11 +139,27 @@ def _moving_window(arr, nn):
return np.lib.stride_tricks.as_strided(arr, shapes, strides) return np.lib.stride_tricks.as_strided(arr, shapes, strides)
_funcs = {'loess': loess, 'savgol': savgol, 'mean': running_mean, 'median': running_median, _funcs = {
'std': running_std, 'var': running_var, 'max': running_max, 'min': running_min, 'sum': running_sum} '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): def smooth(
data: 'Data',
window_size: int,
mode: str = 'mean',
logx: bool = False,
logy: bool = False,
**kwargs
):
try: try:
func = _funcs[mode] func = _funcs[mode]
except KeyError: except KeyError:
@ -162,6 +185,6 @@ def smooth(data, window_size, mode='mean', logx=False, logy=False, **kwargs):
new_y = 10**new_y new_y = 10**new_y
new_data = data.copy() new_data = data.copy()
new_data.set_data(x=new_x, y=new_y, y_err=None) new_data.set_data(x=new_x, y=new_y, y_err=np.zeros_like(new_y))
return new_data return new_data

View File

@ -191,6 +191,18 @@ class PowerLawCross:
return ret_val return ret_val
class Sinc:
type = 'Basic'
name = 'Sinc'
equation = 'C * sinc((x-x_{0})/w)'
params = ['C', 'x_{0}', 'w']
@staticmethod
def func(x, c: float, x0: float, w: float):
# numpy sinc is defined as sin(pi*x)/(pi*x)
return c * np.sinc(((x-x0)/w)/np.pi)
class Sine: class Sine:
""" """
Wavy sine function Wavy sine function

View File

@ -1,5 +1,6 @@
import numpy as np import numpy as np
from . import PowerLaw
from ..distributions import Debye, ColeCole, ColeDavidson, KWW, HavriliakNegami from ..distributions import Debye, ColeCole, ColeDavidson, KWW, HavriliakNegami
from ..utils.constants import epsilon0 from ..utils.constants import epsilon0
@ -232,8 +233,8 @@ class DCCondBDS:
class DerivativeHavriliakNegami: class DerivativeHavriliakNegami:
name = 'Derivative HN' name = 'Havriliak-Negami (der.)'
type = 'Dielectric Spectroscopy' type = 'Dielectric Spectroscopy (derivative)'
params = [r'\Delta\epsilon', r'\tau', r'\alpha', r'\gamma'] params = [r'\Delta\epsilon', r'\tau', r'\alpha', r'\gamma']
choices = [ choices = [
('x axis', 'xaxis', {'Frequency': 'freq', 'Omega': 'omega'}) ('x axis', 'xaxis', {'Frequency': 'freq', 'Omega': 'omega'})
@ -254,8 +255,8 @@ class DerivativeHavriliakNegami:
class DerivativeColeCole: class DerivativeColeCole:
name = 'Derivative CC' name = 'Cole-Cole (der.)'
type = 'Dielectric Spectroscopy' type = 'Dielectric Spectroscopy (derivative)'
params = [r'\Delta\epsilon', r'\tau', r'\alpha'] params = [r'\Delta\epsilon', r'\tau', r'\alpha']
bounds = [(0, None), (0, None), (0, 1)] bounds = [(0, None), (0, None), (0, 1)]
choices = [ choices = [
@ -276,8 +277,8 @@ class DerivativeColeCole:
class DerivativeColeDavidson: class DerivativeColeDavidson:
name = 'Derivative CD' name = 'Cole-Davidson (der.)'
type = 'Dielectric Spectroscopy' type = 'Dielectric Spectroscopy (derivative)'
params = [r'\Delta\epsilon', r'\tau', r'\gamma'] params = [r'\Delta\epsilon', r'\tau', r'\gamma']
bounds = [(0, None), (0, None), (0, 1)] bounds = [(0, None), (0, None), (0, 1)]
choices = [ choices = [
@ -295,8 +296,8 @@ class DerivativeColeDavidson:
class _DerivativeHNWithHF: class _DerivativeHNWithHF:
name = 'Derivative (HN + HF wing)' name = 'HN + HF wing (der.)'
type = 'Dielectric Spectroscopy' type = 'Dielectric Spectroscopy (derivative)'
params = [r'\Delta\epsilon', r'\tau', r'\alpha', r'\gamma', r'\tau_{c}', r'\delta'] params = [r'\Delta\epsilon', r'\tau', r'\alpha', r'\gamma', r'\tau_{c}', r'\delta']
bounds = [(0, None), (0, None), (0, 1), (0, 1), (0, None), (0, 1)] bounds = [(0, None), (0, None), (0, 1), (0, 1), (0, None), (0, 1)]
choices = [ choices = [
@ -325,8 +326,8 @@ class _DerivativeHNWithHF:
class DerivativeCCWithHF: class DerivativeCCWithHF:
name = 'Derivative (CC + HF wing)' name = 'CC + HF wing (der.)'
type = 'Dielectric Spectroscopy' type = 'Dielectric Spectroscopy (derivative)'
params = [r'\Delta\epsilon', r'\tau', r'\alpha', r'\tau_{c}', r'\delta'] params = [r'\Delta\epsilon', r'\tau', r'\alpha', r'\tau_{c}', r'\delta']
bounds = [(0, None), (0, None), (0, 1), (0, None), (0, 1)] bounds = [(0, None), (0, None), (0, 1), (0, None), (0, 1)]
choices = [ choices = [
@ -339,8 +340,8 @@ class DerivativeCCWithHF:
class DerivativeCDWithHF: class DerivativeCDWithHF:
name = 'Derivative (CD + HF wing)' name = 'CD + HF wing (der.)'
type = 'Dielectric Spectroscopy' type = 'Dielectric Spectroscopy (derivative)'
params = [r'\Delta\epsilon', r'\tau', r'\gamma', r'\tau_{c}', r'\delta'] params = [r'\Delta\epsilon', r'\tau', r'\gamma', r'\tau_{c}', r'\delta']
bounds = [(0, None), (0, None), (0, 1), (0, None), (0, 1)] bounds = [(0, None), (0, None), (0, 1), (0, None), (0, 1)]
choices = [ choices = [
@ -352,6 +353,22 @@ class DerivativeCDWithHF:
return _DerivativeHNWithHF.func(x, deps, tau, 1, gamma, tauc, delta, xaxis=xaxis) return _DerivativeHNWithHF.func(x, deps, tau, 1, gamma, tauc, delta, xaxis=xaxis)
class PowerLawBDSDer:
name = 'Power Law'
type = 'Dielectric Spectroscopy (derivative)'
equation = r'A * \omega^{n}'
params = ['A', 'n']
bounds = [(None, None), (None, None)]
choices = [
('x axis', 'xaxis', {'Frequency': 'freq', 'Omega': 'omega'})
]
@staticmethod
def func(x, a, n, xaxis: str = 'freq'):
_w = _convert_x_to_omega(x, xaxis=xaxis)
return a * _w ** n
def _convert_x_to_omega(x, xaxis: str = 'freq'): def _convert_x_to_omega(x, xaxis: str = 'freq'):
if xaxis not in ['freq', 'omega']: if xaxis not in ['freq', 'omega']:
raise ValueError(f'Argument `xaxis` is `freq` or `omega`, given is {xaxis!r}') raise ValueError(f'Argument `xaxis` is `freq` or `omega`, given is {xaxis!r}')

View File

@ -2,6 +2,7 @@ import numpy as np
from ..distributions import * from ..distributions import *
from ..distributions.energy import EnergyBarriers from ..distributions.energy import EnergyBarriers
from ..distributions.gengamma import GGAlpha
from ..distributions.intermolecular import FFHS from ..distributions.intermolecular import FFHS
from ..nmr.relaxation import Relaxation from ..nmr.relaxation import Relaxation
from ..utils.constants import gamma from ..utils.constants import gamma
@ -82,6 +83,13 @@ class FFHSFC(_AbstractFC):
relax = Relaxation(distribution=FFHS) relax = Relaxation(distribution=FFHS)
class GGAFC(_AbstractFC):
name = 'GG(alpha)'
params = _AbstractFC.params + [r'\alpha', r'\beta']
bounds = _AbstractFC.bounds + [(None, None), (None, None)]
relax = Relaxation(distribution=GGAlpha)
class EnergyFC(_AbstractFC): class EnergyFC(_AbstractFC):
name = 'Energy distribution' name = 'Energy distribution'
params = ['C', 'T'] + EnergyBarriers.parameter params = ['C', 'T'] + EnergyBarriers.parameter

View File

@ -1,19 +1,31 @@
from __future__ import annotations
import numpy as np import numpy as np
from scipy import special as special from scipy import special as special
from ..utils import kB from ..utils import kB
class Weight2Phase: class Weight:
type = 'Line shape' type = 'Line shape'
name = 'Weighting factor' name = 'Weighting factor'
equation = r'A*[0.5 + 0.5 erf[(x-T_{0})/\DeltaT]] + A_{0}' equation = r'A * [0.5 \pm 0.5 erf[(x-T_{0})/\DeltaT]] + A_{0}'
params = ['T_{0}', r'\DeltaT', 'A', 'A_{0}'] params = ['T_{0}', r'\DeltaT', 'A', 'A_{0}']
choices = [('Direction', 'sign', {'increase': '+', 'decrease': '-'})]
bounds = [(0, None), (0, None), (None, None), (None, None)] bounds = [(0, None), (0, None), (None, None), (None, None)]
@staticmethod @staticmethod
def func(x, t0, dt, amp, off): def func(x: np.ndarray | float, t0: float, dt: float, amp: float, off: float, sign: str = '+') -> np.ndarray | float:
return amp*(0.5 + 0.5*special.erf((x-t0)/dt)) + off if sign not in '+-':
raise ValueError(f"`value` is `+` or `-`, not {sign}")
error_func = 1
if sign == '+':
error_func += special.erf((x-t0)/dt)
else:
error_func -= special.erf((x - t0) / dt)
return amp * error_func / 2. + off
class HendricksonBray: class HendricksonBray:
@ -24,5 +36,5 @@ class HendricksonBray:
bounds = [(0, None)] * 4 bounds = [(0, None)] * 4
@staticmethod @staticmethod
def func(x, a, b, e, w0): def func(x: np.ndarray | float, a: float, b: float, e: float, w0: float) -> np.ndarray | float:
return a*b / (b + (a-b)*np.exp(-e/kB/x)) + w0 return a*b / (b + (a-b)*np.exp(-e/kB/x)) + w0

View File

@ -525,7 +525,7 @@ class RelaxationEvaluation(Relaxation):
dist_parameter: tuple | list = None, dist_parameter: tuple | list = None,
prefactor: tuple | list | float = None, prefactor: tuple | list | float = None,
coupling_kwargs: dict = None, coupling_kwargs: dict = None,
) -> None: ) -> tuple[float, float] :
""" """
Determine a single parameter from a T1 minimum. Determine a single parameter from a T1 minimum.
It replaces the previously set value. It replaces the previously set value.

View File

@ -5,3 +5,10 @@ from .constants import *
NUMBER_RE = re.compile(r'[-+]?\d*[+p.]?\d+([eE][-+]?\d+)?', re.MULTILINE) NUMBER_RE = re.compile(r'[-+]?\d*[+p.]?\d+([eE][-+]?\d+)?', re.MULTILINE)
UNSIGNED_NUMBER_RE = re.compile(r'[-+]?\d*[+p.]?\d+([eE][-+]?\d+)?', re.MULTILINE) UNSIGNED_NUMBER_RE = re.compile(r'[-+]?\d*[+p.]?\d+([eE][-+]?\d+)?', re.MULTILINE)
def numbers_from_string(any_string: str) -> list[float]:
matches = []
for m in NUMBER_RE.finditer(any_string):
matches.append(float(m.group().replace('p', '.')))
return matches

View File

@ -33,9 +33,9 @@ big_greek = [
special_chars = [ special_chars = [
r'\infty \int \sum \langle \rangle \pm \perp \para \leftarrow \rightarrow \leftrightarrow \cdot \hbar \n', r'\infty \int \sum \langle \rangle \pm \perp \para \leftarrow \rightarrow \leftrightarrow \cdot \hbar \n',
'\u221e \u222b \u2211 \u27e8 \u27e9 \u00b1 \u27c2 \u2225 \u21d0 \u21d2 \u21d4 \u00b7 \u0127 <br>', '\u221e \u222b \u2211 \u27e8 \u27e9 \u00b1 \u27c2 \u2225 \u21d0 \u21d2 \u21d4 \u00b7 \u0127 <br>',
r'\f{Symbol}¥\f{} \f{Symbol}ò\f{} \f{Symbol}å\f{} \f{Symbol}á\f{} \f{Symbol}ñ\f{} \f{Symbol}±\f{} \n' 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'\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}', r'h\h{-0.6}\v{0.3}-\v{-0.3}\h{0.3} \n',
r'infty int sum < > \+- perp para <- -> <-> \* hbar \s', r'infty int sum < > \+- perp para <- -> <-> \* hbar \s',
] ]
funcs = [ funcs = [

View File

@ -336,7 +336,7 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Token:&lt;br/&gt;[abc]: Matches any of a, b, or c&lt;br/&gt;[a-z]: Matches any digit in the range a-z&lt;br/&gt;\d: Matches any digit in the range 0-9 (equal to [0-9}&lt;/p&gt;&lt;p&gt;Quantifiers:&lt;br/&gt;a*: 0 or more of a&lt;br/&gt;a*: 1 or more of a&lt;br/&gt;a?: 0 or 1 of a&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Token:&lt;br/&gt;[abc]: Matches any of a, b, or c&lt;br/&gt;[a-z]: Matches any digit in the range a-z&lt;br/&gt;\d: Matches any digit in the range 0-9 (equal to [0-9}&lt;/p&gt;&lt;p&gt;Quantifiers:&lt;br/&gt;a+: 0 or more of a&lt;br/&gt;a*: 1 or more of a&lt;br/&gt;a?: 0 or 1 of a&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -172,6 +172,14 @@
<property name="title"> <property name="title">
<string>&amp;Data</string> <string>&amp;Data</string>
</property> </property>
<widget class="QMenu" name="menuCut_to_visible_range">
<property name="title">
<string>Cut to visible range</string>
</property>
<addaction name="separator"/>
<addaction name="action_cut_xaxis"/>
<addaction name="action_cut_yaxis"/>
</widget>
<addaction name="action_new_set"/> <addaction name="action_new_set"/>
<addaction name="action_delete_sets"/> <addaction name="action_delete_sets"/>
<addaction name="actionMove_between_plots"/> <addaction name="actionMove_between_plots"/>
@ -181,9 +189,10 @@
<addaction name="action_sort_pts"/> <addaction name="action_sort_pts"/>
<addaction name="actionSkip_points"/> <addaction name="actionSkip_points"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="action_cut"/> <addaction name="menuCut_to_visible_range"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionChange_datatypes"/> <addaction name="actionChange_datatypes"/>
<addaction name="actionUse_script"/>
</widget> </widget>
<widget class="QMenu" name="menuHelp"> <widget class="QMenu" name="menuHelp">
<property name="title"> <property name="title">
@ -247,6 +256,7 @@
<addaction name="action_no_range"/> <addaction name="action_no_range"/>
<addaction name="action_x_range"/> <addaction name="action_x_range"/>
<addaction name="action_custom_range"/> <addaction name="action_custom_range"/>
<addaction name="actionExclude_region"/>
</widget> </widget>
<addaction name="action_FitWidget"/> <addaction name="action_FitWidget"/>
<addaction name="separator"/> <addaction name="separator"/>
@ -437,30 +447,6 @@
<addaction name="t1action"/> <addaction name="t1action"/>
<addaction name="actionCalculateT1"/> <addaction name="actionCalculateT1"/>
</widget> </widget>
<widget class="QToolBar" name="toolBar_fit">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Fit</string>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="action_FitWidget"/>
</widget>
<widget class="QToolBar" name="toolBar_spectrum"> <widget class="QToolBar" name="toolBar_spectrum">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
@ -885,11 +871,6 @@
<string>Integration...</string> <string>Integration...</string>
</property> </property>
</action> </action>
<action name="action_cut">
<property name="text">
<string>Cut to visible range</string>
</property>
</action>
<action name="actionMove_between_plots"> <action name="actionMove_between_plots">
<property name="text"> <property name="text">
<string>Move sets...</string> <string>Move sets...</string>
@ -1045,6 +1026,35 @@
<string>TNMH...</string> <string>TNMH...</string>
</property> </property>
</action> </action>
<action name="actionExclude_region">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Exclude region</string>
</property>
</action>
<action name="action_cut_xaxis">
<property name="text">
<string>x axis</string>
</property>
<property name="toolTip">
<string>Remove data points outside visible x range.</string>
</property>
</action>
<action name="action_cut_yaxis">
<property name="text">
<string>y axis</string>
</property>
<property name="toolTip">
<string>Remove data points outside visible y range. Uses real part of points.</string>
</property>
</action>
<action name="actionUse_script">
<property name="text">
<string>Use script...</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>

View File

@ -106,6 +106,19 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QToolButton" name="pokemon_toolbutton">
<property name="text">
<string/>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextOnly</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@ -96,11 +96,14 @@
<item> <item>
<widget class="QGroupBox" name="region_box"> <widget class="QGroupBox" name="region_box">
<property name="title"> <property name="title">
<string>Evaluate region (empty values default to start/end)</string> <string>Evaluate region (empty values default to values of the script)</string>
</property> </property>
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing"> <property name="spacing">
<number>3</number> <number>3</number>
@ -311,7 +314,11 @@
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="3" column="1">
<widget class="QComboBox" name="graph_comboBox"/> <widget class="QComboBox" name="graph_comboBox">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item> </item>
<item row="3" column="0"> <item row="3" column="0">
<widget class="QCheckBox" name="graph_checkbox"> <widget class="QCheckBox" name="graph_checkbox">

View File

@ -86,11 +86,7 @@
<layout class="QVBoxLayout" name="verticalLayout_6"/> <layout class="QVBoxLayout" name="verticalLayout_6"/>
</widget> </widget>
</widget> </widget>
<widget class="CodeEditor" name="plainTextEdit"> <widget class="EditorWidget" name="editor" native="true"/>
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</widget> </widget>
</item> </item>
<item> <item>
@ -107,9 +103,10 @@
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>
<class>CodeEditor</class> <class>EditorWidget</class>
<extends>QPlainTextEdit</extends> <extends>QWidget</extends>
<header>..lib.codeeditor</header> <header>..lib.codeeditor</header>
<container>1</container>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>

View File

@ -67,6 +67,12 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="maximumSize">
<size>
<width>160</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>Initial values</string> <string>Initial values</string>
</property> </property>
@ -92,6 +98,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="reset_button">
<property name="text">
<string>Use global</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>
@ -151,6 +164,12 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Lower bound. Same bound is used for all data. Leave empty for no boundary condition.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Lower bound. Same bound is used for all data. Leave empty for no boundary condition.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
@ -195,6 +214,12 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Upper bound. Same bound is used for all data. Leave empty for no boundary condition.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Upper bound. Same bound is used for all data. Leave empty for no boundary condition.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>

View File

@ -31,13 +31,13 @@
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout_3"> <layout class="QGridLayout" name="gridLayout_3">
<property name="leftMargin"> <property name="leftMargin">
<number>3</number> <number>6</number>
</property> </property>
<property name="topMargin"> <property name="topMargin">
<number>3</number> <number>3</number>
</property> </property>
<property name="rightMargin"> <property name="rightMargin">
<number>3</number> <number>6</number>
</property> </property>
<property name="bottomMargin"> <property name="bottomMargin">
<number>3</number> <number>3</number>
@ -45,27 +45,104 @@
<property name="spacing"> <property name="spacing">
<number>3</number> <number>3</number>
</property> </property>
<item row="2" column="1"> <item row="2" column="3">
<widget class="QCheckBox" name="logy_box"> <spacer name="horizontalSpacer_2">
<property name="layoutDirection"> <property name="orientation">
<enum>Qt::RightToLeft</enum> <enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="4">
<widget class="QToolButton" name="autoscale_box">
<property name="toolTip">
<string>Auto-scale graph for all sets</string>
</property> </property>
<property name="text"> <property name="text">
<string>logarithmic y axis</string> <string>Autoscale all sets</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="2">
<widget class="QGroupBox" name="verticalGroupBox_2">
<property name="title">
<string>Logarithmic axes</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QCheckBox" name="logx_box">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>x axis</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="logy_box">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>y axis</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QCheckBox" name="logx_box"> <widget class="QGroupBox" name="verticalGroupBox">
<property name="layoutDirection"> <property name="title">
<enum>Qt::RightToLeft</enum> <string>Residuals</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QRadioButton" name="rel_dev_button">
<property name="text"> <property name="text">
<string>logarithmic x axis</string> <string>relative deviation</string>
</property> </property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget> </widget>
</item> </item>
<item row="0" column="0" colspan="2"> <item>
<widget class="QRadioButton" name="abs_dev_button">
<property name="text">
<string>absolute deviation</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="0" colspan="5">
<widget class="GraphicsLayoutWidget" name="graphicsView"/> <widget class="GraphicsLayoutWidget" name="graphicsView"/>
</item> </item>
</layout> </layout>
@ -175,13 +252,6 @@
</widget> </widget>
</widget> </widget>
</item> </item>
<item row="8" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Retry</set>
</property>
</widget>
</item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QTableWidget" name="param_tableWidget"> <widget class="QTableWidget" name="param_tableWidget">
<property name="sizePolicy"> <property name="sizePolicy">
@ -231,152 +301,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Output</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<property name="spacing">
<number>3</number>
</property>
<item row="1" column="0">
<widget class="QCheckBox" name="extrapolate_box">
<property name="toolTip">
<string>Extrapolates only main function</string>
</property>
<property name="text">
<string>Extrapolate curves</string>
</property>
</widget>
</item>
<item row="0" column="5">
<widget class="QCheckBox" name="parameter_checkbox">
<property name="text">
<string>Plot parameter</string>
</property>
</widget>
</item>
<item row="1" column="6">
<widget class="QComboBox" name="graph_comboBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="5">
<widget class="QCheckBox" name="graph_checkBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>New graph for parameter</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="minx_line">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Leave empty to start at lowest point</string>
</property>
<property name="placeholderText">
<string>min x</string>
</property>
</widget>
</item>
<item row="0" column="4" rowspan="2">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="maxx_line">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Leave empty to start at highest point</string>
</property>
<property name="placeholderText">
<string>max x</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLineEdit" name="numx_line">
<property name="enabled">
<bool>false</bool>
</property>
<property name="placeholderText">
<string># pts</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="curve_checkbox">
<property name="text">
<string>Plot fit curve</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="partial_checkBox">
<property name="text">
<string>Plot partial functions</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="0" column="0"> <item row="0" column="0">
<widget class="ElideComboBox" name="sets_comboBox"> <widget class="ElideComboBox" name="sets_comboBox">
<property name="sizePolicy"> <property name="sizePolicy">
@ -409,6 +333,193 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Output</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<property name="spacing">
<number>3</number>
</property>
<item row="1" column="7">
<widget class="QComboBox" name="graph_comboBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="minx_line">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Leave empty to start at lowest point</string>
</property>
<property name="placeholderText">
<string>min x</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="extrapolate_box">
<property name="toolTip">
<string>Extrapolates only main function</string>
</property>
<property name="text">
<string>Extrapolate curves</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLineEdit" name="numx_line">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="placeholderText">
<string># pts</string>
</property>
</widget>
</item>
<item row="1" column="6">
<widget class="QCheckBox" name="graph_checkBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>New graph for parameter</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="maxx_line">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Leave empty to start at highest point</string>
</property>
<property name="placeholderText">
<string>max x</string>
</property>
</widget>
</item>
<item row="0" column="5" rowspan="2">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QCheckBox" name="newx_log_checkbox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>log-spaced?</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="5">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="curve_checkbox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Plot fit curve</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="partial_checkBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Plot partial functions</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="6" colspan="2">
<widget class="QCheckBox" name="parameter_checkbox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Plot parameter</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="8" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Retry</set>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<customwidgets> <customwidgets>
@ -425,4 +536,7 @@
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <connections/>
<buttongroups>
<buttongroup name="buttonGroup"/>
</buttongroups>
</ui> </ui>

View File

@ -449,7 +449,11 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLineEdit" name="title_lineedit"/> <widget class="QLineEdit" name="title_lineedit">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Uses simple latex syntax, does not support italic/math environment. Sub-/superscripts need curly brackets.&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Example: \alpha^{123}&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item> </item>
<item> <item>
<spacer name="horizontalSpacer_3"> <spacer name="horizontalSpacer_3">
@ -475,7 +479,11 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLineEdit" name="xaxis_linedit"/> <widget class="QLineEdit" name="xaxis_linedit">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Uses simple latex syntax, does not support italic/math environment. Sub-/superscripts need curly brackets.&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Example: \alpha^{123}&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item> </item>
<item> <item>
<spacer name="horizontalSpacer_4"> <spacer name="horizontalSpacer_4">
@ -501,7 +509,11 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLineEdit" name="yaxis_linedit"/> <widget class="QLineEdit" name="yaxis_linedit">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Uses simple latex syntax, does not support italic/math environment. Sub-/superscripts need curly brackets.&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Example: \alpha^{123}&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>

View File

@ -119,7 +119,7 @@
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="standardButtons"> <property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> <set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property> </property>
</widget> </widget>
</item> </item>
@ -300,38 +300,5 @@
<tabstop>dest_combobox</tabstop> <tabstop>dest_combobox</tabstop>
</tabstops> </tabstops>
<resources/> <resources/>
<connections> <connections/>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>251</x>
<y>490</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>319</x>
<y>490</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui> </ui>

View File

@ -0,0 +1,460 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>640</width>
<height>642</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="artwork_label">
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="verticalSpacing">
<number>12</number>
</property>
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>National No.</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="nationaldex_label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_1">
<property name="text">
<string>Species</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="species_label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Type</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="type1_label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="type2_label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Abilities</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="ability1_label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="ability2_label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="ability3_label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Height</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="height_label">
<property name="text">
<string>0.0 m</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Weight</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="weight_label">
<property name="text">
<string>0.0 kg</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="gender_label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Stats</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item row="0" column="3">
<widget class="QProgressBar" name="spec_attack_bar">
<property name="styleSheet">
<string notr="true">QProgressBar{
border: none;
background-color: transparent;
text-align: center;
}
QProgressBar::chunk {
background-color: #009cda;
border-radius: 3px;
}</string>
</property>
<property name="maximum">
<number>194</number>
</property>
<property name="value">
<number>24</number>
</property>
<property name="format">
<string>%v</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Special Attack</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QProgressBar" name="spec_defense_bar">
<property name="styleSheet">
<string notr="true">QProgressBar{
border: none;
background-color: transparent;
text-align: center;
}
QProgressBar::chunk {
background-color: #009cda;
border-radius: 3px;
}</string>
</property>
<property name="maximum">
<number>250</number>
</property>
<property name="value">
<number>24</number>
</property>
<property name="format">
<string>%v</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="text">
<string>HP</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QProgressBar" name="hp_bar">
<property name="styleSheet">
<string notr="true">QProgressBar{
border: none;
background-color: transparent;
text-align: center;
}
QProgressBar::chunk {
background-color: #009cda;
border-radius: 3px;
}</string>
</property>
<property name="maximum">
<number>255</number>
</property>
<property name="value">
<number>24</number>
</property>
<property name="format">
<string>%v</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QProgressBar" name="speed_bar">
<property name="styleSheet">
<string notr="true">QProgressBar{
border: none;
background-color: transparent;
text-align: center;
}
QProgressBar::chunk {
background-color: #009cda;
border-radius: 3px;
}</string>
</property>
<property name="maximum">
<number>200</number>
</property>
<property name="value">
<number>24</number>
</property>
<property name="format">
<string>%v</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Speed</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Special Defense</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QProgressBar" name="defense_bar">
<property name="styleSheet">
<string notr="true">QProgressBar{
border: none;
background-color: transparent;
text-align: center;
}
QProgressBar::chunk {
background-color: #009cda;
border-radius: 3px;
}</string>
</property>
<property name="maximum">
<number>250</number>
</property>
<property name="value">
<number>24</number>
</property>
<property name="format">
<string>%v</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QProgressBar" name="attack_bar">
<property name="styleSheet">
<string notr="true">QProgressBar{
border: none;
background-color: transparent;
text-align: center;
}
QProgressBar::chunk {
background-color: #009cda;
border-radius: 3px;
}</string>
</property>
<property name="maximum">
<number>190</number>
</property>
<property name="value">
<number>24</number>
</property>
<property name="format">
<string>%v</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Attack</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Defense</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Evolution chain</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QTableWidget" name="tableWidget">
<property name="styleSheet">
<string notr="true">background-color: transparent;</string>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="gridStyle">
<enum>Qt::NoPen</enum>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,194 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>359</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Random Pokémon</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>National-Dex</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="pokedex_nr">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Name</string>
</property>
<property name="buddy">
<cstring>name</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="name"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Kategorie</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="category">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Typ</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="poketype">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Größe</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="height">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Gewicht</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="weight">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Farbe</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="color">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Mehr...</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="info">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QToolButton" name="prev_button">
<property name="text">
<string>Prev.</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="next_button">
<property name="text">
<string>Next</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Close|QDialogButtonBox::Retry</set>
</property>
<property name="centerButtons">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,219 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1687</width>
<height>991</height>
</rect>
</property>
<property name="windowTitle">
<string>Gotta catch 'em all!</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="2">
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>Random</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QComboBox" name="comboBox_2"/>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBox"/>
</item>
<item row="0" column="4">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="3">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
<item row="1" column="0" colspan="5">
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QTableWidget" name="tableWidget_2">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="gridStyle">
<enum>Qt::NoPen</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>80</number>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>#</string>
</property>
</column>
<column>
<property name="text">
<string>Pokemon</string>
</property>
</column>
<column>
<property name="text">
<string>Type</string>
</property>
</column>
<column>
<property name="text">
<string>Total</string>
</property>
</column>
<column>
<property name="text">
<string>HP</string>
</property>
<property name="toolTip">
<string>Hit Points; Kraftpunkte</string>
</property>
</column>
<column>
<property name="text">
<string>Attack</string>
</property>
<property name="toolTip">
<string>Attacke</string>
</property>
</column>
<column>
<property name="text">
<string>Defense</string>
</property>
<property name="toolTip">
<string>Verteidigung</string>
</property>
</column>
<column>
<property name="text">
<string>Sp. Attack</string>
</property>
<property name="toolTip">
<string>Special Attack; Spezial-Attacke</string>
</property>
</column>
<column>
<property name="text">
<string>Sp. Defense</string>
</property>
<property name="toolTip">
<string>Special Defense; Spezial-Verteidigung</string>
</property>
</column>
<column>
<property name="text">
<string>Speed</string>
</property>
<property name="toolTip">
<string>Initiative</string>
</property>
</column>
<column>
<property name="text">
<string>Height</string>
</property>
<property name="toolTip">
<string>Größe</string>
</property>
</column>
<column>
<property name="text">
<string>Weight</string>
</property>
<property name="toolTip">
<string>Gewicht</string>
</property>
</column>
<column>
<property name="text">
<string>BMI</string>
</property>
<property name="toolTip">
<string>Body-Mass-Index</string>
</property>
</column>
</widget>
<widget class="QTabWidget" name="tabWidget">
<property name="minimumSize">
<size>
<width>418</width>
<height>0</height>
</size>
</property>
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>762</x>
<y>969</y>
</hint>
<hint type="destinationlabel">
<x>762</x>
<y>495</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -6,27 +6,25 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>316</width> <width>417</width>
<height>747</height> <height>746</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Form</string> <string>Form</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QGridLayout" name="gridLayout">
<property name="leftMargin"> <item row="0" column="0" colspan="2">
<number>3</number> <widget class="QLabel" name="label_2">
<property name="text">
<string>Selected points and regions</string>
</property> </property>
<property name="topMargin"> <property name="buddy">
<number>3</number> <cstring>peaktable</cstring>
</property> </property>
<property name="rightMargin"> </widget>
<number>3</number> </item>
</property> <item row="1" column="0" colspan="2">
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QListWidget" name="peaktable"> <widget class="QListWidget" name="peaktable">
<property name="toolTip"> <property name="toolTip">
<string>Edit by entering new value: <string>Edit by entering new value:
@ -39,110 +37,18 @@ Changing between regions and points is NOT possible</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item row="3" column="0">
<widget class="QGroupBox" name="groupBox"> <widget class="QCheckBox" name="special_checkbox">
<property name="title">
<string>Average</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QSpinBox" name="left_pt">
<property name="suffix">
<string> pts</string>
</property>
<property name="prefix">
<string>- </string>
</property>
<property name="maximum">
<number>999</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="right_pt">
<property name="suffix">
<string> pts</string>
</property>
<property name="prefix">
<string>+ </string>
</property>
<property name="maximum">
<number>999</number>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="average_combobox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text"> <property name="text">
<string>Mean</string> <string>Use special value</string>
</property> </property>
</item>
<item>
<property name="text">
<string>Sum</string>
</property>
</item>
<item>
<property name="text">
<string>Integral</string>
</property>
</item>
</widget> </widget>
</item> </item>
</layout> <item row="3" column="1">
</widget> <widget class="QComboBox" name="special_comboBox">
</item> <property name="enabled">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Special value</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool> <bool>false</bool>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QComboBox" name="special_comboBox">
<property name="toolTip"> <property name="toolTip">
<string>Automatic selection of respective points</string> <string>Automatic selection of respective points</string>
</property> </property>
@ -168,38 +74,113 @@ Changing between regions and points is NOT possible</string>
</item> </item>
</widget> </widget>
</item> </item>
</layout> <item row="4" column="0">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>30</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Region around points</string>
</property>
</widget> </widget>
</item> </item>
<item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item> <item>
<widget class="QGroupBox" name="groupBox_3"> <widget class="QLineEdit" name="left_limit"/>
<property name="title"> </item>
<string>Result</string> <item>
<widget class="QLineEdit" name="right_limit"/>
</item>
<item>
<widget class="QComboBox" name="limit_combobox">
<item>
<property name="text">
<string>points</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> </item>
<property name="leftMargin"> <item>
<number>3</number> <property name="text">
<string>range</string>
</property> </property>
<property name="topMargin"> </item>
<number>3</number> </widget>
</item>
</layout>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Aggregation</string>
</property> </property>
<property name="rightMargin"> <property name="buddy">
<number>3</number> <cstring>average_combobox</cstring>
</property> </property>
<property name="bottomMargin"> </widget>
<number>3</number> </item>
<item row="6" column="1">
<widget class="QComboBox" name="average_combobox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<property name="spacing"> <item>
<number>3</number> <property name="text">
<string>Mean</string>
</property> </property>
<item row="0" column="0"> </item>
<item>
<property name="text">
<string>Sum</string>
</property>
</item>
<item>
<property name="text">
<string>Integral</string>
</property>
</item>
<item>
<property name="text">
<string>Std. deviation</string>
</property>
</item>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>New set based on</string>
</property>
<property name="buddy">
<cstring>xbutton</cstring>
</property>
</widget>
</item>
<item row="7" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="xbutton"> <widget class="QCheckBox" name="xbutton">
<property name="text"> <property name="text">
<string>x</string> <string>x</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item>
<widget class="QCheckBox" name="ybutton"> <widget class="QCheckBox" name="ybutton">
<property name="text"> <property name="text">
<string>y</string> <string>y</string>
@ -209,7 +190,49 @@ Changing between regions and points is NOT possible</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0"> </layout>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Group by</string>
</property>
<property name="buddy">
<cstring>group_box</cstring>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QComboBox" name="group_box">
<item>
<property name="text">
<string>&quot;Group&quot; value</string>
</property>
</item>
<item>
<property name="text">
<string>x value</string>
</property>
</item>
</widget>
</item>
<item row="9" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="10" column="0">
<widget class="QCheckBox" name="graph_checkbox"> <widget class="QCheckBox" name="graph_checkbox">
<property name="text"> <property name="text">
<string>New graph?</string> <string>New graph?</string>
@ -219,25 +242,30 @@ Changing between regions and points is NOT possible</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="10" column="1">
<widget class="QComboBox" name="graph_combobox"> <widget class="QComboBox" name="graph_combobox">
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>false</bool>
</property> </property>
</widget> </widget>
</item> </item>
</layout> <item row="12" column="0">
</widget> <spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item> </item>
<item> <item row="11" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>2</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="okButton"> <widget class="QPushButton" name="okButton">
<property name="text"> <property name="text">
<string>Apply</string> <string>Apply</string>
@ -248,10 +276,10 @@ Changing between regions and points is NOT possible</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item row="2" column="0" colspan="2">
<widget class="QPushButton" name="deleteButton"> <widget class="QPushButton" name="deleteButton">
<property name="text"> <property name="text">
<string>Delete selected</string> <string>Delete selection</string>
</property> </property>
<property name="icon"> <property name="icon">
<iconset theme="dialog-cancel"> <iconset theme="dialog-cancel">
@ -260,9 +288,17 @@ Changing between regions and points is NOT possible</string>
</widget> </widget>
</item> </item>
</layout> </layout>
</item>
</layout>
</widget> </widget>
<tabstops>
<tabstop>peaktable</tabstop>
<tabstop>limit_combobox</tabstop>
<tabstop>average_combobox</tabstop>
<tabstop>xbutton</tabstop>
<tabstop>ybutton</tabstop>
<tabstop>group_box</tabstop>
<tabstop>graph_checkbox</tabstop>
<tabstop>graph_combobox</tabstop>
</tabstops>
<resources/> <resources/>
<connections/> <connections/>
</ui> </ui>

Some files were not shown because too many files have changed in this diff Show More