1
0
forked from IPKM/nmreval

Compare commits

..

370 Commits

Author SHA1 Message Date
90084e3481 dev (#297)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 2m35s
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)"
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 2m17s
This reverts commit 7145f9e3cd.
2024-11-14 12:14:17 +01:00
7145f9e3cd dev (#295)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 2m58s
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
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m46s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m40s
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)
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Has been cancelled
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)
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Has been cancelled
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m35s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m33s
fixes #277

Reviewed-on: IPKM/nmreval#278
2024-07-04 10:37:43 +00:00
8f92d8d822 dev (#275)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m38s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m53s
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
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 2m18s
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
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 26s
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
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m27s
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
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m33s
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
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Has been cancelled
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)
Some checks are pending
Build AppImage / Explore-Gitea-Actions (push) Waiting to run
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m32s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m35s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m33s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m32s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m51s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m36s
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
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m45s
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
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m51s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m43s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m47s
closes #231
2024-02-06 17:23:00 +00:00
39b0fe75cb check relative path; remove possible cause of #231 (#232)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m38s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m34s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m53s
closes #225
2024-01-30 18:01:15 +00:00
3626cfc7ea ensure sorted sets before averaging in pick points (#226)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m33s
should finally fix #189
2024-01-30 17:01:13 +00:00
575cb5e8f6 209-fit-tree (#222)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m33s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m35s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m29s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 2m6s
closes #215
2024-01-15 18:41:37 +00:00
d83e112515 bugfixes-022024 (#211)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m30s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m33s
reworked autosave, closes #126; update with restart
2024-01-03 12:30:04 +00:00
58e86f4abc bugfixes-01012024 (#197)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m31s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m48s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m32s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m56s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 2m2s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m59s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m58s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m53s
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)
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Has been cancelled
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m52s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m59s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 2m0s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 2m3s
closes issue #177
2023-12-13 19:10:01 +00:00
a0c07231c3 removed unneeded workflow demo.yaml (#175)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m52s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m25s
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)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m14s
Reviewed-on: IPKM/nmreval#172
2023-12-06 18:48:14 +00:00
f7a17f22cf action_test (#170)
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m3s
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
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 15m12s
2023-12-06 16:51:16 +01:00
bfc25e33f9 build naming fixed, finally
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m1s
2023-12-06 16:41:34 +01:00
772c51669d build naming fixed, finally
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m3s
2023-12-06 16:38:36 +01:00
35ae571de0 build naming fixed,nope 3
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 1m3s
2023-12-06 16:36:56 +01:00
bcc828efdc build naming fixed,nope2
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 27s
2023-12-06 16:33:22 +01:00
4df0ad6d20 build naming fixed,nope
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 28s
2023-12-06 16:31:36 +01:00
adb71257ff build naming fixed,nope
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 11s
2023-12-06 16:29:34 +01:00
c0dabbe9fe build naming fixed
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 24s
2023-12-06 16:21:44 +01:00
500e473212 fixed build nameing, v15
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 24s
2023-12-06 16:19:33 +01:00
3928e02b44 fixed build nameing, v14
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 11s
2023-12-06 16:18:32 +01:00
0c448e8ee9 fixed build nameing, v13
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 11s
2023-12-06 16:14:45 +01:00
6fd44a14fa fixed build nameing, v12
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 12s
2023-12-06 16:12:05 +01:00
9323cb8883 fixed build nameing, v11
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 11s
2023-12-06 16:10:44 +01:00
025b14a288 fixed build nameing, v10
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 11s
2023-12-06 16:09:57 +01:00
a7fed328ae fixed build nameing, v9
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 12s
2023-12-06 16:09:07 +01:00
a09a6bd988 fixed build nameing, v8
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 11s
2023-12-06 16:08:25 +01:00
6ee8d27d4a fixed build nameing, v7
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 12s
2023-12-06 16:07:32 +01:00
a789612eae fixed build nameing, v7
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 11s
2023-12-06 16:06:18 +01:00
c7855a74f4 fixed build nameing, v6
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 11s
2023-12-06 16:04:41 +01:00
d099253812 fixed build nameing, v5
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 11s
2023-12-06 15:58:26 +01:00
7c586c4bf0 fixed build nameing, v4
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 10s
2023-12-06 15:52:27 +01:00
0a9d596a91 fixed build nameing, v3
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 11s
2023-12-06 15:48:45 +01:00
49a1b0d4fa fixed build nameing, v2
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 11s
2023-12-06 15:47:53 +01:00
bd9288d20e fixed build nameing
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 11s
2023-12-06 15:45:19 +01:00
223ddd4cda condensed build steps in workflow
All checks were successful
Build AppImage / Explore-Gitea-Actions (push) Successful in 1m2s
2023-12-06 15:28:26 +01:00
53349d7764 uploading debugging
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 1m11s
2023-12-06 15:24:36 +01:00
4afa7a37ca make the AppImage build docker friendly
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 1m4s
2023-12-06 15:00:25 +01:00
32ca840c78 force a start of gpg-agent, v3
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 13s
2023-12-06 14:53:07 +01:00
a3f57fbf73 force a start of gpg-agent, v2
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 12s
2023-12-06 14:52:26 +01:00
c157ebb0f9 force a start of gpg-agent
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 11s
2023-12-06 14:50:38 +01:00
962802ebe4 using vars instead of env context for variables
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 11s
2023-12-06 14:47:16 +01:00
340bba747a debug
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 11s
2023-12-06 14:42:51 +01:00
331f700933 added proper variables for GPG
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 11s
2023-12-06 14:35:38 +01:00
2a1852b4ca added apt-get update and install for neede packages in workflow
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 12s
2023-12-06 14:30:48 +01:00
98c9354883 added GO_PIPELINE_LABEL to workflow
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 5s
2023-12-06 14:29:36 +01:00
94fc92fb56 workflow fixes
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 5s
2023-12-06 14:16:48 +01:00
7e3541806b preliminary build workflow
Some checks failed
Build AppImage / Explore-Gitea-Actions (push) Failing after 5s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 5s
2023-12-06 14:13:57 +01:00
bd9051be00 vars testing
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 5s
2023-12-06 12:24:46 +01:00
d5293cf534 test ENV variables
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 5s
2023-12-06 12:12:23 +01:00
d4812a25ea changed label to 'bullseye'
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
2023-12-06 12:01:24 +01:00
a37704acc4 moved demo.yaml workflow to correct directory
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 32s
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
Dominik Demuth
8fb17b22fd fit results save weight; closes #139 2023-11-13 18:46:46 +01:00
Dominik Demuth
f04cadf780 remember header settings in ascii dialog; fixes #140 2023-11-13 18:03:57 +01:00
Dominik Demuth
ae45ff2e55 set name of fit results manually; fix #138 2023-11-13 17:17:50 +01:00
Dominik Demuth
735896e68d background color of legend is not changed; fixes #141 2023-11-13 17:11:38 +01:00
Dominik Demuth
aff9cd31a7 add "index" keyword to ascii reader to use row number; part of #135 2023-11-10 18:56:13 +01:00
8772fa9e7a add __future__ import to fix #136 2023-11-10 10:17:42 +00:00
Dominik Demuth
9c5d91918f added f-omega option to log-fourier transform; closes #134 2023-11-09 17:50:29 +01:00
Dominik Demuth
dbb35cdba4 added f-omega option to bds fit functions; closes #133 2023-11-09 17:26:57 +01:00
Dominik Demuth
070509c691 do not overwrite old backup until new backup is done 2023-11-09 17:05:17 +01:00
Dominik Demuth
5ea1ed6d0a change level of appimage version logger 2023-11-07 17:50:32 +01:00
Dominik Demuth
a1691f11a6 remove leading space in ascii preview, because some dimwit wants to use spaces in the beginning of his files... 2023-11-07 17:07:47 +01:00
Dominik Demuth
f956c111c2 regex for numeric value for data from text files; closes #127 2023-11-05 17:23:51 +01:00
Dominik Demuth
cb2bc78a2a work on edit signals 2023-11-05 15:25:31 +01:00
Dominik Demuth
9e9751cd9d add log to edit signal 2023-11-05 15:21:19 +01:00
Dominik Demuth
5a8ef8c9c7 change wording for delete previous fit in fitresult 2023-11-05 15:17:59 +01:00
45f5eb9bef Update src/gui_qt/data/container.py
phase correction should be done when users wants it; closes #129
2023-11-02 13:05:18 +00:00
Dominik Demuth
5116622e42 fixed fit parameter ignore boundary problems; fixes #128 2023-11-01 18:33:52 +01:00
Dominik Demuth
661ab81d7e fix edit dialog for spectra 2023-10-31 19:12:02 +01:00
Dominik Demuth
185dd160f1 fix import of agr with missing legends 2023-10-30 19:34:18 +01:00
Dominik Demuth
7fe564a61e more interactive edit 2023-10-25 20:04:49 +02:00
Dominik Demuth
6339bdc2cc escape backslashes in fit function equation 2023-10-17 18:16:31 +02:00
Dominik Demuth
37a6d789bb increase precision in log-gaussian integration 2023-10-02 12:31:40 +02:00
Dominik Demuth
cde794cb0d parabola on a log-scale added 2023-09-28 17:55:25 +02:00
Dominik Demuth
2a69147ed4 remove slot reference in parameter.get_state; fixes #125 2023-09-28 15:43:55 +02:00
Dominik Demuth
902a35b71a check if Default.agr knows what a graph is; helps with #124 2023-09-27 18:06:53 +02:00
Dominik Demuth
6f76349932 Merge branch 'fit_bounds' 2023-09-27 15:39:23 +02:00
Dominik Demuth
f9bb466cb3 fix data look-up for multiple fit models 2023-09-27 15:38:55 +02:00
Dominik Demuth
a97c31325f 1/p bounds in ui 2023-09-27 15:32:19 +02:00
Dominik Demuth
207ee5bffd 1/p bounds added 2023-09-27 13:55:37 +02:00
Dominik Demuth
0046d04683 improve performance building data tree 2023-09-19 17:22:59 +02:00
Dominik Demuth
9463ed1e6c improve performance for show/hide graphs 2023-09-19 17:05:06 +02:00
Dominik Demuth
ea62a05bd3 improve performance for show/hide graphs 2023-09-19 16:32:54 +02:00
Dominik Demuth
c5706084bf fixing merge problems 2023-09-19 12:48:12 +02:00
Dominik Demuth
04037d6b4d Merge branch 'fit_constraints'
# Conflicts:
#	src/gui_qt/main/management.py
2023-09-19 12:39:32 +02:00
Dominik Demuth
dedb130163 make final fit parameter values 2023-09-19 12:35:24 +02:00
Dominik Demuth
6ecc4a4126 make final fit parameter values 2023-09-19 12:34:44 +02:00
Dominik Demuth
067857eda2 minor fixes 2023-09-19 12:22:33 +02:00
Dominik Demuth
41d90bb15f minor fixes 2023-09-19 11:33:52 +02:00
Dominik Demuth
8d994bb9b4 fixed preview 2023-09-18 17:39:31 +02:00
Dominik Demuth
2cbc7e8d75 more detailed error message in fit preparation 2023-09-18 16:08:42 +02:00
Dominik Demuth
1d22f22901 Parameter in preview 2023-09-18 15:30:06 +02:00
Dominik Demuth
bd1a227e4c use Parameter when collecting fit values 2023-09-18 13:52:10 +02:00
Dominik Demuth
03d172bade use Parameter when collecting fit values 2023-09-18 11:43:28 +02:00
Dominik Demuth
e51a02d277 improvement of t1 calc 2023-09-16 17:42:59 +02:00
Dominik Demuth
3af5cb0301 add todos 2023-09-16 14:16:45 +02:00
Dominik Demuth
22f317da8d remove wonky path variable from graph export to use last directory instead; closes #121 2023-09-13 17:25:53 +02:00
Dominik Demuth
24b56cbd2a calc_relaxation: create copy of x to avoid unintended overwrite 2023-09-13 17:16:57 +02:00
5fd52a1a44 Update src/nmreval/distributions/loggaussian.py
fix typo; closes #119
2023-09-12 07:07:36 +00:00
Dominik Demuth
d8e5de6b7a T1+T2 calculation now always iterates over all args that can be from data 2023-09-11 19:50:58 +02:00
Dominik Demuth
869901596b Merge branch 'main' into fit_constraints
# Conflicts:
#	src/gui_qt/fit/fit_forms.py
#	src/gui_qt/main/management.py
#	src/nmreval/fit/minimizer.py
2023-09-11 18:18:30 +02:00
Dominik Demuth
e4dbaf2b91 work on ui 2023-09-11 18:09:08 +02:00
Dominik Demuth
dee1271fe1 add action to context menu to replace single set fit parameter with general fit parameter 2023-09-08 18:59:14 +02:00
Dominik Demuth
b8bab2af7b fixed LG+CC spectral density calculation for omega=0; closes #118 2023-09-08 18:10:19 +02:00
Dominik Demuth
a406908a69 catch errors in fit preparation 2023-09-07 19:52:53 +02:00
Dominik Demuth
5e55f06723 add completer to general fit linedit 2023-09-07 19:30:10 +02:00
Dominik Demuth
e2e52cebde fixed overwrite of weight option for deltay, resulted in invalid weights; closes #117 2023-09-07 17:48:49 +02:00
Dominik Demuth
53c58b2bbb disable validators 2023-09-06 17:45:57 +02:00
4e865cd0c6 Update src/gui_qt/nmr/t1_from_tau.py
Add Log-Gaussian to T1 calculation; closes #116
2023-09-06 10:20:22 +00:00
Dominik Demuth
311157a01a fix parameter setting after fitting multiple models 2023-09-03 20:17:07 +02:00
Dominik Demuth
f0ee2073ad fixed wrong order for nested fit functions; swapped exchange rates in Peschier model 2023-08-31 19:34:13 +02:00
Dominik Demuth
d2e63a5ee3 refactor odr 2023-08-29 19:44:09 +02:00
Dominik Demuth
5a153585ee write error for global fits 2023-08-29 19:11:56 +02:00
Dominik Demuth
d17d0f251e work on linked models 2023-08-26 20:08:13 +02:00
Dominik Demuth
0b8f4932b2 seems mostly to be working 2023-08-25 18:46:36 +02:00
Dominik Demuth
5d43ccb05d fit with global parameter 2023-08-24 16:19:09 +02:00
Dominik Demuth
88a32ea7fd multiple single fits working 2023-08-15 18:44:08 +02:00
Dominik Demuth
7febe55929 order of fits correspond order in graph, fit result window has correct order, see #109 2023-08-07 18:42:10 +02:00
Dominik Demuth
783fe505ba better check for valid class and argument names in fit function creation 2023-08-06 16:41:22 +02:00
Dominik Demuth
2fed4bb0bf fit creation dialog uses option name for kwargs 2023-08-05 17:53:24 +02:00
Dominik Demuth
ec8fafcc9c correct type for linenumbers in codeeditor 2023-08-05 15:31:17 +02:00
Dominik Demuth
7762758958 save original expression 2023-08-04 17:58:48 +02:00
Dominik Demuth
258922772e change debian source 2023-08-02 19:58:02 +02:00
Dominik Demuth
e612c607e2 Merge remote-tracking branch 'origin/master' 2023-08-01 19:59:16 +02:00
Dominik Demuth
b27d9b55ff subclass every PlotWidget because logtickvalues 2023-08-01 19:47:55 +02:00
Dominik Demuth
3475650899 signal b/w change in graph was not connected 2023-08-01 18:06:33 +02:00
Dominik Demuth
0eca306ebe assert that log scale is finite 2023-08-01 18:00:23 +02:00
d7eaff5f0b functioning apply button for tau calculation, part of #109 2023-08-01 06:54:35 +00:00
Dominik Demuth
a5216b1eed constrain legend rect to be fully inside viewbox; simplest solution to fix #113 2023-07-31 20:17:31 +02:00
cf1565f7d3 use symbol for length 1 data in shift/scale; fixes #112 2023-07-31 11:48:57 +00:00
Dominik Demuth
c601e77cec fit with expression works with single fit 2023-07-30 19:34:59 +02:00
Dominik Demuth
63f4f82228 remove spurious print 2023-07-30 18:10:14 +02:00
Dominik Demuth
bae7ee2db6 list of active sets uses actual order instead order they were displayed; closes #111 2023-07-30 16:18:18 +02:00
Dominik Demuth
dde7b7006d overwrite logTickValues of AaxisItem to avoid to many minor ticks; maybe closes #110 2023-07-29 21:25:22 +02:00
Dominik Demuth
141e9f810a convert removes agr controls 2023-07-29 21:22:12 +02:00
Dominik Demuth
b6136bc8ce subclass QTableWidget for change multiple checkstates 2023-07-29 21:22:12 +02:00
Dominik Demuth
7762e299e4 manually setting graph limits when log-scaling to reduce memory usages; 2023-07-29 21:22:10 +02:00
Dominik Demuth
ca130eaa14 eval parameter 2023-07-27 18:58:22 +02:00
Dominik Demuth
2b2c6e932d set lower bound of FC and BDS KWW to 0.1 2023-07-25 17:31:42 +02:00
Dominik Demuth
402994e09b remove parameter limit; closes #106 2023-07-25 17:23:25 +02:00
Dominik Demuth
7a50028202 add namespace to parameter 2023-07-24 20:12:55 +02:00
Dominik Demuth
becc1a15a9 move shift/scale to container; closes #102 2023-07-24 17:38:55 +02:00
13e6112c99 overwrite error in interpolation; avoid mismatch in length
part of #103
2023-07-24 11:08:42 +00:00
Dominik Demuth
1ff462c4b1 remove duplicate call of t1 calculate and t1 interpolation 2023-07-21 19:53:08 +02:00
0cf5d7eef9 remove log(y) weight option from fit
close #101
2023-07-20 07:27:33 +00:00
Dominik Demuth
609f135855 correcter preview for text files with mixed delimiters 2023-07-19 19:09:42 +02:00
Dominik Demuth
5db6a9f2c5 add HN model with independent slopes 2023-07-19 18:36:13 +02:00
Dominik Demuth
3149a3f958 setRegion of RegionItem with option to account for log mode 2023-07-17 19:19:38 +02:00
Dominik Demuth
3321d85203 set integration limits via keyboard; closes #50 2023-07-13 19:51:19 +02:00
Dominik Demuth
76cd4acfb0 add flag for partial fits; closes #83 2023-07-12 20:48:28 +02:00
Dominik Demuth
33afc2ca94 active graph is unchanged after pick position is applied; closes #89 2023-07-12 19:05:17 +02:00
Dominik Demuth
ac039c1b6d higher zValues for borders of RegionItem to move overlapping RegionItems 2023-07-12 18:31:29 +02:00
Dominik Demuth
ef21d7c7f2 use time for interpolation pf empty dsc reference to avoid infs 2023-07-11 20:31:21 +02:00
Dominik Demuth
3ed7368bb0 fix even savgol window length manuallz 2023-07-11 18:06:17 +02:00
Dominik Demuth
fbf4246c7e bugfix eval expression; closes #83 2023-07-09 19:45:26 +02:00
Dominik Demuth
91afe8224f numerical values of data as tooltip; closes #90 2023-07-08 19:55:20 +02:00
Dominik Demuth
ead9751541 tg dialog clean-up 2023-07-06 20:05:10 +02:00
Dominik Demuth
1d9b641f0a closing Tg dialog is no longer export 2023-07-05 20:06:56 +02:00
Dominik Demuth
46ca50845b Merge branch 'dsc'
# Conflicts:
#	src/gui_qt/_py/basewindow.py
#	src/gui_qt/_py/tnmh_dialog.py
#	src/gui_qt/dsc/glass_dialog.py
#	src/gui_qt/main/mainwindow.py
#	src/gui_qt/main/management.py
#	src/gui_qt/math/binning.py
#	src/nmreval/data/dsc.py
#	src/nmreval/dsc/hodge.py
#	src/resources/_ui/basewindow.ui
#	src/resources/_ui/tnmh_dialog.ui
2023-07-05 20:02:52 +02:00
Dominik Demuth
a310a78fb9 added tnmh; some bugfixes 2023-07-05 19:51:06 +02:00
Dominik Demuth
9756bf958b close #86; 2023-07-05 18:11:58 +02:00
Dominik Demuth
8de4a0cbd3 interim save 2023-07-05 17:35:31 +02:00
Dominik Demuth
bc946e1027 interim save 2023-07-03 18:21:55 +02:00
Dominik Demuth
1d9bf600ba add hodge fit 2023-06-27 18:23:41 +02:00
Dominik Demuth
4a04502012 correct URL for issues 2023-06-27 17:26:44 +02:00
Dominik Demuth
6bcf42d6a8 check even window length in tg calc; part of #91 2023-06-26 20:34:52 +02:00
Dominik Demuth
8a96e0472d stuff 2023-06-26 19:57:23 +02:00
567fcfe37e „src/nmreval/models/stimecho.py“ ändern 2023-06-26 17:22:52 +00:00
Dominik Demuth
058551cc72 closes #86 2023-06-22 17:42:07 +02:00
Dominik Demuth
6fd3053964 Merge remote-tracking branch 'origin/master' 2023-06-20 19:22:19 +02:00
Dominik Demuth
3ee95a1990 correct description in binning dialog 2023-06-20 19:22:00 +02:00
Dominik Demuth
1a2f178f58 undo move of .desktop file 2023-06-20 19:21:37 +02:00
Dominik Demuth
c39ffb8a51 undo move of .desktop file 2023-06-20 19:19:05 +02:00
a26595695c Binning and Tg (#85)
add binning; determine Tg of DSC;

closes #60; part of #61

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#85
2023-06-20 17:13:13 +00:00
Dominik Demuth
c9ea32629d Tg export finalized 2023-06-20 19:09:31 +02:00
Dominik Demuth
032ffb72b1 Merge branch 'main' into dsc
# Conflicts:
#	src/gui_qt/main/mainwindow.py
2023-06-20 18:07:52 +02:00
Dominik Demuth
354d5cbc99 generalize binning 2023-06-19 20:03:21 +02:00
Dominik Demuth
22f8bc80ed concat mask also; closes #84 2023-06-19 18:15:25 +02:00
Dominik Demuth
84d588cf80 work on export 2023-06-19 17:50:36 +02:00
Dominik Demuth
5b146433f3 binning 2023-06-16 20:25:24 +02:00
Dominik Demuth
1a225b2cd2 multiselection and check in graph and bdsdialog; part of #82 2023-06-15 20:16:42 +02:00
Dominik Demuth
89c640e591 bugfix: remove value display item from graph before deletion 2023-06-15 20:00:52 +02:00
Dominik Demuth
1703b8d514 add binning 2023-06-15 19:57:13 +02:00
Dominik Demuth
7732544f69 start tg saving 2023-06-15 17:24:35 +02:00
Dominik Demuth
988d2ccbda minor cleanup 2023-06-13 20:28:37 +02:00
Dominik Demuth
4268bdc5ae #81 make t1 data real and catch more errors; 2023-06-13 19:08:24 +02:00
Dominik Demuth
775b5e7e8a correct tab behavior after value change (part of #79) 2023-06-13 18:31:39 +02:00
Dominik Demuth
30e148de14 Merge branch '79' 2023-06-12 20:40:28 +02:00
Dominik Demuth
2499aac7a1 check for value changes while tabbing in edit mode, avoid signal; closes #79 2023-06-12 20:40:00 +02:00
Dominik Demuth
f934104587 closes #80; evaluate expression an Datensätzen mit gehiddenen Values 2023-06-12 18:07:51 +02:00
Dominik Demuth
6e976e1404 closes #78 interpolieren, auswahl von sets 2023-06-12 17:27:43 +02:00
Dominik Demuth
92c29bec2a split tg calculation from tnmh model 2023-06-12 17:20:58 +02:00
Dominik Demuth
9f6d4e0d0c split ficitive cp and tnmh 2023-06-05 19:57:42 +02:00
Dominik Demuth
90bd33a680 check also for individual files in config directory; closes #76 2023-06-04 19:39:12 +02:00
Dominik Demuth
26fea8f69f remove temporary elements from graphs before deletion 2023-06-04 15:57:17 +02:00
Dominik Demuth
25f7ff5616 ui adjustments 2023-06-03 20:27:56 +02:00
Dominik Demuth
bec789318d initialize plot at start 2023-06-03 17:04:14 +02:00
Dominik Demuth
edc6af2762 fictive cp and tnmh model 2023-06-01 20:09:11 +02:00
Dominik Demuth
eb1e657dab more ui work 2023-05-31 19:35:03 +02:00
Dominik Demuth
79a424c4bd fix mismatched indexes between data and labels in asciireader 2023-05-31 17:21:56 +02:00
Dominik Demuth
f74dd982ba color to dsc curves in tg 2023-05-31 17:20:51 +02:00
Dominik Demuth
a5a9561b54 Merge remote-tracking branch 'origin/master' 2023-05-30 17:42:38 +02:00
Dominik Demuth
c8287c2dbf fix missing return of comments in asciireader 2023-05-30 17:42:12 +02:00
Dominik Demuth
a8a7e75501 plot DSC data and baselines 2023-05-30 17:36:44 +02:00
Dominik Demuth
30750cc4ed more ui 2023-05-26 18:22:13 +02:00
Dominik Demuth
af14793286 more ui 2023-05-26 17:28:06 +02:00
Dominik Demuth
2906c17472 more ui 2023-05-24 19:34:06 +02:00
5590b5cd16 SelectionWidget has no function show_as_local_parameter; fixes #75 2023-05-24 09:00:31 +00:00
Dominik Demuth
09e415babf begin work on gui 2023-05-23 19:23:13 +02:00
4e2938b2a2 proper handling of sets without label,; closes #73 2023-05-23 06:40:08 +00:00
Dominik Demuth
685b9d2316 HN + Excess wing 2023-05-22 18:50:03 +02:00
Dominik Demuth
b91fbf7621 tg evaluation in dsc 2023-05-22 18:49:49 +02:00
Dominik Demuth
8d55430246 binning added to points 2023-05-22 18:49:27 +02:00
Dominik Demuth
8d06240205 Merge branch 'fit' 2023-05-19 18:14:29 +02:00
Dominik Demuth
65034d4518 sub function show numbering; closes #70 2023-05-19 18:13:57 +02:00
Dominik Demuth
2adf15104f attempt to only show real/imag part of complex fit functions; closes #72 2023-05-19 17:35:32 +02:00
Dominik Demuth
cd9c85c12b add FID as fallback option for dtype in nmrreader 2023-05-19 16:56:01 +02:00
Dominik Demuth
84d136dd4c .nmr files recognize all data types; closes #71 2023-05-19 16:53:54 +02:00
Dominik Demuth
f60e125487 display parameter name with number ind window 2023-05-19 16:41:24 +02:00
Dominik Demuth
1e9a390ae2 attempt to distinguish general and set-specifi parameter 2023-05-19 16:17:49 +02:00
Dominik Demuth
8673e5acdb catch error in f-test 2023-05-19 16:00:04 +02:00
Dominik Demuth
66a0e40a23 display correct start parameter 2023-05-19 15:48:32 +02:00
Dominik Demuth
a1ab6335c5 remove remnants of fitresult redesign; closes #68 2023-05-19 13:25:56 +02:00
Dominik Demuth
09a2b61160 read text files as BDS and DSC 2023-05-19 13:02:58 +02:00
Dominik Demuth
681b49a22f Merge branch 'read_fc' 2023-05-19 11:37:42 +02:00
Dominik Demuth
3dcd44c3ee restore optionality to figure saving 2023-05-19 11:37:24 +02:00
Dominik Demuth
6e9dd4d45f use gnuplot as lightweight plotter 2023-05-19 11:33:02 +02:00
Dominik Demuth
888d86d9fe remove typo in AppImageBuilder.yml 2023-05-18 20:50:39 +02:00
Dominik Demuth
67d60949b5 plot fit and residual together with bigger fit 2023-05-18 20:43:45 +02:00
Dominik Demuth
fcaf43b3eb fixed problem with setting color from context menu 2023-05-18 18:51:02 +02:00
Dominik Demuth
49101565a3 fixed problem with setting color from context menu 2023-05-17 19:41:44 +02:00
255d7c7862 Update 'src/nmreval/io/dsc.py'
remove matplotlib import
2023-05-17 15:39:36 +00:00
Dominik Demuth
12dacb73b3 add QMessageBox to display existing backup files 2023-05-16 20:05:55 +02:00
Dominik Demuth
b127cc15e2 dsc: only use low temperature to baseline correct calibration without enthalpy 2023-05-16 19:22:50 +02:00
Dominik Demuth
bc215ce32b some stuff done 2023-05-16 17:45:39 +02:00
Dominik Demuth
996b0b2ae2 tnm function with numpy arrays 2023-05-14 20:05:10 +02:00
Dominik Demuth
753cd06dd1 pick position remembers selected destination graph; closes #62 2023-05-14 18:16:23 +02:00
Dominik Demuth
45d319834b fitresult window is reused and remembers fitplot range/log; closes #65
add color to sub-functions in fit result window
Fitresult window shows one set at a time, more space for plot; closes #66
2023-05-14 15:24:25 +02:00
Dominik Demuth
11ed3c16eb add tnmh stuff 2023-05-13 19:23:15 +02:00
Dominik Demuth
edf858da29 hacky solution to have correct tuda colors in agr file; closes #63 2023-05-11 20:12:32 +02:00
Dominik Demuth
e10b85b904 more correct lines preview in asciireader 2023-05-11 18:28:03 +02:00
Dominik Demuth
267554b252 check for empty line moved to avoid error 2023-05-11 18:00:29 +02:00
Dominik Demuth
233cdd9f80 add gnuplot to requirements for plots of FC reading 2023-05-10 17:09:11 +02:00
Dominik Demuth
15c959fd71 save fit parameter adds name to file as extra comment; possible solution for #59 2023-05-08 19:54:55 +02:00
Dominik Demuth
1378cf6ac7 Fitresult accepts label argument as set name 2023-05-08 18:48:08 +02:00
Dominik Demuth
a964e02612 correct baseline and slopes for calibration measurements at high DSC rates 2023-05-08 18:20:04 +02:00
Dominik Demuth
a72e4ba833 add logging to fit exception to retain more info; all references to current graph are either None or id 2023-05-03 19:06:35 +02:00
Dominik Demuth
75ec462efd add logging to fit exception to retain more info and find problem with #56; all references to current graph are either None or id, should close #57 2023-05-03 19:04:25 +02:00
Dominik Demuth
adcd98fc31 dsc baseline uses consistent offset for correction; closes #53 2023-05-02 17:35:28 +02:00
d8cc99cea4 dsc (#55)
closes #53

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#55
2023-04-30 18:21:16 +00:00
Dominik Demuth
7671a56b6f merge cfunc -> main 2023-04-30 14:59:58 +02:00
Dominik Demuth
8086dd5276 finalized c functions 2023-04-29 20:40:32 +02:00
Dominik Demuth
9479364a64 delete item after the iteration to find its index; closes #52 2023-04-27 19:08:14 +02:00
def2a99ed8 datawidget.py: raise AttributeError manually if item is graph, was not always raised; maybe problem of #52 2023-04-27 12:15:43 +02:00
Dominik Demuth
ac4a4d3b8e DSC: remove non-finite values due mismatch in beginnings of sample and empty 2023-04-24 19:24:23 +02:00
Dominik Demuth
dd1c26e285 more work on loggaussian 2023-04-23 19:55:53 +02:00
Dominik Demuth
eba7869c4d data list in QEvalDialog uses current names instead of stored 2023-04-19 19:43:04 +02:00
Dominik Demuth
efd5b34b13 create .local/bin folder for AppImage if not existent 2023-04-19 18:33:11 +02:00
Dominik Demuth
2d472bd44e lower epsabs for integration of energy barrier spec dens 2023-04-19 18:24:29 +02:00
Dominik Demuth
2042148d0f C function for energy distribution spectral density 2023-04-17 20:17:54 +02:00
Dominik Demuth
4a9d50e8a4 refactor Log-Gaussian susceptibility 2023-04-17 17:43:18 +02:00
Dominik Demuth
56b588293d add loggausian to c func and move lookup of c library 2023-04-17 17:31:34 +02:00
59625c1581 more_bugs (#51)
closes #46, #47, #7

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#51
2023-04-16 15:40:58 +00:00
Dominik Demuth
bb3d5ac58b remoce matplotlib import 2023-04-16 14:33:05 +02:00
Dominik Demuth
48861c3f80 remove spurious print 2023-04-16 14:20:40 +02:00
Dominik Demuth
8e2ebfc765 Merge branch 'more_bugs' 2023-04-16 14:17:36 +02:00
Dominik Demuth
924b58beda move iteration of deleting sets inside undo, fixes #36 2023-04-15 15:49:08 +02:00
Dominik Demuth
bd8a4f16ea UpperManagement.deleteDate emits list of sets, indexes in datawidget are now only updated once 2023-04-15 15:40:49 +02:00
Dominik Demuth
e41c42d573 move iteration of deleting sets inside undo 2023-04-15 14:55:14 +02:00
Dominik Demuth
c94231f9d9 add block to graphwindow to stop rescale/legend uupdates 2023-04-15 14:54:25 +02:00
Dominik Demuth
99bb196e5c basic FC reading is working for different script version 2023-04-14 19:17:05 +02:00
Dominik Demuth
e5563d55d5 set graphs in fc reader 2023-04-14 17:58:14 +02:00
Dominik Demuth
abe6ca42c7 added build date of appimage as version; closes #48 2023-04-13 20:03:39 +02:00
Dominik Demuth
40961a89df correct path in makefile 2023-04-13 19:38:58 +02:00
Dominik Demuth
29518b9ea0 newer FC scripts are working, now older ones fail 2023-04-12 20:42:33 +02:00
Dominik Demuth
3793f67951 rate and time in exponential function was switched 2023-04-12 20:01:27 +02:00
Dominik Demuth
0251dea563 uncheck fits in data_table 2023-04-12 17:28:12 +02:00
Dominik Demuth
bc034cf4f4 set region flag correctly 2023-04-12 17:27:05 +02:00
Dominik Demuth
3e513a1231 update makefile 2023-04-11 19:06:35 +02:00
Dominik Demuth
ead30d127a add C function for FFHS integration 2023-04-10 19:33:23 +02:00
Dominik Demuth
3b79c571fb eval expression reuses ListWidgetItems of datasets; closes #31 2023-04-09 20:20:37 +02:00
Dominik Demuth
ee8ea4f2c5 remove epsilons from integration, now 50% faster 2023-04-09 19:37:22 +02:00
Dominik Demuth
2c0cdfbd68 add constant to ffhs 2023-04-09 19:37:18 +02:00
Dominik Demuth
767fa5f6fb timer needs to be child of window 2023-04-09 19:37:01 +02:00
02f8a3bb31 fitting (#43)
adjustments to fit to see if fit is at least running, could help with #39

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#43
2023-04-08 18:37:07 +00:00
Dominik Demuth
ffecc9c873 remove duplicates of keys in deletion; may help with #36 2023-04-08 17:16:05 +02:00
0ec0021727 appimage-starter (#42)
create program launcher;
reduced size of appimage;
download of appimages working(?)

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#42
2023-04-08 13:28:13 +00:00
6b71de8265 bug-fixer (#40)
fixes #32, #34, #39

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#40
2023-04-07 13:45:28 +00:00
43285b4bd5 added first version of a backup system (#37)
Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: IPKM/nmreval#37
2023-04-05 17:50:06 +00:00
2fbaa94109 catch strange behavior of strptime in get_zsync 2023-04-04 16:43:14 +00:00
ebf3e9635d Update 'src/gui_qt/lib/utils.py' 2023-04-04 15:49:09 +00:00
79befb50f1 Update 'src/gui_qt/lib/utils.py' 2023-04-04 15:41:21 +00:00
0adb530dc2 Update 'src/gui_qt/lib/utils.py' 2023-04-04 15:34:09 +00:00
Dominik Demuth
d7fd8395e5 update zsync location 2023-04-02 19:44:01 +02:00
Dominik Demuth
75f9bc84f8 remove typo; remove unnecessary package 2023-04-02 19:23:04 +02:00
1c98676bee Merge pull request 'show sub-functions in fitresult; closes #26' (#28) from fitting into master
Reviewed-on: IPKM/nmreval#28
2023-04-02 15:42:05 +00:00
Dominik Demuth
8a35a5e6cb show sub-functions in fitresult; closes #26 2023-04-02 17:40:23 +02:00
df9b302f9d Merge pull request 'replace semicolon and commas with spaces in csv; closes #23' (#24) from csv_broken into master
Reviewed-on: IPKM/nmreval#24
2023-03-24 10:05:43 +00:00
180 changed files with 12604 additions and 23469 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 }}

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ AppDir
NMReval*.zsync NMReval*.zsync
.idea .idea
*.zs-old *.zs-old
docs

View File

@ -12,7 +12,9 @@ script:
# Copy the python application code into the AppDir # Copy the python application code into the AppDir
- cp bin/evaluate.py $TARGET_APPDIR/usr/bin/ - cp bin/evaluate.py $TARGET_APPDIR/usr/bin/
- cp -r src/* $TARGET_APPDIR/usr/src/ - cp -r src/* $TARGET_APPDIR/usr/src/
- cp src/pkm.vogel.nmreval.desktop $TARGET_APPDIR/usr/share/applications - cp src/resources/pkm.vogel.nmreval.desktop $TARGET_APPDIR/usr/share/applications
# set current date as version info
- sed -i "s/CURRENT_DATE/$(date +'%Y-%m-%d')/" $TARGET_APPDIR/usr/src/nmreval/version.py
AppDir: AppDir:
@ -30,35 +32,50 @@ AppDir:
arch: amd64 arch: amd64
allow_unauthenticated: true allow_unauthenticated: true
sources: sources:
- sourceline: 'deb [arch=amd64] http://mirror.infra.pkm/ bullseye main contrib non-free' - sourceline: 'deb [arch=amd64] http://ftp.uni-mainz.de/debian bullseye main contrib non-free'
include: include:
# for /usr/bin/env # for /usr/bin/env
- coreutils # - coreutils
- dash # - dash
- zsync # - zsync
- hicolor-icon-theme # - hicolor-icon-theme
- libatlas3-base - libatlas3-base
- python3.9-minimal - gnuplot-nox
- python3-minimal
- python3-numpy - python3-numpy
- python3-scipy - python3-scipy
# - python3-matplotlib
# - python-matplotlib-data
- python3-bsddb3 - python3-bsddb3
- python3-h5py - python3-h5py
- python3-pyqt5 - python3-pyqt5
- python3-pyqtgraph - python3-pyqtgraph
- python3-requests
- python3-urllib3
# - python3-tk
exclude: exclude:
# lots of qt stuff we do not use
- libqt5designer5
- libqt5help5
- libqt5network5
- libqt5sql5
- libqt5test5
- libqt5xml5
- qtbase5-dev-tools
- pyqt5-dev-tools
- libavahi-client3 - libavahi-client3
- libavahi-common-data - libavahi-common-data
- libavahi-common3 - libavahi-common3
after_bundle: | - libwacom2
echo "MONSTER SED FOLLOWING...(uncomment if needed for mpl-data)" - libwacom-common
#sed -i s,\'/usr/share/matplotlib/mpl-data\',"f\"\{os.environ.get\('APPDIR'\,'/'\)\}/usr/share/matplotlib/mpl-data\"", ${TARGET_APPDIR}/usr/lib/python3/dist-packages/matplotlib/__init__.py
files:
exclude:
- usr/share/man
- usr/share/doc/*/README.*
- usr/share/doc/*/changelog.*
- usr/share/doc/*/NEWS.*
- usr/share/doc/*/TODO.*
runtime: runtime:
# if needed, apparently replaces hardcoded location with APPDIR location
# path_mappings:
# - /usr/share/matplotlib/mpl-data:$APPDIR/usr/share/matplotlib/mpl-data
version: "continuous" version: "continuous"
env: env:
PATH: '${APPDIR}/usr/bin:${PATH}' PATH: '${APPDIR}/usr/bin:${PATH}'

View File

@ -5,30 +5,36 @@ PYUIC = pyuic5
PYRCC = pyrcc5 PYRCC = pyrcc5
#Directory with ui files #Directory with ui files
RESOURCE_DIR = resources/_ui RESOURCE_DIR = src/resources/_ui
#Directory for compiled resources #Directory for compiled resources
COMPILED_DIR = nmreval/gui_qt/_py PYQT_DIR = src/gui_qt/_py
#UI files to compile, uses every *.ui found in RESOURCE_DIR #UI files to compile, uses every *.ui found in RESOURCE_DIR
UI_FILES = $(foreach dir, $(RESOURCE_DIR), $(notdir $(wildcard $(dir)/*.ui))) UI_FILES = $(foreach dir, $(RESOURCE_DIR), $(notdir $(wildcard $(dir)/*.ui)))
COMPILED_UI = $(UI_FILES:%.ui=$(COMPILED_DIR)/%.py) PYQT_UI = $(UI_FILES:%.ui=$(PYQT_DIR)/%.py)
SVG_FILES = $(foreach dir, $(RCC_DIR), $(notdir $(wildcard $(dir)/*.svg))) SVG_FILES = $(foreach dir, $(RCC_DIR), $(notdir $(wildcard $(dir)/*.svg)))
PNG_FILES = $(SVG_FILES:%.svg=$(RCC_DIR)/%.png) PNG_FILES = $(SVG_FILES:%.svg=$(RCC_DIR)/%.png)
all : ui CC = gcc
CFLAGS = -O2 -fPIC
LDFLAGS = -shared
ui : $(COMPILED_UI) C_DIR = src/nmreval/clib
rcc: $(PNG_FILES) all : ui compile
ui : $(PYQT_UI)
rcc : $(PNG_FILES)
# only one C file at the moment
compile : $(C_DIR)/integrate.c
$(CC) $(LDFLAGS) $(CFLAGS) -o $(C_DIR)/integrate.so $<
$(COMPILED_DIR)/%.py : $(RESOURCE_DIR)/%.ui $(COMPILED_DIR)/%.py : $(RESOURCE_DIR)/%.ui
$(PYUIC) $< -o $@ $(PYUIC) $< -o $@
# replace import of ressource to correct path
# @sed -i s/images_rc/nmrevalqt.$(COMPILED_DIR).images_rc/g $@
# @sed -i /images_rc/d $@
$(RCC_DIR)/%.png : $(RCC_DIR)/%.svg $(RCC_DIR)/%.png : $(RCC_DIR)/%.svg
convert -background none $< $@ convert -background none $< $@

View File

@ -2,6 +2,7 @@
import sys import sys
import pathlib import pathlib
import os
sys.path.append(str(pathlib.Path(__file__).absolute().parent.parent / 'src')) sys.path.append(str(pathlib.Path(__file__).absolute().parent.parent / 'src'))
from nmreval.configs import check_for_config from nmreval.configs import check_for_config
@ -16,12 +17,45 @@ from nmreval.lib.logger import handle_exception
sys.excepthook = handle_exception sys.excepthook = handle_exception
from gui_qt import App from gui_qt import App
from gui_qt.Qt import QtCore
app = App(['Team Rocket FTW!']) app = App(['NMReval'])
from gui_qt.main.mainwindow import NMRMainWindow 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 = NMRMainWindow()
mplQt.show() mplQt.show()
sys.exit(app.exec()) sys.exit(app.exec())

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '_ui/apod_dialog.ui' # Form implementation generated from reading ui file 'src/resources/_ui/apod_dialog.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.9
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
@ -13,62 +14,223 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_ApodEdit(object): class Ui_ApodEdit(object):
def setupUi(self, ApodEdit): def setupUi(self, ApodEdit):
ApodEdit.setObjectName("ApodEdit") ApodEdit.setObjectName("ApodEdit")
ApodEdit.resize(784, 484) ApodEdit.resize(1144, 655)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(ApodEdit.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(ApodEdit.sizePolicy().hasHeightForWidth())
ApodEdit.setSizePolicy(sizePolicy) ApodEdit.setSizePolicy(sizePolicy)
self.gridLayout = QtWidgets.QGridLayout(ApodEdit) self.gridLayout = QtWidgets.QGridLayout(ApodEdit)
self.gridLayout.setContentsMargins(3, 3, 3, 3) self.gridLayout.setContentsMargins(9, 9, 9, 9)
self.gridLayout.setSpacing(3) self.gridLayout.setSpacing(3)
self.gridLayout.setObjectName("gridLayout") self.gridLayout.setObjectName("gridLayout")
self.graphicsView = PlotWidget(ApodEdit) self.freq_graph = NMRPlotWidget(ApodEdit)
self.freq_graph.setObjectName("freq_graph")
self.gridLayout.addWidget(self.freq_graph, 2, 2, 1, 1)
self.time_graph = NMRPlotWidget(ApodEdit)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.graphicsView.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.time_graph.sizePolicy().hasHeightForWidth())
self.graphicsView.setSizePolicy(sizePolicy) self.time_graph.setSizePolicy(sizePolicy)
self.graphicsView.setObjectName("graphicsView") self.time_graph.setObjectName("time_graph")
self.gridLayout.addWidget(self.graphicsView, 2, 0, 1, 1) self.gridLayout.addWidget(self.time_graph, 2, 1, 1, 1)
self.graphicsView_2 = PlotWidget(ApodEdit) self.widget = QtWidgets.QWidget(ApodEdit)
self.graphicsView_2.setObjectName("graphicsView_2") sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
self.gridLayout.addWidget(self.graphicsView_2, 2, 1, 1, 1)
self.apodcombobox = QtWidgets.QComboBox(ApodEdit)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.apodcombobox.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth())
self.apodcombobox.setSizePolicy(sizePolicy) self.widget.setSizePolicy(sizePolicy)
self.widget.setObjectName("widget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.widget)
self.verticalLayout.setObjectName("verticalLayout")
self.baseline_box = QtWidgets.QCheckBox(self.widget)
self.baseline_box.setObjectName("baseline_box")
self.verticalLayout.addWidget(self.baseline_box)
self.shift_box = QtWidgets.QGroupBox(self.widget)
self.shift_box.setFlat(True)
self.shift_box.setCheckable(True)
self.shift_box.setChecked(False)
self.shift_box.setObjectName("shift_box")
self.gridLayout_4 = QtWidgets.QGridLayout(self.shift_box)
self.gridLayout_4.setContentsMargins(3, 3, 3, 3)
self.gridLayout_4.setSpacing(3)
self.gridLayout_4.setObjectName("gridLayout_4")
self.ls_lineedit = QtWidgets.QLineEdit(self.shift_box)
self.ls_lineedit.setObjectName("ls_lineedit")
self.gridLayout_4.addWidget(self.ls_lineedit, 1, 1, 1, 1)
self.ls_spinbox = QtWidgets.QSpinBox(self.shift_box)
self.ls_spinbox.setMaximum(999999)
self.ls_spinbox.setObjectName("ls_spinbox")
self.gridLayout_4.addWidget(self.ls_spinbox, 0, 1, 1, 1)
self.ls_combobox = QtWidgets.QComboBox(self.shift_box)
self.ls_combobox.setObjectName("ls_combobox")
self.ls_combobox.addItem("")
self.ls_combobox.addItem("")
self.gridLayout_4.addWidget(self.ls_combobox, 0, 0, 2, 1)
self.verticalLayout.addWidget(self.shift_box)
self.apod_box = QtWidgets.QGroupBox(self.widget)
self.apod_box.setFlat(True)
self.apod_box.setCheckable(True)
self.apod_box.setChecked(False)
self.apod_box.setObjectName("apod_box")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.apod_box)
self.verticalLayout_2.setContentsMargins(3, 3, 3, 3)
self.verticalLayout_2.setSpacing(3)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.apodcombobox = QtWidgets.QComboBox(self.apod_box)
self.apodcombobox.setObjectName("apodcombobox") self.apodcombobox.setObjectName("apodcombobox")
self.gridLayout.addWidget(self.apodcombobox, 0, 0, 1, 1) self.verticalLayout_2.addWidget(self.apodcombobox)
self.eqn_label = QtWidgets.QLabel(self.apod_box)
self.eqn_label.setIndent(3)
self.eqn_label.setObjectName("eqn_label")
self.verticalLayout_2.addWidget(self.eqn_label)
self.widget_layout = QtWidgets.QHBoxLayout() self.widget_layout = QtWidgets.QHBoxLayout()
self.widget_layout.setContentsMargins(-1, 6, -1, -1) self.widget_layout.setContentsMargins(-1, 6, -1, -1)
self.widget_layout.setSpacing(20) self.widget_layout.setSpacing(20)
self.widget_layout.setObjectName("widget_layout") self.widget_layout.setObjectName("widget_layout")
self.gridLayout.addLayout(self.widget_layout, 1, 0, 1, 2) self.verticalLayout_2.addLayout(self.widget_layout)
self.verticalLayout.addWidget(self.apod_box)
self.zerofill_box = QtWidgets.QGroupBox(self.widget)
self.zerofill_box.setFlat(True)
self.zerofill_box.setCheckable(True)
self.zerofill_box.setChecked(False)
self.zerofill_box.setObjectName("zerofill_box")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.zerofill_box)
self.horizontalLayout.setContentsMargins(3, 3, 3, 3)
self.horizontalLayout.setSpacing(3)
self.horizontalLayout.setObjectName("horizontalLayout")
self.label = QtWidgets.QLabel(self.zerofill_box)
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label)
self.zf_spinbox = QtWidgets.QSpinBox(self.zerofill_box)
self.zf_spinbox.setMinimum(1)
self.zf_spinbox.setMaximum(3)
self.zf_spinbox.setObjectName("zf_spinbox")
self.horizontalLayout.addWidget(self.zf_spinbox)
self.verticalLayout.addWidget(self.zerofill_box)
self.phase_box = QtWidgets.QGroupBox(self.widget)
self.phase_box.setFlat(True)
self.phase_box.setCheckable(True)
self.phase_box.setChecked(False)
self.phase_box.setObjectName("phase_box")
self.gridLayout_2 = QtWidgets.QGridLayout(self.phase_box)
self.gridLayout_2.setContentsMargins(3, 3, 3, 3)
self.gridLayout_2.setSpacing(3)
self.gridLayout_2.setObjectName("gridLayout_2")
self.label_3 = QtWidgets.QLabel(self.phase_box)
self.label_3.setObjectName("label_3")
self.gridLayout_2.addWidget(self.label_3, 1, 0, 1, 1)
self.label_4 = QtWidgets.QLabel(self.phase_box)
self.label_4.setObjectName("label_4")
self.gridLayout_2.addWidget(self.label_4, 2, 0, 1, 1)
self.ph0_spinbox = QtWidgets.QDoubleSpinBox(self.phase_box)
self.ph0_spinbox.setWrapping(True)
self.ph0_spinbox.setDecimals(1)
self.ph0_spinbox.setMinimum(-180.0)
self.ph0_spinbox.setMaximum(180.0)
self.ph0_spinbox.setSingleStep(0.5)
self.ph0_spinbox.setObjectName("ph0_spinbox")
self.gridLayout_2.addWidget(self.ph0_spinbox, 0, 1, 1, 1)
self.pivot_lineedit = QtWidgets.QLineEdit(self.phase_box)
self.pivot_lineedit.setObjectName("pivot_lineedit")
self.gridLayout_2.addWidget(self.pivot_lineedit, 2, 1, 1, 1)
self.label_2 = QtWidgets.QLabel(self.phase_box)
self.label_2.setObjectName("label_2")
self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1)
self.ph1_spinbox = QtWidgets.QDoubleSpinBox(self.phase_box)
self.ph1_spinbox.setWrapping(True)
self.ph1_spinbox.setDecimals(2)
self.ph1_spinbox.setMinimum(-720.0)
self.ph1_spinbox.setMaximum(720.0)
self.ph1_spinbox.setSingleStep(0.05)
self.ph1_spinbox.setObjectName("ph1_spinbox")
self.gridLayout_2.addWidget(self.ph1_spinbox, 1, 1, 1, 1)
self.verticalLayout.addWidget(self.phase_box)
self.ft_box = QtWidgets.QGroupBox(self.widget)
self.ft_box.setCheckable(True)
self.ft_box.setChecked(False)
self.ft_box.setObjectName("ft_box")
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.ft_box)
self.verticalLayout_3.setContentsMargins(3, 3, 3, 3)
self.verticalLayout_3.setSpacing(3)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.phase_before_button = QtWidgets.QRadioButton(self.ft_box)
self.phase_before_button.setChecked(True)
self.phase_before_button.setObjectName("phase_before_button")
self.buttonGroup = QtWidgets.QButtonGroup(ApodEdit)
self.buttonGroup.setObjectName("buttonGroup")
self.buttonGroup.addButton(self.phase_before_button)
self.verticalLayout_3.addWidget(self.phase_before_button)
self.phase_after_button = QtWidgets.QRadioButton(self.ft_box)
self.phase_after_button.setObjectName("phase_after_button")
self.buttonGroup.addButton(self.phase_after_button)
self.verticalLayout_3.addWidget(self.phase_after_button)
self.verticalLayout.addWidget(self.ft_box)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem)
self.gridLayout.addWidget(self.widget, 2, 0, 1, 1)
self.buttonBox = QtWidgets.QDialogButtonBox(ApodEdit) self.buttonBox = QtWidgets.QDialogButtonBox(ApodEdit)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox") self.buttonBox.setObjectName("buttonBox")
self.gridLayout.addWidget(self.buttonBox, 4, 0, 1, 2) self.gridLayout.addWidget(self.buttonBox, 4, 1, 1, 2)
self.eqn_label = QtWidgets.QLabel(ApodEdit) self.log_freq_widget = QtWidgets.QWidget(ApodEdit)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred) self.log_freq_widget.setObjectName("log_freq_widget")
sizePolicy.setHorizontalStretch(0) self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.log_freq_widget)
sizePolicy.setVerticalStretch(0) self.horizontalLayout_3.setObjectName("horizontalLayout_3")
sizePolicy.setHeightForWidth(self.eqn_label.sizePolicy().hasHeightForWidth()) spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.eqn_label.setSizePolicy(sizePolicy) self.horizontalLayout_3.addItem(spacerItem1)
self.eqn_label.setIndent(3) self.logx_freq = QtWidgets.QCheckBox(self.log_freq_widget)
self.eqn_label.setObjectName("eqn_label") self.logx_freq.setObjectName("logx_freq")
self.gridLayout.addWidget(self.eqn_label, 0, 1, 1, 1) self.horizontalLayout_3.addWidget(self.logx_freq)
self.logy_freq = QtWidgets.QCheckBox(self.log_freq_widget)
self.logy_freq.setObjectName("logy_freq")
self.horizontalLayout_3.addWidget(self.logy_freq)
self.gridLayout.addWidget(self.log_freq_widget, 3, 2, 1, 1)
self.logtime_widget = QtWidgets.QWidget(ApodEdit)
self.logtime_widget.setObjectName("logtime_widget")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.logtime_widget)
self.horizontalLayout_2.setContentsMargins(-1, 1, -1, -1)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem2)
self.logx_time = QtWidgets.QCheckBox(self.logtime_widget)
self.logx_time.setObjectName("logx_time")
self.horizontalLayout_2.addWidget(self.logx_time)
self.logy_time = QtWidgets.QCheckBox(self.logtime_widget)
self.logy_time.setObjectName("logy_time")
self.horizontalLayout_2.addWidget(self.logy_time)
self.gridLayout.addWidget(self.logtime_widget, 3, 1, 1, 1)
self.retranslateUi(ApodEdit) self.retranslateUi(ApodEdit)
self.buttonBox.accepted.connect(ApodEdit.accept) self.buttonBox.accepted.connect(ApodEdit.accept) # type: ignore
self.buttonBox.rejected.connect(ApodEdit.close) self.buttonBox.rejected.connect(ApodEdit.close) # type: ignore
QtCore.QMetaObject.connectSlotsByName(ApodEdit) QtCore.QMetaObject.connectSlotsByName(ApodEdit)
def retranslateUi(self, ApodEdit): def retranslateUi(self, ApodEdit):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
ApodEdit.setWindowTitle(_translate("ApodEdit", "Apodization")) ApodEdit.setWindowTitle(_translate("ApodEdit", "Apodization"))
self.baseline_box.setText(_translate("ApodEdit", "Baseline"))
self.shift_box.setTitle(_translate("ApodEdit", "Shift"))
self.ls_lineedit.setText(_translate("ApodEdit", "0"))
self.ls_combobox.setItemText(0, _translate("ApodEdit", "Points"))
self.ls_combobox.setItemText(1, _translate("ApodEdit", "Seconds"))
self.apod_box.setTitle(_translate("ApodEdit", "Apodization"))
self.eqn_label.setText(_translate("ApodEdit", "TextLabel")) self.eqn_label.setText(_translate("ApodEdit", "TextLabel"))
from pyqtgraph import PlotWidget self.zerofill_box.setTitle(_translate("ApodEdit", "Zero fill"))
self.label.setText(_translate("ApodEdit", "Double length"))
self.zf_spinbox.setSuffix(_translate("ApodEdit", "x"))
self.phase_box.setTitle(_translate("ApodEdit", "Phase correction"))
self.label_3.setText(_translate("ApodEdit", "Phase 1"))
self.label_4.setText(_translate("ApodEdit", "Pivot"))
self.pivot_lineedit.setText(_translate("ApodEdit", "0"))
self.label_2.setText(_translate("ApodEdit", "Phase 0"))
self.ft_box.setTitle(_translate("ApodEdit", "Fourier transform"))
self.phase_before_button.setText(_translate("ApodEdit", "before phase correction"))
self.phase_after_button.setText(_translate("ApodEdit", "after phase correction"))
self.logx_freq.setText(_translate("ApodEdit", "Log X"))
self.logy_freq.setText(_translate("ApodEdit", "Log Y"))
self.logx_time.setText(_translate("ApodEdit", "Log X"))
self.logy_time.setText(_translate("ApodEdit", "Log Y"))
from ..lib.graph_items import NMRPlotWidget

View File

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'src/resources/_ui/asciidialog.ui' # Form implementation generated from reading ui file 'src/resources/_ui/asciidialog.ui'
# #
# Created by: PyQt5 UI code generator 5.15.7 # Created by: PyQt5 UI code generator 5.15.9
# #
# WARNING: Any manual changes made to this file will be lost when pyuic5 is # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing. # run again. Do not edit this file unless you know what you are doing.
@ -14,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_ascii_reader(object): class Ui_ascii_reader(object):
def setupUi(self, ascii_reader): def setupUi(self, ascii_reader):
ascii_reader.setObjectName("ascii_reader") ascii_reader.setObjectName("ascii_reader")
ascii_reader.resize(627, 703) ascii_reader.resize(665, 904)
self.verticalLayout = QtWidgets.QVBoxLayout(ascii_reader) self.verticalLayout = QtWidgets.QVBoxLayout(ascii_reader)
self.verticalLayout.setObjectName("verticalLayout") self.verticalLayout.setObjectName("verticalLayout")
self.tabWidget = QtWidgets.QTabWidget(ascii_reader) self.tabWidget = QtWidgets.QTabWidget(ascii_reader)
@ -28,92 +28,25 @@ class Ui_ascii_reader(object):
self.header_widget.setObjectName("header_widget") self.header_widget.setObjectName("header_widget")
self.verticalLayout_3.addWidget(self.header_widget) self.verticalLayout_3.addWidget(self.header_widget)
self.groupBox = QtWidgets.QGroupBox(self.tabWidgetPage1) self.groupBox = QtWidgets.QGroupBox(self.tabWidgetPage1)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.groupBox.sizePolicy().hasHeightForWidth())
self.groupBox.setSizePolicy(sizePolicy)
self.groupBox.setObjectName("groupBox") self.groupBox.setObjectName("groupBox")
self.gridLayout = QtWidgets.QGridLayout(self.groupBox) self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.groupBox)
self.gridLayout.setContentsMargins(3, 3, 3, 3) self.horizontalLayout_4.setContentsMargins(3, 3, 3, 3)
self.gridLayout.setHorizontalSpacing(9) self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.gridLayout.setObjectName("gridLayout") self.verticalLayout_6 = QtWidgets.QVBoxLayout()
self.FID_radioButton = QtWidgets.QRadioButton(self.groupBox) self.verticalLayout_6.setObjectName("verticalLayout_6")
self.FID_radioButton.setAutoExclusive(True)
self.FID_radioButton.setObjectName("FID_radioButton")
self.buttonGroup = QtWidgets.QButtonGroup(ascii_reader)
self.buttonGroup.setObjectName("buttonGroup")
self.buttonGroup.addButton(self.FID_radioButton)
self.gridLayout.addWidget(self.FID_radioButton, 2, 1, 1, 1)
self.x_lineedit = QtWidgets.QLineEdit(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.x_lineedit.sizePolicy().hasHeightForWidth())
self.x_lineedit.setSizePolicy(sizePolicy)
self.x_lineedit.setInputMethodHints(QtCore.Qt.ImhFormattedNumbersOnly|QtCore.Qt.ImhPreferNumbers)
self.x_lineedit.setObjectName("x_lineedit")
self.gridLayout.addWidget(self.x_lineedit, 1, 3, 1, 1)
self.y_label = QtWidgets.QLabel(self.groupBox)
self.y_label.setObjectName("y_label")
self.gridLayout.addWidget(self.y_label, 2, 2, 1, 1)
self.pts_radioButton = QtWidgets.QRadioButton(self.groupBox)
self.pts_radioButton.setChecked(True)
self.pts_radioButton.setAutoExclusive(True)
self.pts_radioButton.setObjectName("pts_radioButton")
self.buttonGroup.addButton(self.pts_radioButton)
self.gridLayout.addWidget(self.pts_radioButton, 1, 1, 1, 1)
self.deltay_lineEdit = QtWidgets.QLineEdit(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.deltay_lineEdit.sizePolicy().hasHeightForWidth())
self.deltay_lineEdit.setSizePolicy(sizePolicy)
self.deltay_lineEdit.setObjectName("deltay_lineEdit")
self.gridLayout.addWidget(self.deltay_lineEdit, 3, 3, 1, 1)
self.label_5 = QtWidgets.QLabel(self.groupBox)
self.label_5.setObjectName("label_5")
self.gridLayout.addWidget(self.label_5, 3, 2, 1, 1)
self.column_checkBox = QtWidgets.QCheckBox(self.groupBox) self.column_checkBox = QtWidgets.QCheckBox(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.column_checkBox.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.column_checkBox.sizePolicy().hasHeightForWidth())
self.column_checkBox.setSizePolicy(sizePolicy) self.column_checkBox.setSizePolicy(sizePolicy)
self.column_checkBox.setObjectName("column_checkBox") self.column_checkBox.setObjectName("column_checkBox")
self.gridLayout.addWidget(self.column_checkBox, 0, 0, 1, 1) self.verticalLayout_6.addWidget(self.column_checkBox)
self.label = QtWidgets.QLabel(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
self.label.setSizePolicy(sizePolicy)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 0, 1, 1, 1)
self.label_7 = QtWidgets.QLabel(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_7.sizePolicy().hasHeightForWidth())
self.label_7.setSizePolicy(sizePolicy)
self.label_7.setObjectName("label_7")
self.gridLayout.addWidget(self.label_7, 0, 2, 1, 2)
self.y_lineedit = QtWidgets.QLineEdit(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.y_lineedit.sizePolicy().hasHeightForWidth())
self.y_lineedit.setSizePolicy(sizePolicy)
self.y_lineedit.setInputMethodHints(QtCore.Qt.ImhFormattedNumbersOnly|QtCore.Qt.ImhPreferNumbers)
self.y_lineedit.setObjectName("y_lineedit")
self.gridLayout.addWidget(self.y_lineedit, 2, 3, 1, 1)
self.spectrum_radioButton = QtWidgets.QRadioButton(self.groupBox)
self.spectrum_radioButton.setObjectName("spectrum_radioButton")
self.buttonGroup.addButton(self.spectrum_radioButton)
self.gridLayout.addWidget(self.spectrum_radioButton, 3, 1, 1, 1)
self.x_label = QtWidgets.QLabel(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.x_label.sizePolicy().hasHeightForWidth())
self.x_label.setSizePolicy(sizePolicy)
self.x_label.setObjectName("x_label")
self.gridLayout.addWidget(self.x_label, 1, 2, 1, 1)
self.line_spinBox = QtWidgets.QSpinBox(self.groupBox) self.line_spinBox = QtWidgets.QSpinBox(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
@ -122,17 +55,158 @@ class Ui_ascii_reader(object):
self.line_spinBox.setSizePolicy(sizePolicy) self.line_spinBox.setSizePolicy(sizePolicy)
self.line_spinBox.setMinimum(1) self.line_spinBox.setMinimum(1)
self.line_spinBox.setObjectName("line_spinBox") self.line_spinBox.setObjectName("line_spinBox")
self.gridLayout.addWidget(self.line_spinBox, 1, 0, 1, 1) self.verticalLayout_6.addWidget(self.line_spinBox)
self.label_6 = QtWidgets.QLabel(self.groupBox)
self.label_6.setObjectName("label_6")
self.verticalLayout_6.addWidget(self.label_6)
self.preview_spinBox = QtWidgets.QSpinBox(self.groupBox) self.preview_spinBox = QtWidgets.QSpinBox(self.groupBox)
self.preview_spinBox.setMinimum(1) self.preview_spinBox.setMinimum(1)
self.preview_spinBox.setProperty("value", 10) self.preview_spinBox.setProperty("value", 10)
self.preview_spinBox.setObjectName("preview_spinBox") self.preview_spinBox.setObjectName("preview_spinBox")
self.gridLayout.addWidget(self.preview_spinBox, 3, 0, 1, 1) self.verticalLayout_6.addWidget(self.preview_spinBox)
self.label_6 = QtWidgets.QLabel(self.groupBox) spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.label_6.setObjectName("label_6") self.verticalLayout_6.addItem(spacerItem)
self.gridLayout.addWidget(self.label_6, 2, 0, 1, 1) self.horizontalLayout_4.addLayout(self.verticalLayout_6)
self.verticalLayout_5 = QtWidgets.QVBoxLayout()
self.verticalLayout_5.setObjectName("verticalLayout_5")
self.label = QtWidgets.QLabel(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
self.label.setSizePolicy(sizePolicy)
self.label.setObjectName("label")
self.verticalLayout_5.addWidget(self.label)
self.pts_radioButton = QtWidgets.QRadioButton(self.groupBox)
self.pts_radioButton.setChecked(True)
self.pts_radioButton.setAutoExclusive(True)
self.pts_radioButton.setObjectName("pts_radioButton")
self.buttonGroup = QtWidgets.QButtonGroup(ascii_reader)
self.buttonGroup.setObjectName("buttonGroup")
self.buttonGroup.addButton(self.pts_radioButton)
self.verticalLayout_5.addWidget(self.pts_radioButton)
self.dsc_radioButton = QtWidgets.QRadioButton(self.groupBox)
self.dsc_radioButton.setObjectName("dsc_radioButton")
self.buttonGroup.addButton(self.dsc_radioButton)
self.verticalLayout_5.addWidget(self.dsc_radioButton)
self.FID_radioButton = QtWidgets.QRadioButton(self.groupBox)
self.FID_radioButton.setAutoExclusive(True)
self.FID_radioButton.setObjectName("FID_radioButton")
self.buttonGroup.addButton(self.FID_radioButton)
self.verticalLayout_5.addWidget(self.FID_radioButton)
self.spectrum_radioButton = QtWidgets.QRadioButton(self.groupBox)
self.spectrum_radioButton.setObjectName("spectrum_radioButton")
self.buttonGroup.addButton(self.spectrum_radioButton)
self.verticalLayout_5.addWidget(self.spectrum_radioButton)
self.bds_radioButton = QtWidgets.QRadioButton(self.groupBox)
self.bds_radioButton.setObjectName("bds_radioButton")
self.buttonGroup.addButton(self.bds_radioButton)
self.verticalLayout_5.addWidget(self.bds_radioButton)
self.horizontalLayout_4.addLayout(self.verticalLayout_5)
self.gridLayout_2 = QtWidgets.QGridLayout()
self.gridLayout_2.setObjectName("gridLayout_2")
self.deltay_lineEdit = QtWidgets.QLineEdit(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.deltay_lineEdit.sizePolicy().hasHeightForWidth())
self.deltay_lineEdit.setSizePolicy(sizePolicy)
self.deltay_lineEdit.setObjectName("deltay_lineEdit")
self.gridLayout_2.addWidget(self.deltay_lineEdit, 3, 1, 1, 1)
self.label_7 = QtWidgets.QLabel(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_7.sizePolicy().hasHeightForWidth())
self.label_7.setSizePolicy(sizePolicy)
self.label_7.setObjectName("label_7")
self.gridLayout_2.addWidget(self.label_7, 0, 0, 1, 2)
self.y_lineedit = QtWidgets.QLineEdit(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.y_lineedit.sizePolicy().hasHeightForWidth())
self.y_lineedit.setSizePolicy(sizePolicy)
self.y_lineedit.setInputMethodHints(QtCore.Qt.ImhFormattedNumbersOnly|QtCore.Qt.ImhPreferNumbers)
self.y_lineedit.setObjectName("y_lineedit")
self.gridLayout_2.addWidget(self.y_lineedit, 2, 1, 1, 1)
self.y_label = QtWidgets.QLabel(self.groupBox)
self.y_label.setObjectName("y_label")
self.gridLayout_2.addWidget(self.y_label, 2, 0, 1, 1)
self.x_lineedit = QtWidgets.QLineEdit(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.x_lineedit.sizePolicy().hasHeightForWidth())
self.x_lineedit.setSizePolicy(sizePolicy)
self.x_lineedit.setInputMethodHints(QtCore.Qt.ImhFormattedNumbersOnly|QtCore.Qt.ImhPreferNumbers)
self.x_lineedit.setObjectName("x_lineedit")
self.gridLayout_2.addWidget(self.x_lineedit, 1, 1, 1, 1)
self.label_5 = QtWidgets.QLabel(self.groupBox)
self.label_5.setObjectName("label_5")
self.gridLayout_2.addWidget(self.label_5, 3, 0, 1, 1)
self.x_label = QtWidgets.QLabel(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.x_label.sizePolicy().hasHeightForWidth())
self.x_label.setSizePolicy(sizePolicy)
self.x_label.setObjectName("x_label")
self.gridLayout_2.addWidget(self.x_label, 1, 0, 1, 1)
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout_2.addItem(spacerItem1, 4, 1, 1, 1)
self.horizontalLayout_4.addLayout(self.gridLayout_2)
self.verticalLayout_3.addWidget(self.groupBox) self.verticalLayout_3.addWidget(self.groupBox)
self.dsdfsf = QtWidgets.QLabel(self.tabWidgetPage1)
self.dsdfsf.setObjectName("dsdfsf")
self.verticalLayout_3.addWidget(self.dsdfsf)
self.gridLayout = QtWidgets.QGridLayout()
self.gridLayout.setObjectName("gridLayout")
self.re_match_index = QtWidgets.QSpinBox(self.tabWidgetPage1)
self.re_match_index.setMinimum(1)
self.re_match_index.setObjectName("re_match_index")
self.gridLayout.addWidget(self.re_match_index, 1, 3, 1, 1)
self.label_9 = QtWidgets.QLabel(self.tabWidgetPage1)
self.label_9.setObjectName("label_9")
self.gridLayout.addWidget(self.label_9, 1, 2, 1, 1)
self.regex_input = QtWidgets.QLineEdit(self.tabWidgetPage1)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.regex_input.sizePolicy().hasHeightForWidth())
self.regex_input.setSizePolicy(sizePolicy)
self.regex_input.setObjectName("regex_input")
self.gridLayout.addWidget(self.regex_input, 1, 1, 1, 1)
self.re_button = QtWidgets.QRadioButton(self.tabWidgetPage1)
self.re_button.setChecked(True)
self.re_button.setObjectName("re_button")
self.buttonGroup_2 = QtWidgets.QButtonGroup(ascii_reader)
self.buttonGroup_2.setObjectName("buttonGroup_2")
self.buttonGroup_2.addButton(self.re_button)
self.gridLayout.addWidget(self.re_button, 1, 0, 1, 1)
self.custom_input = QtWidgets.QLineEdit(self.tabWidgetPage1)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.custom_input.sizePolicy().hasHeightForWidth())
self.custom_input.setSizePolicy(sizePolicy)
self.custom_input.setObjectName("custom_input")
self.gridLayout.addWidget(self.custom_input, 2, 1, 1, 1)
self.custom_button = QtWidgets.QRadioButton(self.tabWidgetPage1)
self.custom_button.setObjectName("custom_button")
self.buttonGroup_2.addButton(self.custom_button)
self.gridLayout.addWidget(self.custom_button, 2, 0, 1, 1)
self.label_8 = QtWidgets.QLabel(self.tabWidgetPage1)
self.label_8.setWordWrap(True)
self.label_8.setObjectName("label_8")
self.gridLayout.addWidget(self.label_8, 0, 0, 1, 4)
self.verticalLayout_3.addLayout(self.gridLayout)
self.groupBox_2 = QtWidgets.QGroupBox(self.tabWidgetPage1) self.groupBox_2 = QtWidgets.QGroupBox(self.tabWidgetPage1)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.groupBox_2.sizePolicy().hasHeightForWidth())
self.groupBox_2.setSizePolicy(sizePolicy)
self.groupBox_2.setObjectName("groupBox_2") self.groupBox_2.setObjectName("groupBox_2")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox_2) self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox_2)
self.verticalLayout_2.setContentsMargins(3, 3, 3, 3) self.verticalLayout_2.setContentsMargins(3, 3, 3, 3)
@ -215,24 +289,19 @@ class Ui_ascii_reader(object):
self.pushButton.setSizePolicy(sizePolicy) self.pushButton.setSizePolicy(sizePolicy)
self.pushButton.setObjectName("pushButton") self.pushButton.setObjectName("pushButton")
self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.pushButton) self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.pushButton)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.formLayout.setItem(6, QtWidgets.QFormLayout.FieldRole, spacerItem) self.formLayout.setItem(6, QtWidgets.QFormLayout.FieldRole, spacerItem2)
self.horizontalLayout_3.addLayout(self.formLayout) self.horizontalLayout_3.addLayout(self.formLayout)
self.tabWidget.addTab(self.tabWidgetPage2, "") self.tabWidget.addTab(self.tabWidgetPage2, "")
self.verticalLayout.addWidget(self.tabWidget) self.verticalLayout.addWidget(self.tabWidget)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem1)
self.verticalLayout.addLayout(self.horizontalLayout)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1) self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1)
self.horizontalLayout_2.setObjectName("horizontalLayout_2") self.horizontalLayout_2.setObjectName("horizontalLayout_2")
spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem3)
self.skippy_checkbox = QtWidgets.QCheckBox(ascii_reader) self.skippy_checkbox = QtWidgets.QCheckBox(ascii_reader)
self.skippy_checkbox.setObjectName("skippy_checkbox") self.skippy_checkbox.setObjectName("skippy_checkbox")
self.horizontalLayout_2.addWidget(self.skippy_checkbox) self.horizontalLayout_2.addWidget(self.skippy_checkbox)
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem2)
self.verticalLayout.addLayout(self.horizontalLayout_2) self.verticalLayout.addLayout(self.horizontalLayout_2)
self.buttonbox = QtWidgets.QDialogButtonBox(ascii_reader) self.buttonbox = QtWidgets.QDialogButtonBox(ascii_reader)
self.buttonbox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) self.buttonbox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
@ -247,14 +316,20 @@ class Ui_ascii_reader(object):
ascii_reader.setTabOrder(self.column_checkBox, self.line_spinBox) ascii_reader.setTabOrder(self.column_checkBox, self.line_spinBox)
ascii_reader.setTabOrder(self.line_spinBox, self.preview_spinBox) ascii_reader.setTabOrder(self.line_spinBox, self.preview_spinBox)
ascii_reader.setTabOrder(self.preview_spinBox, self.pts_radioButton) ascii_reader.setTabOrder(self.preview_spinBox, self.pts_radioButton)
ascii_reader.setTabOrder(self.pts_radioButton, self.FID_radioButton) ascii_reader.setTabOrder(self.pts_radioButton, self.dsc_radioButton)
ascii_reader.setTabOrder(self.dsc_radioButton, self.FID_radioButton)
ascii_reader.setTabOrder(self.FID_radioButton, self.spectrum_radioButton) ascii_reader.setTabOrder(self.FID_radioButton, self.spectrum_radioButton)
ascii_reader.setTabOrder(self.spectrum_radioButton, self.x_lineedit) ascii_reader.setTabOrder(self.spectrum_radioButton, self.bds_radioButton)
ascii_reader.setTabOrder(self.bds_radioButton, self.x_lineedit)
ascii_reader.setTabOrder(self.x_lineedit, self.y_lineedit) ascii_reader.setTabOrder(self.x_lineedit, self.y_lineedit)
ascii_reader.setTabOrder(self.y_lineedit, self.deltay_lineEdit) ascii_reader.setTabOrder(self.y_lineedit, self.deltay_lineEdit)
ascii_reader.setTabOrder(self.deltay_lineEdit, self.ascii_table) ascii_reader.setTabOrder(self.deltay_lineEdit, self.re_button)
ascii_reader.setTabOrder(self.ascii_table, self.skippy_checkbox) ascii_reader.setTabOrder(self.re_button, self.regex_input)
ascii_reader.setTabOrder(self.skippy_checkbox, self.delay_textfield) ascii_reader.setTabOrder(self.regex_input, self.re_match_index)
ascii_reader.setTabOrder(self.re_match_index, self.custom_button)
ascii_reader.setTabOrder(self.custom_button, self.custom_input)
ascii_reader.setTabOrder(self.custom_input, self.ascii_table)
ascii_reader.setTabOrder(self.ascii_table, self.delay_textfield)
ascii_reader.setTabOrder(self.delay_textfield, self.delay_lineedit) ascii_reader.setTabOrder(self.delay_textfield, self.delay_lineedit)
ascii_reader.setTabOrder(self.delay_lineedit, self.start_lineedit) ascii_reader.setTabOrder(self.delay_lineedit, self.start_lineedit)
ascii_reader.setTabOrder(self.start_lineedit, self.end_lineedit) ascii_reader.setTabOrder(self.start_lineedit, self.end_lineedit)
@ -262,24 +337,33 @@ class Ui_ascii_reader(object):
ascii_reader.setTabOrder(self.log_checkBox, self.staggered_checkBox) ascii_reader.setTabOrder(self.log_checkBox, self.staggered_checkBox)
ascii_reader.setTabOrder(self.staggered_checkBox, self.stag_lineEdit) ascii_reader.setTabOrder(self.staggered_checkBox, self.stag_lineEdit)
ascii_reader.setTabOrder(self.stag_lineEdit, self.pushButton) ascii_reader.setTabOrder(self.stag_lineEdit, self.pushButton)
ascii_reader.setTabOrder(self.pushButton, self.skippy_checkbox)
def retranslateUi(self, ascii_reader): def retranslateUi(self, ascii_reader):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
ascii_reader.setWindowTitle(_translate("ascii_reader", "Read text file")) ascii_reader.setWindowTitle(_translate("ascii_reader", "Read text file"))
self.groupBox.setTitle(_translate("ascii_reader", "Options")) self.groupBox.setTitle(_translate("ascii_reader", "Options"))
self.FID_radioButton.setText(_translate("ascii_reader", "FID"))
self.x_lineedit.setToolTip(_translate("ascii_reader", "<html><head/><body><p>Specify which column is used as x-value.</p></body></html>"))
self.y_label.setText(_translate("ascii_reader", "y"))
self.pts_radioButton.setText(_translate("ascii_reader", "Points"))
self.label_5.setText(_translate("ascii_reader", "<html><head/><body><p>Δy</p></body></html>"))
self.column_checkBox.setText(_translate("ascii_reader", "Column name")) self.column_checkBox.setText(_translate("ascii_reader", "Column name"))
self.label.setText(_translate("ascii_reader", "Import as"))
self.label_7.setText(_translate("ascii_reader", "Use columns as"))
self.y_lineedit.setToolTip(_translate("ascii_reader", "<html><head/><body><p>Specify which columns are read for y-values. (\'Points\': Every number creates a new data set;\'FID\'/\'Spectrum\': Numbers at even positions are used for real parts, at odd positions for imaginary parts.)</p></body></html>"))
self.spectrum_radioButton.setText(_translate("ascii_reader", "Spectrum"))
self.x_label.setText(_translate("ascii_reader", "x"))
self.line_spinBox.setPrefix(_translate("ascii_reader", "header line ")) self.line_spinBox.setPrefix(_translate("ascii_reader", "header line "))
self.label_6.setText(_translate("ascii_reader", "Preview length")) self.label_6.setText(_translate("ascii_reader", "Preview length"))
self.label.setText(_translate("ascii_reader", "Import as"))
self.pts_radioButton.setText(_translate("ascii_reader", "Points"))
self.dsc_radioButton.setText(_translate("ascii_reader", "DSC"))
self.FID_radioButton.setText(_translate("ascii_reader", "FID"))
self.spectrum_radioButton.setText(_translate("ascii_reader", "Spectrum"))
self.bds_radioButton.setText(_translate("ascii_reader", "BDS"))
self.label_7.setText(_translate("ascii_reader", "Use columns as"))
self.y_lineedit.setToolTip(_translate("ascii_reader", "<html><head/><body><p>Specify which columns are used for y values.</p><p>- \'Points\': Every number creates a new data set;<br/>- \'FID\'/\'Spectrum\': Numbers at even positions are used for real parts, at odd positions for imaginary parts.</p></body></html>"))
self.y_label.setText(_translate("ascii_reader", "y"))
self.x_lineedit.setToolTip(_translate("ascii_reader", "<html><head/><body><p>Specify which column is used for x values, write <span style=\" font-style:italic;\">index</span> to use row numbers (starting with 0). </p></body></html>"))
self.label_5.setText(_translate("ascii_reader", "<html><head/><body><p>Δy</p></body></html>"))
self.x_label.setText(_translate("ascii_reader", "x"))
self.dsdfsf.setText(_translate("ascii_reader", "Numerical value"))
self.label_9.setText(_translate("ascii_reader", "Match index"))
self.regex_input.setToolTip(_translate("ascii_reader", "<html><head/><body><p>Token:<br/>[abc]: Matches any of a, b, or c<br/>[a-z]: Matches any digit in the range a-z<br/>\\d: Matches any digit in the range 0-9 (equal to [0-9}</p><p>Quantifiers:<br/>a*: 0 or more of a<br/>a+: 1 or more of a<br/>a?: 0 or 1 of a</p></body></html>"))
self.re_button.setText(_translate("ascii_reader", "Regex"))
self.custom_button.setText(_translate("ascii_reader", "Custom value"))
self.label_8.setText(_translate("ascii_reader", "Filename"))
self.groupBox_2.setTitle(_translate("ascii_reader", "Preview")) self.groupBox_2.setTitle(_translate("ascii_reader", "Preview"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabWidgetPage1), _translate("ascii_reader", "Data")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabWidgetPage1), _translate("ascii_reader", "Data"))
self.label_2.setText(_translate("ascii_reader", "Number of delays")) self.label_2.setText(_translate("ascii_reader", "Number of delays"))

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '_ui/baseline_dialog.ui' # Form implementation generated from reading ui file 'src/resources/_ui/baseline_dialog.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.9
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
@ -44,7 +45,7 @@ class Ui_SignalEdit(object):
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox") self.buttonBox.setObjectName("buttonBox")
self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 3) self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 3)
self.graphicsView = PlotWidget(SignalEdit) self.graphicsView = NMRPlotWidget(SignalEdit)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -54,11 +55,11 @@ class Ui_SignalEdit(object):
self.gridLayout.addWidget(self.graphicsView, 0, 2, 1, 1) self.gridLayout.addWidget(self.graphicsView, 0, 2, 1, 1)
self.retranslateUi(SignalEdit) self.retranslateUi(SignalEdit)
self.buttonBox.accepted.connect(SignalEdit.accept) self.buttonBox.accepted.connect(SignalEdit.accept) # type: ignore
self.buttonBox.rejected.connect(SignalEdit.close) self.buttonBox.rejected.connect(SignalEdit.close) # type: ignore
QtCore.QMetaObject.connectSlotsByName(SignalEdit) QtCore.QMetaObject.connectSlotsByName(SignalEdit)
def retranslateUi(self, SignalEdit): def retranslateUi(self, SignalEdit):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
SignalEdit.setWindowTitle(_translate("SignalEdit", "Dialog")) SignalEdit.setWindowTitle(_translate("SignalEdit", "Dialog"))
from pyqtgraph import PlotWidget from ..lib.graph_items import NMRPlotWidget

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.7 # 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.
@ -70,7 +70,7 @@ class Ui_BaseWindow(object):
self.integralwidget = IntegralWidget() self.integralwidget = IntegralWidget()
self.integralwidget.setObjectName("integralwidget") self.integralwidget.setObjectName("integralwidget")
self.tabWidget.addTab(self.integralwidget, "") self.tabWidget.addTab(self.integralwidget, "")
self.area = QtWidgets.QMdiArea(self.splitter) self.area = MdiAreaTile(self.splitter)
self.area.setObjectName("area") self.area.setObjectName("area")
self.horizontalLayout.addWidget(self.splitter) self.horizontalLayout.addWidget(self.splitter)
BaseWindow.setCentralWidget(self.centralwidget) BaseWindow.setCentralWidget(self.centralwidget)
@ -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)
@ -117,6 +119,8 @@ class Ui_BaseWindow(object):
self.menuStuff = QtWidgets.QMenu(self.menubar) self.menuStuff = QtWidgets.QMenu(self.menubar)
self.menuStuff.setTitle("") self.menuStuff.setTitle("")
self.menuStuff.setObjectName("menuStuff") self.menuStuff.setObjectName("menuStuff")
self.menuDSC = QtWidgets.QMenu(self.menubar)
self.menuDSC.setObjectName("menuDSC")
BaseWindow.setMenuBar(self.menubar) BaseWindow.setMenuBar(self.menubar)
self.toolBar = QtWidgets.QToolBar(BaseWindow) self.toolBar = QtWidgets.QToolBar(BaseWindow)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
@ -151,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)
@ -261,6 +256,10 @@ class Ui_BaseWindow(object):
self.actionMaximize.setObjectName("actionMaximize") self.actionMaximize.setObjectName("actionMaximize")
self.actionTile = QtWidgets.QAction(BaseWindow) self.actionTile = QtWidgets.QAction(BaseWindow)
self.actionTile.setObjectName("actionTile") self.actionTile.setObjectName("actionTile")
self.actionTileVertical = QtWidgets.QAction(BaseWindow)
self.actionTileVertical.setObjectName("actionTileVertical")
self.actionTileHorizontal = QtWidgets.QAction(BaseWindow)
self.actionTileHorizontal.setObjectName("actionTileHorizontal")
self.actionMinimize = QtWidgets.QAction(BaseWindow) self.actionMinimize = QtWidgets.QAction(BaseWindow)
self.actionMinimize.setCheckable(True) self.actionMinimize.setCheckable(True)
self.actionMinimize.setVisible(False) self.actionMinimize.setVisible(False)
@ -307,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)
@ -358,6 +355,25 @@ class Ui_BaseWindow(object):
self.actionBugs.setObjectName("actionBugs") self.actionBugs.setObjectName("actionBugs")
self.actionShow_error_log = QtWidgets.QAction(BaseWindow) self.actionShow_error_log = QtWidgets.QAction(BaseWindow)
self.actionShow_error_log.setObjectName("actionShow_error_log") self.actionShow_error_log.setObjectName("actionShow_error_log")
self.actionCreate_starter = QtWidgets.QAction(BaseWindow)
self.actionCreate_starter.setObjectName("actionCreate_starter")
self.actionAbout = QtWidgets.QAction(BaseWindow)
self.actionAbout.setObjectName("actionAbout")
self.actionTNMH_model = QtWidgets.QAction(BaseWindow)
self.actionTNMH_model.setObjectName("actionTNMH_model")
self.actionBinning = QtWidgets.QAction(BaseWindow)
self.actionBinning.setObjectName("actionBinning")
self.actionTNMH = QtWidgets.QAction(BaseWindow)
self.actionTNMH.setObjectName("actionTNMH")
self.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)
@ -370,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)
@ -379,13 +398,14 @@ 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.actionDocumentation)
self.menuHelp.addAction(self.actionUpdate) self.menuHelp.addAction(self.actionUpdate)
self.menuHelp.addAction(self.actionBugs) self.menuHelp.addAction(self.actionBugs)
self.menuHelp.addAction(self.actionAbout)
self.menuNormalize.addAction(self.action_norm_max) self.menuNormalize.addAction(self.action_norm_max)
self.menuNormalize.addAction(self.action_norm_max_abs) self.menuNormalize.addAction(self.action_norm_max_abs)
self.menuNormalize.addSeparator() self.menuNormalize.addSeparator()
@ -401,6 +421,7 @@ class Ui_BaseWindow(object):
self.menuExtra.addSeparator() self.menuExtra.addSeparator()
self.menuExtra.addAction(self.menuNormalize.menuAction()) self.menuExtra.addAction(self.menuNormalize.menuAction())
self.menuExtra.addAction(self.actionInterpolation) self.menuExtra.addAction(self.actionInterpolation)
self.menuExtra.addAction(self.actionBinning)
self.menuExtra.addAction(self.actionRunning_values) self.menuExtra.addAction(self.actionRunning_values)
self.menuExtra.addAction(self.actionShift) self.menuExtra.addAction(self.actionShift)
self.menuExtra.addSeparator() self.menuExtra.addSeparator()
@ -411,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)
@ -422,7 +444,10 @@ class Ui_BaseWindow(object):
self.menuOptions.addSeparator() self.menuOptions.addSeparator()
self.menuOptions.addAction(self.action_colorcycle) self.menuOptions.addAction(self.action_colorcycle)
self.menuOptions.addAction(self.actionConfiguration) self.menuOptions.addAction(self.actionConfiguration)
self.menuOptions.addAction(self.actionCreate_starter)
self.menuView.addAction(self.actionTile) self.menuView.addAction(self.actionTile)
self.menuView.addAction(self.actionTileVertical)
self.menuView.addAction(self.actionTileHorizontal)
self.menuView.addAction(self.actionCascade_windows) self.menuView.addAction(self.actionCascade_windows)
self.menuWindow.addAction(self.actionNew_window) self.menuWindow.addAction(self.actionNew_window)
self.menuWindow.addAction(self.actionDelete_window) self.menuWindow.addAction(self.actionDelete_window)
@ -451,6 +476,7 @@ class Ui_BaseWindow(object):
self.menuStuff.addAction(self.actionLife) self.menuStuff.addAction(self.actionLife)
self.menuStuff.addAction(self.actionTetris) self.menuStuff.addAction(self.actionTetris)
self.menuStuff.addAction(self.actionMine) self.menuStuff.addAction(self.actionMine)
self.menuDSC.addAction(self.actionTNMH_model)
self.menubar.addAction(self.menuFile.menuAction()) self.menubar.addAction(self.menuFile.menuAction())
self.menubar.addAction(self.menuWindow.menuAction()) self.menubar.addAction(self.menuWindow.menuAction())
self.menubar.addAction(self.menuData.menuAction()) self.menubar.addAction(self.menuData.menuAction())
@ -459,6 +485,7 @@ class Ui_BaseWindow(object):
self.menubar.addAction(self.menuFit.menuAction()) self.menubar.addAction(self.menuFit.menuAction())
self.menubar.addAction(self.menuNMR.menuAction()) self.menubar.addAction(self.menuNMR.menuAction())
self.menubar.addAction(self.menuBDS.menuAction()) self.menubar.addAction(self.menuBDS.menuAction())
self.menubar.addAction(self.menuDSC.menuAction())
self.menubar.addAction(self.menuOptions.menuAction()) self.menubar.addAction(self.menuOptions.menuAction())
self.menubar.addAction(self.menuHelp.menuAction()) self.menubar.addAction(self.menuHelp.menuAction())
self.menubar.addAction(self.menuStuff.menuAction()) self.menubar.addAction(self.menuStuff.menuAction())
@ -474,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)
@ -499,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"))
@ -511,10 +538,10 @@ class Ui_BaseWindow(object):
self.menuNMR.setTitle(_translate("BaseWindow", "NMR")) self.menuNMR.setTitle(_translate("BaseWindow", "NMR"))
self.menuBDS.setTitle(_translate("BaseWindow", "BDS")) self.menuBDS.setTitle(_translate("BaseWindow", "BDS"))
self.menuSpectrum.setTitle(_translate("BaseWindow", "Spectrum")) self.menuSpectrum.setTitle(_translate("BaseWindow", "Spectrum"))
self.menuDSC.setTitle(_translate("BaseWindow", "DSC"))
self.toolBar.setWindowTitle(_translate("BaseWindow", "Main")) self.toolBar.setWindowTitle(_translate("BaseWindow", "Main"))
self.toolbar_edit.setWindowTitle(_translate("BaseWindow", "Math")) self.toolbar_edit.setWindowTitle(_translate("BaseWindow", "Math"))
self.toolBar_nmr.setWindowTitle(_translate("BaseWindow", "NMR")) self.toolBar_nmr.setWindowTitle(_translate("BaseWindow", "NMR"))
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"))
@ -566,6 +593,8 @@ class Ui_BaseWindow(object):
self.actionGuide_lines.setText(_translate("BaseWindow", "Draw lines...")) self.actionGuide_lines.setText(_translate("BaseWindow", "Draw lines..."))
self.actionMaximize.setText(_translate("BaseWindow", "Maximize")) self.actionMaximize.setText(_translate("BaseWindow", "Maximize"))
self.actionTile.setText(_translate("BaseWindow", "Tile windows")) self.actionTile.setText(_translate("BaseWindow", "Tile windows"))
self.actionTileVertical.setText(_translate("BaseWindow", "Tile windows vertically"))
self.actionTileHorizontal.setText(_translate("BaseWindow", "Tile windows horizontally"))
self.actionMinimize.setText(_translate("BaseWindow", "Minimize")) self.actionMinimize.setText(_translate("BaseWindow", "Minimize"))
self.actionNew_window.setText(_translate("BaseWindow", "New graph")) self.actionNew_window.setText(_translate("BaseWindow", "New graph"))
self.actionDelete_window.setText(_translate("BaseWindow", "Delete graph")) self.actionDelete_window.setText(_translate("BaseWindow", "Delete graph"))
@ -590,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..."))
@ -612,6 +640,17 @@ class Ui_BaseWindow(object):
self.action_draw_object.setText(_translate("BaseWindow", "Draw objects...")) self.action_draw_object.setText(_translate("BaseWindow", "Draw objects..."))
self.actionBugs.setText(_translate("BaseWindow", "Bugs! Problems! Wishes!")) self.actionBugs.setText(_translate("BaseWindow", "Bugs! Problems! Wishes!"))
self.actionShow_error_log.setText(_translate("BaseWindow", "Show error log")) self.actionShow_error_log.setText(_translate("BaseWindow", "Show error log"))
self.actionCreate_starter.setText(_translate("BaseWindow", "Create starter.."))
self.actionAbout.setText(_translate("BaseWindow", "About..."))
self.actionTNMH_model.setText(_translate("BaseWindow", "Tg , Hodge, TNMH,,,"))
self.actionBinning.setText(_translate("BaseWindow", "Binning..."))
self.actionTNMH.setText(_translate("BaseWindow", "TNMH..."))
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
@ -619,4 +658,5 @@ from ..data.signaledit.editsignalwidget import EditSignalWidget
from ..data.valueeditwidget import ValueEditWidget from ..data.valueeditwidget import ValueEditWidget
from ..fit.fitwindow import QFitDialog from ..fit.fitwindow import QFitDialog
from ..graphs.drawings import DrawingsWidget from ..graphs.drawings import DrawingsWidget
from ..lib.mdiarea import MdiAreaTile
from ..nmr.t1widget import QT1Widget from ..nmr.t1widget import QT1Widget

View File

@ -1,13 +1,16 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/bdsdialog.ui' # Form implementation generated from reading ui file 'src/resources/_ui/bdsdialog.ui'
# #
# Created by: PyQt5 UI code generator 5.9.2 # Created by: PyQt5 UI code generator 5.15.9
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object): class Ui_Dialog(object):
def setupUi(self, Dialog): def setupUi(self, Dialog):
Dialog.setObjectName("Dialog") Dialog.setObjectName("Dialog")
@ -16,12 +19,13 @@ class Ui_Dialog(object):
self.gridLayout.setContentsMargins(3, 3, 3, 3) self.gridLayout.setContentsMargins(3, 3, 3, 3)
self.gridLayout.setSpacing(3) self.gridLayout.setSpacing(3)
self.gridLayout.setObjectName("gridLayout") self.gridLayout.setObjectName("gridLayout")
self.listWidget = QtWidgets.QListWidget(Dialog) self.listWidget = QListWidgetSelect(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
self.listWidget.setSizePolicy(sizePolicy) self.listWidget.setSizePolicy(sizePolicy)
self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.listWidget.setObjectName("listWidget") self.listWidget.setObjectName("listWidget")
self.gridLayout.addWidget(self.listWidget, 1, 0, 2, 1) self.gridLayout.addWidget(self.listWidget, 1, 0, 2, 1)
self.groupBox_2 = QtWidgets.QGroupBox(Dialog) self.groupBox_2 = QtWidgets.QGroupBox(Dialog)
@ -93,8 +97,8 @@ class Ui_Dialog(object):
self.gridLayout.addWidget(self.label, 0, 0, 1, 2) self.gridLayout.addWidget(self.label, 0, 0, 1, 2)
self.retranslateUi(Dialog) self.retranslateUi(Dialog)
self.buttonBox.accepted.connect(Dialog.accept) self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
self.buttonBox.rejected.connect(Dialog.reject) self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(Dialog) QtCore.QMetaObject.connectSlotsByName(Dialog)
Dialog.setTabOrder(self.freq_button, self.temp_button) Dialog.setTabOrder(self.freq_button, self.temp_button)
Dialog.setTabOrder(self.temp_button, self.eps_checkBox) Dialog.setTabOrder(self.temp_button, self.eps_checkBox)
@ -117,4 +121,4 @@ class Ui_Dialog(object):
self.temp_checkBox.setText(_translate("Dialog", "Meas. temperature")) self.temp_checkBox.setText(_translate("Dialog", "Meas. temperature"))
self.time_checkBox.setText(_translate("Dialog", "Meas. time")) self.time_checkBox.setText(_translate("Dialog", "Meas. time"))
self.label.setText(_translate("Dialog", "Found entries")) self.label.setText(_translate("Dialog", "Found entries"))
from ..lib.listwidget import QListWidgetSelect

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,29 +1,113 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/dscfile_dialog.ui' # Form implementation generated from reading ui file 'src/resources/_ui/dscfile_dialog.ui'
# #
# Created by: PyQt5 UI code generator 5.9.2 # Created by: PyQt5 UI code generator 5.15.9
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object): class Ui_Dialog(object):
def setupUi(self, Dialog): def setupUi(self, Dialog):
Dialog.setObjectName("Dialog") Dialog.setObjectName("Dialog")
Dialog.resize(962, 662) Dialog.resize(1341, 799)
self.gridLayout_2 = QtWidgets.QGridLayout(Dialog) self.verticalLayout_5 = QtWidgets.QVBoxLayout(Dialog)
self.gridLayout_2.setObjectName("gridLayout_2") self.verticalLayout_5.setObjectName("verticalLayout_5")
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) self.splitter = QtWidgets.QSplitter(Dialog)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.splitter.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Save) self.splitter.setObjectName("splitter")
self.buttonBox.setObjectName("buttonBox") self.verticalLayoutWidget = QtWidgets.QWidget(self.splitter)
self.gridLayout_2.addWidget(self.buttonBox, 1, 1, 1, 1) self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
self.gridLayout_4 = QtWidgets.QGridLayout() self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
self.gridLayout_4.setContentsMargins(-1, 0, 0, -1) self.verticalLayout_4.setContentsMargins(6, 6, 6, 6)
self.gridLayout_4.setSpacing(3) self.verticalLayout_4.setObjectName("verticalLayout_4")
self.gridLayout_4.setObjectName("gridLayout_4") self.groupBox = QtWidgets.QGroupBox(self.verticalLayoutWidget)
self.cp_checkBox = QtWidgets.QCheckBox(Dialog) self.groupBox.setFlat(False)
self.groupBox.setObjectName("groupBox")
self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox)
self.verticalLayout.setContentsMargins(6, 6, 6, 6)
self.verticalLayout.setObjectName("verticalLayout")
self.step_listWidget = QtWidgets.QListWidget(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Minimum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.step_listWidget.sizePolicy().hasHeightForWidth())
self.step_listWidget.setSizePolicy(sizePolicy)
self.step_listWidget.setMinimumSize(QtCore.QSize(0, 0))
self.step_listWidget.setObjectName("step_listWidget")
self.verticalLayout.addWidget(self.step_listWidget)
self.verticalLayout_4.addWidget(self.groupBox)
self.groupBox_2 = QtWidgets.QGroupBox(self.verticalLayoutWidget)
self.groupBox_2.setObjectName("groupBox_2")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox_2)
self.verticalLayout_2.setContentsMargins(6, 6, 6, 6)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.groupBox_4 = QtWidgets.QGroupBox(self.groupBox_2)
self.groupBox_4.setObjectName("groupBox_4")
self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.groupBox_4)
self.verticalLayout_6.setContentsMargins(6, 6, 6, 6)
self.verticalLayout_6.setObjectName("verticalLayout_6")
self.empty_label = QtWidgets.QLabel(self.groupBox_4)
self.empty_label.setObjectName("empty_label")
self.verticalLayout_6.addWidget(self.empty_label)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setSpacing(3)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.loadempty_button = QtWidgets.QPushButton(self.groupBox_4)
self.loadempty_button.setObjectName("loadempty_button")
self.horizontalLayout_2.addWidget(self.loadempty_button)
self.delempty_button = QtWidgets.QPushButton(self.groupBox_4)
self.delempty_button.setObjectName("delempty_button")
self.horizontalLayout_2.addWidget(self.delempty_button)
self.verticalLayout_6.addLayout(self.horizontalLayout_2)
self.verticalLayout_2.addWidget(self.groupBox_4)
self.groupBox_5 = QtWidgets.QGroupBox(self.groupBox_2)
self.groupBox_5.setObjectName("groupBox_5")
self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.groupBox_5)
self.verticalLayout_7.setContentsMargins(6, 6, 6, 6)
self.verticalLayout_7.setSpacing(3)
self.verticalLayout_7.setObjectName("verticalLayout_7")
self.none_radioButton = QtWidgets.QRadioButton(self.groupBox_5)
self.none_radioButton.setObjectName("none_radioButton")
self.buttonGroup = QtWidgets.QButtonGroup(Dialog)
self.buttonGroup.setObjectName("buttonGroup")
self.buttonGroup.addButton(self.none_radioButton)
self.verticalLayout_7.addWidget(self.none_radioButton)
self.isotherm_radioButton = QtWidgets.QRadioButton(self.groupBox_5)
self.isotherm_radioButton.setChecked(True)
self.isotherm_radioButton.setObjectName("isotherm_radioButton")
self.buttonGroup.addButton(self.isotherm_radioButton)
self.verticalLayout_7.addWidget(self.isotherm_radioButton)
self.slope_radioButton = QtWidgets.QRadioButton(self.groupBox_5)
self.slope_radioButton.setObjectName("slope_radioButton")
self.buttonGroup.addButton(self.slope_radioButton)
self.verticalLayout_7.addWidget(self.slope_radioButton)
self.widget = QtWidgets.QWidget(self.groupBox_5)
self.widget.setMinimumSize(QtCore.QSize(0, 33))
self.widget.setObjectName("widget")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.widget)
self.horizontalLayout_3.setContentsMargins(3, 3, 3, 3)
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.limit1_lineedit = QtWidgets.QLineEdit(self.widget)
self.limit1_lineedit.setObjectName("limit1_lineedit")
self.horizontalLayout_3.addWidget(self.limit1_lineedit)
self.limit2_lineedit = QtWidgets.QLineEdit(self.widget)
self.limit2_lineedit.setText("")
self.limit2_lineedit.setObjectName("limit2_lineedit")
self.horizontalLayout_3.addWidget(self.limit2_lineedit)
self.verticalLayout_7.addWidget(self.widget)
self.verticalLayout_2.addWidget(self.groupBox_5)
self.verticalLayout_4.addWidget(self.groupBox_2)
self.groupBox_3 = QtWidgets.QGroupBox(self.verticalLayoutWidget)
self.groupBox_3.setObjectName("groupBox_3")
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.groupBox_3)
self.verticalLayout_3.setContentsMargins(6, 6, 6, 6)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.cp_checkBox = QtWidgets.QCheckBox(self.groupBox_3)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -31,36 +115,8 @@ class Ui_Dialog(object):
self.cp_checkBox.setSizePolicy(sizePolicy) self.cp_checkBox.setSizePolicy(sizePolicy)
self.cp_checkBox.setChecked(True) self.cp_checkBox.setChecked(True)
self.cp_checkBox.setObjectName("cp_checkBox") self.cp_checkBox.setObjectName("cp_checkBox")
self.gridLayout_4.addWidget(self.cp_checkBox, 11, 0, 1, 4) self.verticalLayout_3.addWidget(self.cp_checkBox)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.reference_tableWidget = QtWidgets.QTableWidget(self.groupBox_3)
self.horizontalLayout_2.setSpacing(3)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.loadempty_button = QtWidgets.QPushButton(Dialog)
self.loadempty_button.setObjectName("loadempty_button")
self.horizontalLayout_2.addWidget(self.loadempty_button)
self.delempty_button = QtWidgets.QPushButton(Dialog)
self.delempty_button.setObjectName("delempty_button")
self.horizontalLayout_2.addWidget(self.delempty_button)
self.gridLayout_4.addLayout(self.horizontalLayout_2, 5, 0, 1, 4)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout_4.addItem(spacerItem, 12, 0, 1, 1)
self.isotherm_radioButton = QtWidgets.QRadioButton(Dialog)
self.isotherm_radioButton.setChecked(True)
self.isotherm_radioButton.setObjectName("isotherm_radioButton")
self.buttonGroup = QtWidgets.QButtonGroup(Dialog)
self.buttonGroup.setObjectName("buttonGroup")
self.buttonGroup.addButton(self.isotherm_radioButton)
self.gridLayout_4.addWidget(self.isotherm_radioButton, 6, 1, 1, 1)
self.label_4 = QtWidgets.QLabel(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth())
self.label_4.setSizePolicy(sizePolicy)
self.label_4.setStyleSheet("font-weight: bold")
self.label_4.setObjectName("label_4")
self.gridLayout_4.addWidget(self.label_4, 0, 0, 1, 4)
self.reference_tableWidget = QtWidgets.QTableWidget(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -75,57 +131,11 @@ class Ui_Dialog(object):
self.reference_tableWidget.horizontalHeader().setVisible(False) self.reference_tableWidget.horizontalHeader().setVisible(False)
self.reference_tableWidget.horizontalHeader().setStretchLastSection(True) self.reference_tableWidget.horizontalHeader().setStretchLastSection(True)
self.reference_tableWidget.verticalHeader().setVisible(False) self.reference_tableWidget.verticalHeader().setVisible(False)
self.gridLayout_4.addWidget(self.reference_tableWidget, 9, 0, 1, 4) self.verticalLayout_3.addWidget(self.reference_tableWidget)
self.step_listWidget = QtWidgets.QListWidget(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Minimum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.step_listWidget.sizePolicy().hasHeightForWidth())
self.step_listWidget.setSizePolicy(sizePolicy)
self.step_listWidget.setMinimumSize(QtCore.QSize(0, 0))
self.step_listWidget.setObjectName("step_listWidget")
self.gridLayout_4.addWidget(self.step_listWidget, 1, 0, 1, 4)
self.label = QtWidgets.QLabel(Dialog)
self.label.setObjectName("label")
self.gridLayout_4.addWidget(self.label, 6, 0, 1, 1)
self.slope_radioButton = QtWidgets.QRadioButton(Dialog)
self.slope_radioButton.setObjectName("slope_radioButton")
self.buttonGroup.addButton(self.slope_radioButton)
self.gridLayout_4.addWidget(self.slope_radioButton, 6, 2, 1, 1)
self.empty_label = QtWidgets.QLabel(Dialog)
self.empty_label.setObjectName("empty_label")
self.gridLayout_4.addWidget(self.empty_label, 4, 0, 1, 4)
self.label_3 = QtWidgets.QLabel(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth())
self.label_3.setSizePolicy(sizePolicy)
self.label_3.setStyleSheet("font-weight: bold")
self.label_3.setObjectName("label_3")
self.gridLayout_4.addWidget(self.label_3, 8, 0, 1, 4)
self.label_2 = QtWidgets.QLabel(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth())
self.label_2.setSizePolicy(sizePolicy)
self.label_2.setStyleSheet("font-weight: bold")
self.label_2.setObjectName("label_2")
self.gridLayout_4.addWidget(self.label_2, 3, 0, 1, 4)
self.line = QtWidgets.QFrame(Dialog)
self.line.setFrameShape(QtWidgets.QFrame.HLine)
self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
self.line.setObjectName("line")
self.gridLayout_4.addWidget(self.line, 7, 0, 1, 4)
self.none_radioButton = QtWidgets.QRadioButton(Dialog)
self.none_radioButton.setObjectName("none_radioButton")
self.buttonGroup.addButton(self.none_radioButton)
self.gridLayout_4.addWidget(self.none_radioButton, 6, 3, 1, 1)
self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setSpacing(3) self.horizontalLayout.setSpacing(3)
self.horizontalLayout.setObjectName("horizontalLayout") self.horizontalLayout.setObjectName("horizontalLayout")
self.ref_add_pushButton = QtWidgets.QPushButton(Dialog) self.ref_add_pushButton = QtWidgets.QPushButton(self.groupBox_3)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -133,7 +143,7 @@ class Ui_Dialog(object):
self.ref_add_pushButton.setSizePolicy(sizePolicy) self.ref_add_pushButton.setSizePolicy(sizePolicy)
self.ref_add_pushButton.setObjectName("ref_add_pushButton") self.ref_add_pushButton.setObjectName("ref_add_pushButton")
self.horizontalLayout.addWidget(self.ref_add_pushButton) self.horizontalLayout.addWidget(self.ref_add_pushButton)
self.ref_remove_pushButton = QtWidgets.QPushButton(Dialog) self.ref_remove_pushButton = QtWidgets.QPushButton(self.groupBox_3)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -141,16 +151,22 @@ class Ui_Dialog(object):
self.ref_remove_pushButton.setSizePolicy(sizePolicy) self.ref_remove_pushButton.setSizePolicy(sizePolicy)
self.ref_remove_pushButton.setObjectName("ref_remove_pushButton") self.ref_remove_pushButton.setObjectName("ref_remove_pushButton")
self.horizontalLayout.addWidget(self.ref_remove_pushButton) self.horizontalLayout.addWidget(self.ref_remove_pushButton)
self.gridLayout_4.addLayout(self.horizontalLayout, 10, 0, 1, 4) self.verticalLayout_3.addLayout(self.horizontalLayout)
self.line_2 = QtWidgets.QFrame(Dialog) self.verticalLayout_4.addWidget(self.groupBox_3)
self.line_2.setFrameShape(QtWidgets.QFrame.HLine) spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken) self.verticalLayout_4.addItem(spacerItem)
self.line_2.setObjectName("line_2") self.buttonBox = QtWidgets.QDialogButtonBox(self.verticalLayoutWidget)
self.gridLayout_4.addWidget(self.line_2, 2, 0, 1, 4) self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.gridLayout_2.addLayout(self.gridLayout_4, 0, 0, 1, 1) self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Save)
self.gridLayout = QtWidgets.QGridLayout() self.buttonBox.setCenterButtons(True)
self.buttonBox.setObjectName("buttonBox")
self.verticalLayout_4.addWidget(self.buttonBox)
self.layoutWidget = QtWidgets.QWidget(self.splitter)
self.layoutWidget.setObjectName("layoutWidget")
self.gridLayout = QtWidgets.QGridLayout(self.layoutWidget)
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.setObjectName("gridLayout") self.gridLayout.setObjectName("gridLayout")
self.raw_graph = PlotWidget(Dialog) self.raw_graph = NMRPlotWidget(self.layoutWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -159,7 +175,7 @@ class Ui_Dialog(object):
self.raw_graph.setMinimumSize(QtCore.QSize(300, 200)) self.raw_graph.setMinimumSize(QtCore.QSize(300, 200))
self.raw_graph.setObjectName("raw_graph") self.raw_graph.setObjectName("raw_graph")
self.gridLayout.addWidget(self.raw_graph, 0, 0, 1, 1) self.gridLayout.addWidget(self.raw_graph, 0, 0, 1, 1)
self.calib_graph = PlotWidget(Dialog) self.calib_graph = NMRPlotWidget(self.layoutWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -168,7 +184,7 @@ class Ui_Dialog(object):
self.calib_graph.setMinimumSize(QtCore.QSize(300, 200)) self.calib_graph.setMinimumSize(QtCore.QSize(300, 200))
self.calib_graph.setObjectName("calib_graph") self.calib_graph.setObjectName("calib_graph")
self.gridLayout.addWidget(self.calib_graph, 1, 0, 1, 1) self.gridLayout.addWidget(self.calib_graph, 1, 0, 1, 1)
self.baseline_graph = PlotWidget(Dialog) self.baseline_graph = NMRPlotWidget(self.layoutWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -177,7 +193,7 @@ class Ui_Dialog(object):
self.baseline_graph.setMinimumSize(QtCore.QSize(300, 200)) self.baseline_graph.setMinimumSize(QtCore.QSize(300, 200))
self.baseline_graph.setObjectName("baseline_graph") self.baseline_graph.setObjectName("baseline_graph")
self.gridLayout.addWidget(self.baseline_graph, 0, 1, 1, 1) self.gridLayout.addWidget(self.baseline_graph, 0, 1, 1, 1)
self.end_graph = PlotWidget(Dialog) self.end_graph = NMRPlotWidget(self.layoutWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -186,28 +202,30 @@ class Ui_Dialog(object):
self.end_graph.setMinimumSize(QtCore.QSize(0, 0)) self.end_graph.setMinimumSize(QtCore.QSize(0, 0))
self.end_graph.setObjectName("end_graph") self.end_graph.setObjectName("end_graph")
self.gridLayout.addWidget(self.end_graph, 1, 1, 1, 1) self.gridLayout.addWidget(self.end_graph, 1, 1, 1, 1)
self.gridLayout_2.addLayout(self.gridLayout, 0, 1, 1, 1) self.verticalLayout_5.addWidget(self.splitter)
self.retranslateUi(Dialog) self.retranslateUi(Dialog)
self.buttonBox.accepted.connect(Dialog.accept) self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
self.buttonBox.rejected.connect(Dialog.reject) self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(Dialog) QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog): def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Read DSC file")) Dialog.setWindowTitle(_translate("Dialog", "Read DSC file"))
self.cp_checkBox.setText(_translate("Dialog", "Convert to heat capacity")) self.groupBox.setTitle(_translate("Dialog", "Detected steps"))
self.groupBox_2.setTitle(_translate("Dialog", "Baseline corrections"))
self.groupBox_4.setTitle(_translate("Dialog", "Empty measurement"))
self.empty_label.setText(_translate("Dialog", "No emtpy measurement"))
self.loadempty_button.setText(_translate("Dialog", "Load empty")) self.loadempty_button.setText(_translate("Dialog", "Load empty"))
self.delempty_button.setText(_translate("Dialog", "Remove empty")) self.delempty_button.setText(_translate("Dialog", "Remove empty"))
self.isotherm_radioButton.setText(_translate("Dialog", "Isotherms")) self.groupBox_5.setTitle(_translate("Dialog", "Slope correction"))
self.label_4.setText(_translate("Dialog", "Detected steps"))
self.label.setText(_translate("Dialog", "Slope"))
self.slope_radioButton.setText(_translate("Dialog", "Initial slope"))
self.empty_label.setText(_translate("Dialog", "Empty measurement"))
self.label_3.setText(_translate("Dialog", "Calibration"))
self.label_2.setText(_translate("Dialog", "Baseline"))
self.none_radioButton.setText(_translate("Dialog", "None")) self.none_radioButton.setText(_translate("Dialog", "None"))
self.isotherm_radioButton.setText(_translate("Dialog", "Isotherms"))
self.slope_radioButton.setText(_translate("Dialog", "Curve slope"))
self.limit1_lineedit.setPlaceholderText(_translate("Dialog", "start (in min)"))
self.limit2_lineedit.setPlaceholderText(_translate("Dialog", "stop (in min)"))
self.groupBox_3.setTitle(_translate("Dialog", "References"))
self.cp_checkBox.setText(_translate("Dialog", "Use reference to convert to heat capacity"))
self.ref_add_pushButton.setText(_translate("Dialog", "Add reference")) self.ref_add_pushButton.setText(_translate("Dialog", "Add reference"))
self.ref_remove_pushButton.setText(_translate("Dialog", "Remove reference")) self.ref_remove_pushButton.setText(_translate("Dialog", "Remove reference"))
from ..lib.graph_items import NMRPlotWidget
from pyqtgraph import PlotWidget

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/eval_expr_dialog.ui' # Form implementation generated from reading ui file 'src/resources/_ui/eval_expr_dialog.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.2
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
@ -36,14 +37,14 @@ class Ui_CalcDialog(object):
self.label_2 = QtWidgets.QLabel(self.page) self.label_2 = QtWidgets.QLabel(self.page)
self.label_2.setObjectName("label_2") self.label_2.setObjectName("label_2")
self.verticalLayout_2.addWidget(self.label_2) self.verticalLayout_2.addWidget(self.label_2)
self.listWidget = QtWidgets.QListWidget(self.page) self.listWidget = QListWidgetSelect(self.page)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
self.listWidget.setSizePolicy(sizePolicy) self.listWidget.setSizePolicy(sizePolicy)
self.listWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) self.listWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.listWidget.setObjectName("listWidget") self.listWidget.setObjectName("listWidget")
self.verticalLayout_2.addWidget(self.listWidget) self.verticalLayout_2.addWidget(self.listWidget)
self.overwrite_checkbox = QtWidgets.QCheckBox(self.page) self.overwrite_checkbox = QtWidgets.QCheckBox(self.page)
@ -202,7 +203,7 @@ class Ui_CalcDialog(object):
self.label_8.setBuddy(self.line_doubleSpinBox) self.label_8.setBuddy(self.line_doubleSpinBox)
self.retranslateUi(CalcDialog) self.retranslateUi(CalcDialog)
self.stackedWidget.setCurrentIndex(2) self.stackedWidget.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(CalcDialog) QtCore.QMetaObject.connectSlotsByName(CalcDialog)
CalcDialog.setTabOrder(self.calc_edit, self.listWidget) CalcDialog.setTabOrder(self.calc_edit, self.listWidget)
CalcDialog.setTabOrder(self.listWidget, self.overwrite_checkbox) CalcDialog.setTabOrder(self.listWidget, self.overwrite_checkbox)
@ -237,4 +238,5 @@ class Ui_CalcDialog(object):
self.label_11.setText(_translate("CalcDialog", "Style")) self.label_11.setText(_translate("CalcDialog", "Style"))
self.label.setText(_translate("CalcDialog", "Expressions are evaluated line by line and change previous values")) self.label.setText(_translate("CalcDialog", "Expressions are evaluated line by line and change previous values"))
from ..lib.delegates import ColorListEditor, LineStyleEditor, SymbolStyleEditor from ..lib.delegates import ColorListEditor, LineStyleEditor, SymbolStyleEditor
from ..lib.listwidget import QListWidgetSelect
from ..lib.namespace import QNamespaceWidget from ..lib.namespace import QNamespaceWidget

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

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/fitdialog.ui' # Form implementation generated from reading ui file 'src/resources/_ui/fitdialog.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.2
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
@ -37,7 +38,6 @@ class Ui_FitDialog(object):
self.weight_combobox.addItem("") self.weight_combobox.addItem("")
self.weight_combobox.addItem("") self.weight_combobox.addItem("")
self.weight_combobox.addItem("") self.weight_combobox.addItem("")
self.weight_combobox.addItem("")
self.gridLayout_2.addWidget(self.weight_combobox, 6, 1, 1, 1) self.gridLayout_2.addWidget(self.weight_combobox, 6, 1, 1, 1)
self.newmodel_button = QtWidgets.QPushButton(self.scrollAreaWidgetContents_2) self.newmodel_button = QtWidgets.QPushButton(self.scrollAreaWidgetContents_2)
self.newmodel_button.setEnabled(False) self.newmodel_button.setEnabled(False)
@ -143,7 +143,6 @@ class Ui_FitDialog(object):
self.weight_combobox.setItemText(1, _translate("FitDialog", "y")) self.weight_combobox.setItemText(1, _translate("FitDialog", "y"))
self.weight_combobox.setItemText(2, _translate("FitDialog", "")) self.weight_combobox.setItemText(2, _translate("FitDialog", ""))
self.weight_combobox.setItemText(3, _translate("FitDialog", "Δy")) self.weight_combobox.setItemText(3, _translate("FitDialog", "Δy"))
self.weight_combobox.setItemText(4, _translate("FitDialog", "log(y)"))
self.newmodel_button.setText(_translate("FitDialog", "New model")) self.newmodel_button.setText(_translate("FitDialog", "New model"))
self.deletemodel_button.setText(_translate("FitDialog", "Delete model")) self.deletemodel_button.setText(_translate("FitDialog", "Delete model"))
self.label_3.setText(_translate("FitDialog", "Weight")) self.label_3.setText(_translate("FitDialog", "Weight"))

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/fitmodelwidget.ui' # Form implementation generated from reading ui file 'src/resources/_ui/fitmodelwidget.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.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,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_FitParameter(object): class Ui_FitParameter(object):
def setupUi(self, FitParameter): def setupUi(self, FitParameter):
FitParameter.setObjectName("FitParameter") FitParameter.setObjectName("FitParameter")
FitParameter.resize(365, 78) FitParameter.resize(365, 66)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -36,28 +37,24 @@ class Ui_FitParameter(object):
self.parametername.setObjectName("parametername") self.parametername.setObjectName("parametername")
self.horizontalLayout_2.addWidget(self.parametername) self.horizontalLayout_2.addWidget(self.parametername)
self.parameter_line = LineEdit(FitParameter) self.parameter_line = LineEdit(FitParameter)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.parameter_line.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.parameter_line.sizePolicy().hasHeightForWidth())
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)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem)
self.fixed_check = QtWidgets.QCheckBox(FitParameter) self.fixed_check = QtWidgets.QCheckBox(FitParameter)
self.fixed_check.setObjectName("fixed_check") self.fixed_check.setObjectName("fixed_check")
self.horizontalLayout_2.addWidget(self.fixed_check) self.horizontalLayout_2.addWidget(self.fixed_check)
self.global_checkbox = QtWidgets.QCheckBox(FitParameter) self.global_checkbox = QtWidgets.QCheckBox(FitParameter)
self.global_checkbox.setObjectName("global_checkbox") self.global_checkbox.setObjectName("global_checkbox")
self.horizontalLayout_2.addWidget(self.global_checkbox) self.horizontalLayout_2.addWidget(self.global_checkbox)
self.toolButton = QtWidgets.QToolButton(FitParameter) self.reset_button = QtWidgets.QPushButton(FitParameter)
self.toolButton.setText("") self.reset_button.setObjectName("reset_button")
self.toolButton.setPopupMode(QtWidgets.QToolButton.InstantPopup) self.horizontalLayout_2.addWidget(self.reset_button)
self.toolButton.setArrowType(QtCore.Qt.RightArrow)
self.toolButton.setObjectName("toolButton")
self.horizontalLayout_2.addWidget(self.toolButton)
self.verticalLayout.addLayout(self.horizontalLayout_2) self.verticalLayout.addLayout(self.horizontalLayout_2)
self.frame = QtWidgets.QFrame(FitParameter) self.frame = QtWidgets.QFrame(FitParameter)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
@ -89,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)
@ -107,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")
@ -129,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

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '/autohome/dominik/nmreval-gitea/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.7 # 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.
@ -14,120 +14,9 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object): class Ui_Dialog(object):
def setupUi(self, Dialog): def setupUi(self, Dialog):
Dialog.setObjectName("Dialog") Dialog.setObjectName("Dialog")
Dialog.resize(864, 649) Dialog.resize(969, 974)
self.gridLayout = QtWidgets.QGridLayout(Dialog) self.gridLayout = QtWidgets.QGridLayout(Dialog)
self.gridLayout.setObjectName("gridLayout") self.gridLayout.setObjectName("gridLayout")
self.sets_comboBox = ElideComboBox(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.sets_comboBox.sizePolicy().hasHeightForWidth())
self.sets_comboBox.setSizePolicy(sizePolicy)
self.sets_comboBox.setMaximumSize(QtCore.QSize(400, 16777215))
self.sets_comboBox.setBaseSize(QtCore.QSize(200, 0))
self.sets_comboBox.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToMinimumContentsLength)
self.sets_comboBox.setObjectName("sets_comboBox")
self.gridLayout.addWidget(self.sets_comboBox, 0, 0, 1, 1)
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Retry)
self.buttonBox.setObjectName("buttonBox")
self.gridLayout.addWidget(self.buttonBox, 6, 0, 1, 2)
self.param_tableWidget = QtWidgets.QTableWidget(Dialog)
self.param_tableWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
self.param_tableWidget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustIgnored)
self.param_tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.param_tableWidget.setAlternatingRowColors(True)
self.param_tableWidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
self.param_tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectColumns)
self.param_tableWidget.setShowGrid(False)
self.param_tableWidget.setColumnCount(0)
self.param_tableWidget.setObjectName("param_tableWidget")
self.param_tableWidget.setRowCount(0)
self.param_tableWidget.horizontalHeader().setStretchLastSection(False)
self.gridLayout.addWidget(self.param_tableWidget, 1, 0, 1, 1)
self.groupBox = QtWidgets.QGroupBox(Dialog)
self.groupBox.setObjectName("groupBox")
self.gridLayout_2 = QtWidgets.QGridLayout(self.groupBox)
self.gridLayout_2.setContentsMargins(3, 3, 3, 3)
self.gridLayout_2.setSpacing(3)
self.gridLayout_2.setObjectName("gridLayout_2")
self.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, 5, 0, 1, 2)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setSpacing(3)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.reject_fit_checkBox = QtWidgets.QCheckBox(Dialog)
self.reject_fit_checkBox.setObjectName("reject_fit_checkBox")
self.horizontalLayout_2.addWidget(self.reject_fit_checkBox)
self.del_prev_checkBox = QtWidgets.QCheckBox(Dialog)
self.del_prev_checkBox.setObjectName("del_prev_checkBox")
self.horizontalLayout_2.addWidget(self.del_prev_checkBox)
self.gridLayout.addLayout(self.horizontalLayout_2, 2, 0, 1, 1)
self.line = QtWidgets.QFrame(Dialog)
self.line.setFrameShape(QtWidgets.QFrame.HLine)
self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
self.line.setObjectName("line")
self.gridLayout.addWidget(self.line, 3, 0, 1, 2)
self.stack = QtWidgets.QTabWidget(Dialog) self.stack = QtWidgets.QTabWidget(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
@ -138,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")
@ -194,7 +111,142 @@ class Ui_Dialog(object):
self.corr_tableWidget.verticalHeader().setVisible(False) self.corr_tableWidget.verticalHeader().setVisible(False)
self.verticalLayout_3.addWidget(self.corr_tableWidget) self.verticalLayout_3.addWidget(self.corr_tableWidget)
self.stack.addTab(self.stackPage3, "") self.stack.addTab(self.stackPage3, "")
self.gridLayout.addWidget(self.stack, 0, 1, 3, 1) self.gridLayout.addWidget(self.stack, 0, 1, 5, 1)
self.param_tableWidget = QtWidgets.QTableWidget(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.param_tableWidget.sizePolicy().hasHeightForWidth())
self.param_tableWidget.setSizePolicy(sizePolicy)
self.param_tableWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
self.param_tableWidget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustIgnored)
self.param_tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.param_tableWidget.setAlternatingRowColors(True)
self.param_tableWidget.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
self.param_tableWidget.setColumnCount(1)
self.param_tableWidget.setObjectName("param_tableWidget")
self.param_tableWidget.setRowCount(0)
self.param_tableWidget.horizontalHeader().setVisible(False)
self.param_tableWidget.horizontalHeader().setStretchLastSection(True)
self.gridLayout.addWidget(self.param_tableWidget, 1, 0, 1, 1)
self.del_prev_checkBox = QtWidgets.QCheckBox(Dialog)
self.del_prev_checkBox.setObjectName("del_prev_checkBox")
self.gridLayout.addWidget(self.del_prev_checkBox, 3, 0, 1, 1)
self.line = QtWidgets.QFrame(Dialog)
self.line.setFrameShape(QtWidgets.QFrame.HLine)
self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
self.line.setObjectName("line")
self.gridLayout.addWidget(self.line, 5, 0, 1, 2)
self.sets_comboBox = ElideComboBox(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.sets_comboBox.sizePolicy().hasHeightForWidth())
self.sets_comboBox.setSizePolicy(sizePolicy)
self.sets_comboBox.setMaximumSize(QtCore.QSize(400, 16777215))
self.sets_comboBox.setBaseSize(QtCore.QSize(200, 0))
self.sets_comboBox.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToMinimumContentsLength)
self.sets_comboBox.setObjectName("sets_comboBox")
self.gridLayout.addWidget(self.sets_comboBox, 0, 0, 1, 1)
self.reject_fit_checkBox = QtWidgets.QCheckBox(Dialog)
self.reject_fit_checkBox.setObjectName("reject_fit_checkBox")
self.gridLayout.addWidget(self.reject_fit_checkBox, 2, 0, 1, 1)
self.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,22 +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.groupBox.setTitle(_translate("Dialog", "Output")) self.autoscale_box.setToolTip(_translate("Dialog", "Auto-scale graph for all sets"))
self.extrapolate_box.setToolTip(_translate("Dialog", "Extrapolates only main function")) self.autoscale_box.setText(_translate("Dialog", "Autoscale all sets"))
self.extrapolate_box.setText(_translate("Dialog", "Extrapolate curves")) self.verticalGroupBox_2.setTitle(_translate("Dialog", "Logarithmic axes"))
self.parameter_checkbox.setText(_translate("Dialog", "Plot parameter")) self.logx_box.setText(_translate("Dialog", "x axis"))
self.graph_checkBox.setText(_translate("Dialog", "New graph for parameter")) self.logy_box.setText(_translate("Dialog", "y axis"))
self.minx_line.setToolTip(_translate("Dialog", "Leave empty to start at lowest point")) self.verticalGroupBox.setTitle(_translate("Dialog", "Residuals"))
self.minx_line.setPlaceholderText(_translate("Dialog", "min x")) self.rel_dev_button.setText(_translate("Dialog", "relative deviation"))
self.maxx_line.setToolTip(_translate("Dialog", "Leave empty to start at highest point")) self.abs_dev_button.setText(_translate("Dialog", "absolute deviation"))
self.maxx_line.setPlaceholderText(_translate("Dialog", "max x"))
self.numx_line.setPlaceholderText(_translate("Dialog", "# pts"))
self.curve_checkbox.setText(_translate("Dialog", "Plot fit curve"))
self.partial_checkBox.setText(_translate("Dialog", "Plot partial functions"))
self.reject_fit_checkBox.setText(_translate("Dialog", "Reject this fit"))
self.del_prev_checkBox.setText(_translate("Dialog", "Delete previous fits"))
self.logy_box.setText(_translate("Dialog", "logarithmic y axis"))
self.logx_box.setText(_translate("Dialog", "logarithmic x axis"))
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)
@ -230,5 +274,20 @@ class Ui_Dialog(object):
item = self.corr_tableWidget.horizontalHeaderItem(3) item = self.corr_tableWidget.horizontalHeaderItem(3)
item.setText(_translate("Dialog", "Partial Corr.")) item.setText(_translate("Dialog", "Partial Corr."))
self.stack.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.reject_fit_checkBox.setText(_translate("Dialog", "Reject this fit"))
self.groupBox.setTitle(_translate("Dialog", "Output"))
self.minx_line.setToolTip(_translate("Dialog", "Leave empty to start at lowest point"))
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.setPlaceholderText(_translate("Dialog", "max x"))
self.newx_log_checkbox.setText(_translate("Dialog", "log-spaced?"))
self.curve_checkbox.setText(_translate("Dialog", "Plot fit curve"))
self.partial_checkBox.setText(_translate("Dialog", "Plot partial functions"))
self.parameter_checkbox.setText(_translate("Dialog", "Plot parameter"))
from ..lib.forms import ElideComboBox from ..lib.forms import ElideComboBox
from pyqtgraph import GraphicsLayoutWidget from pyqtgraph import GraphicsLayoutWidget

View File

@ -2,9 +2,10 @@
# Form implementation generated from reading ui file 'resources/_ui/graph.ui' # Form implementation generated from reading ui file 'resources/_ui/graph.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
@ -196,19 +197,20 @@ class Ui_GraphWindow(object):
self.gridLayout.setHorizontalSpacing(3) self.gridLayout.setHorizontalSpacing(3)
self.gridLayout.setVerticalSpacing(0) self.gridLayout.setVerticalSpacing(0)
self.gridLayout.setObjectName("gridLayout") self.gridLayout.setObjectName("gridLayout")
self.listWidget = QtWidgets.QListWidget(GraphWindow) self.listWidget = QListWidgetSelect(GraphWindow)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
self.listWidget.setSizePolicy(sizePolicy) self.listWidget.setSizePolicy(sizePolicy)
self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.listWidget.setObjectName("listWidget") self.listWidget.setObjectName("listWidget")
self.gridLayout.addWidget(self.listWidget, 1, 1, 1, 1) self.gridLayout.addWidget(self.listWidget, 1, 1, 1, 1)
self.checkBox = QtWidgets.QCheckBox(GraphWindow) self.checkBox = QtWidgets.QCheckBox(GraphWindow)
self.checkBox.setChecked(True) self.checkBox.setChecked(True)
self.checkBox.setObjectName("checkBox") self.checkBox.setObjectName("checkBox")
self.gridLayout.addWidget(self.checkBox, 0, 1, 1, 1) self.gridLayout.addWidget(self.checkBox, 0, 1, 1, 1)
self.graphic = PlotWidget(GraphWindow) self.graphic = NMRPlotWidget(GraphWindow)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -269,7 +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 pyqtgraph import PlotWidget from ..lib.graph_items import NMRPlotWidget
from ..lib.listwidget import QListWidgetSelect

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '/autohome/dominik/nmreval/src/resources/_ui/integral_widget.ui' # Form implementation generated from reading ui file 'src/resources/_ui/integral_widget.ui'
# #
# Created by: PyQt5 UI code generator 5.15.4 # Created by: PyQt5 UI code generator 5.15.9
# #
# WARNING: Any manual changes made to this file will be lost when pyuic5 is # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing. # run again. Do not edit this file unless you know what you are doing.
@ -26,7 +26,6 @@ class Ui_Form(object):
self.set_combobox.setObjectName("set_combobox") self.set_combobox.setObjectName("set_combobox")
self.verticalLayout.addWidget(self.set_combobox) self.verticalLayout.addWidget(self.set_combobox)
self.treeWidget = QtWidgets.QTreeWidget(Form) self.treeWidget = QtWidgets.QTreeWidget(Form)
self.treeWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.treeWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) self.treeWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.treeWidget.setHeaderHidden(True) self.treeWidget.setHeaderHidden(True)
self.treeWidget.setObjectName("treeWidget") self.treeWidget.setObjectName("treeWidget")

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/integratederive_dialog.ui' # Form implementation generated from reading ui file 'src/resources/_ui/integratederive_dialog.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.9
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
@ -13,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object): class Ui_Dialog(object):
def setupUi(self, Dialog): def setupUi(self, Dialog):
Dialog.setObjectName("Dialog") Dialog.setObjectName("Dialog")
Dialog.resize(400, 308) Dialog.resize(400, 375)
self.verticalLayout = QtWidgets.QVBoxLayout(Dialog) self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
self.verticalLayout.setObjectName("verticalLayout") self.verticalLayout.setObjectName("verticalLayout")
self.listWidget = QtWidgets.QListWidget(Dialog) self.listWidget = QtWidgets.QListWidget(Dialog)
@ -49,6 +50,9 @@ class Ui_Dialog(object):
self.log_checkbox = QtWidgets.QCheckBox(Dialog) self.log_checkbox = QtWidgets.QCheckBox(Dialog)
self.log_checkbox.setObjectName("log_checkbox") self.log_checkbox.setObjectName("log_checkbox")
self.verticalLayout.addWidget(self.log_checkbox) self.verticalLayout.addWidget(self.log_checkbox)
self.freq_box = QtWidgets.QCheckBox(Dialog)
self.freq_box.setObjectName("freq_box")
self.verticalLayout.addWidget(self.freq_box)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1) self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1)
self.horizontalLayout_2.setSpacing(3) self.horizontalLayout_2.setSpacing(3)
@ -67,8 +71,8 @@ class Ui_Dialog(object):
self.verticalLayout.addWidget(self.buttonBox) self.verticalLayout.addWidget(self.buttonBox)
self.retranslateUi(Dialog) self.retranslateUi(Dialog)
self.buttonBox.accepted.connect(Dialog.accept) self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
self.buttonBox.rejected.connect(Dialog.reject) self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(Dialog) QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog): def retranslateUi(self, Dialog):
@ -80,4 +84,5 @@ class Ui_Dialog(object):
self.ft_comboBox.setItemText(1, _translate("Dialog", "Imag")) self.ft_comboBox.setItemText(1, _translate("Dialog", "Imag"))
self.ft_comboBox.setItemText(2, _translate("Dialog", "Complex")) self.ft_comboBox.setItemText(2, _translate("Dialog", "Complex"))
self.log_checkbox.setText(_translate("Dialog", "use logarithmic x axis")) self.log_checkbox.setText(_translate("Dialog", "use logarithmic x axis"))
self.freq_box.setText(_translate("Dialog", "return x axis as f, not omega"))
self.newgraph_checkbox.setText(_translate("Dialog", "New graph")) self.newgraph_checkbox.setText(_translate("Dialog", "New graph"))

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/interpol_dialog.ui' # Form implementation generated from reading ui file './nmreval/src/resources/_ui/interpol_dialog.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
@ -54,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)
@ -65,12 +66,13 @@ class Ui_Dialog(object):
self.label_2 = QtWidgets.QLabel(Dialog) self.label_2 = QtWidgets.QLabel(Dialog)
self.label_2.setObjectName("label_2") self.label_2.setObjectName("label_2")
self.gridLayout.addWidget(self.label_2, 6, 0, 1, 1) self.gridLayout.addWidget(self.label_2, 6, 0, 1, 1)
self.listWidget = QtWidgets.QListWidget(Dialog) self.listWidget = QListWidgetSelect(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
self.listWidget.setSizePolicy(sizePolicy) self.listWidget.setSizePolicy(sizePolicy)
self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
self.listWidget.setObjectName("listWidget") self.listWidget.setObjectName("listWidget")
self.gridLayout.addWidget(self.listWidget, 1, 0, 1, 2) self.gridLayout.addWidget(self.listWidget, 1, 0, 1, 2)
self.sampling_widget = QtWidgets.QWidget(Dialog) self.sampling_widget = QtWidgets.QWidget(Dialog)
@ -130,8 +132,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)
self.buttonBox.rejected.connect(Dialog.reject)
QtCore.QMetaObject.connectSlotsByName(Dialog) QtCore.QMetaObject.connectSlotsByName(Dialog)
Dialog.setTabOrder(self.listWidget, self.ylog_checkBox) Dialog.setTabOrder(self.listWidget, self.ylog_checkBox)
Dialog.setTabOrder(self.ylog_checkBox, self.interp_comboBox) Dialog.setTabOrder(self.ylog_checkBox, self.interp_comboBox)
@ -164,3 +164,4 @@ class Ui_Dialog(object):
self.xaxis_comboBox.setItemText(1, _translate("Dialog", "from data")) self.xaxis_comboBox.setItemText(1, _translate("Dialog", "from data"))
self.label_8.setText(_translate("Dialog", "Add interpolated data to")) self.label_8.setText(_translate("Dialog", "Add interpolated data to"))
self.xlog_checkBox.setText(_translate("Dialog", "use log(x)")) self.xlog_checkBox.setText(_translate("Dialog", "use log(x)"))
from ..lib.listwidget import QListWidgetSelect

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/phase_corr_dialog.ui' # Form implementation generated from reading ui file 'src/resources/_ui/phase_corr_dialog.ui'
# #
# Created by: PyQt5 UI code generator 5.15.4 # Created by: PyQt5 UI code generator 5.15.9
# #
# WARNING: Any manual changes made to this file will be lost when pyuic5 is # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing. # run again. Do not edit this file unless you know what you are doing.
@ -24,7 +24,7 @@ class Ui_SignalEdit(object):
self.gridLayout.setContentsMargins(6, 6, 6, 6) self.gridLayout.setContentsMargins(6, 6, 6, 6)
self.gridLayout.setSpacing(3) self.gridLayout.setSpacing(3)
self.gridLayout.setObjectName("gridLayout") self.gridLayout.setObjectName("gridLayout")
self.graphicsView = PlotWidget(SignalEdit) self.graphicsView = NMRPlotWidget(SignalEdit)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -83,8 +83,8 @@ class Ui_SignalEdit(object):
self.gridLayout.addItem(spacerItem1, 1, 5, 1, 1) self.gridLayout.addItem(spacerItem1, 1, 5, 1, 1)
self.retranslateUi(SignalEdit) self.retranslateUi(SignalEdit)
self.buttonBox.accepted.connect(SignalEdit.accept) self.buttonBox.accepted.connect(SignalEdit.accept) # type: ignore
self.buttonBox.rejected.connect(SignalEdit.close) self.buttonBox.rejected.connect(SignalEdit.close) # type: ignore
QtCore.QMetaObject.connectSlotsByName(SignalEdit) QtCore.QMetaObject.connectSlotsByName(SignalEdit)
def retranslateUi(self, SignalEdit): def retranslateUi(self, SignalEdit):
@ -94,4 +94,4 @@ class Ui_SignalEdit(object):
self.label_8.setText(_translate("SignalEdit", "Pivot")) self.label_8.setText(_translate("SignalEdit", "Pivot"))
self.label_6.setText(_translate("SignalEdit", "Phase 1")) self.label_6.setText(_translate("SignalEdit", "Phase 1"))
self.label.setText(_translate("SignalEdit", "Phase 0")) self.label.setText(_translate("SignalEdit", "Phase 0"))
from pyqtgraph import PlotWidget from ..lib.graph_items import NMRPlotWidget

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/shift_scale_dialog.ui' # Form implementation generated from reading ui file 'src/resources/_ui/shift_scale_dialog.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.9
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
@ -162,7 +163,7 @@ class Ui_shift_dialog(object):
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.verticalFrame_2) self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.verticalFrame_2)
self.verticalLayout_2.setSpacing(3) self.verticalLayout_2.setSpacing(3)
self.verticalLayout_2.setObjectName("verticalLayout_2") self.verticalLayout_2.setObjectName("verticalLayout_2")
self.graphicsView = PlotWidget(self.verticalFrame_2) self.graphicsView = NMRPlotWidget(self.verticalFrame_2)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -267,8 +268,8 @@ class Ui_shift_dialog(object):
self.retranslateUi(shift_dialog) self.retranslateUi(shift_dialog)
self.tabWidget.setCurrentIndex(0) self.tabWidget.setCurrentIndex(0)
self.buttonBox.accepted.connect(shift_dialog.accept) self.buttonBox.accepted.connect(shift_dialog.accept) # type: ignore
self.buttonBox.rejected.connect(shift_dialog.reject) self.buttonBox.rejected.connect(shift_dialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(shift_dialog) QtCore.QMetaObject.connectSlotsByName(shift_dialog)
shift_dialog.setTabOrder(self.tabWidget, self.shift_table) shift_dialog.setTabOrder(self.tabWidget, self.shift_table)
shift_dialog.setTabOrder(self.shift_table, self.x_shift_spinbox) shift_dialog.setTabOrder(self.shift_table, self.x_shift_spinbox)
@ -310,5 +311,5 @@ class Ui_shift_dialog(object):
self.overwrite_checkbox.setText(_translate("shift_dialog", "Overwrite data")) self.overwrite_checkbox.setText(_translate("shift_dialog", "Overwrite data"))
self.data_newgraph.setText(_translate("shift_dialog", "New graph")) self.data_newgraph.setText(_translate("shift_dialog", "New graph"))
self.values_newgraph.setText(_translate("shift_dialog", "New graph")) self.values_newgraph.setText(_translate("shift_dialog", "New graph"))
from ..lib.graph_items import NMRPlotWidget
from ..lib.spinboxes import SciSpinBox from ..lib.spinboxes import SciSpinBox
from pyqtgraph import PlotWidget

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

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/t1dialog.ui' # Form implementation generated from reading ui file 'src/resources/_ui/t1dialog.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.9
# #
# WARNING! All changes made in this file will be lost! # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
@ -160,6 +161,7 @@ class Ui_t1dialog(object):
self.tau_combox.addItem("") self.tau_combox.addItem("")
self.gridLayout_4.addWidget(self.tau_combox, 1, 0, 1, 2) self.gridLayout_4.addWidget(self.tau_combox, 1, 0, 1, 2)
self.checkBox_interpol = QtWidgets.QCheckBox(self.groupBox_3) self.checkBox_interpol = QtWidgets.QCheckBox(self.groupBox_3)
self.checkBox_interpol.setEnabled(False)
self.checkBox_interpol.setObjectName("checkBox_interpol") self.checkBox_interpol.setObjectName("checkBox_interpol")
self.gridLayout_4.addWidget(self.checkBox_interpol, 2, 0, 1, 2) self.gridLayout_4.addWidget(self.checkBox_interpol, 2, 0, 1, 2)
self.graph_checkbox = QtWidgets.QCheckBox(self.groupBox_3) self.graph_checkbox = QtWidgets.QCheckBox(self.groupBox_3)

View File

@ -0,0 +1,253 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'src/resources/_ui/tnmh_dialog.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_DSCEvalDialog(object):
def setupUi(self, DSCEvalDialog):
DSCEvalDialog.setObjectName("DSCEvalDialog")
DSCEvalDialog.resize(996, 712)
self.gridLayout = QtWidgets.QGridLayout(DSCEvalDialog)
self.gridLayout.setObjectName("gridLayout")
self.listWidget = QListWidgetSelect(DSCEvalDialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth())
self.listWidget.setSizePolicy(sizePolicy)
self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.listWidget.setObjectName("listWidget")
self.gridLayout.addWidget(self.listWidget, 0, 0, 1, 1)
self.stackedWidget = QtWidgets.QStackedWidget(DSCEvalDialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.stackedWidget.sizePolicy().hasHeightForWidth())
self.stackedWidget.setSizePolicy(sizePolicy)
self.stackedWidget.setFrameShape(QtWidgets.QFrame.Box)
self.stackedWidget.setObjectName("stackedWidget")
self.page = QtWidgets.QWidget()
self.page.setObjectName("page")
self.gridLayout_2 = QtWidgets.QGridLayout(self.page)
self.gridLayout_2.setObjectName("gridLayout_2")
self.gridLayout_9 = QtWidgets.QGridLayout()
self.gridLayout_9.setObjectName("gridLayout_9")
self.tg_export_check = QtWidgets.QCheckBox(self.page)
self.tg_export_check.setChecked(True)
self.tg_export_check.setObjectName("tg_export_check")
self.gridLayout_9.addWidget(self.tg_export_check, 0, 0, 1, 1)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout_9.addItem(spacerItem, 3, 0, 1, 1)
self.tglines_export_check = QtWidgets.QCheckBox(self.page)
self.tglines_export_check.setChecked(True)
self.tglines_export_check.setObjectName("tglines_export_check")
self.gridLayout_9.addWidget(self.tglines_export_check, 0, 1, 1, 1)
self.tg_export_button = QtWidgets.QPushButton(self.page)
self.tg_export_button.setObjectName("tg_export_button")
self.gridLayout_9.addWidget(self.tg_export_button, 2, 0, 1, 2)
self.gridLayout_2.addLayout(self.gridLayout_9, 2, 1, 1, 1)
self.tg_tree = QtWidgets.QTreeWidget(self.page)
self.tg_tree.setObjectName("tg_tree")
self.tg_tree.headerItem().setText(0, "1")
self.tg_tree.header().setVisible(False)
self.gridLayout_2.addWidget(self.tg_tree, 2, 0, 1, 1)
self.dsc_plot = NMRPlotWidget(self.page)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.dsc_plot.sizePolicy().hasHeightForWidth())
self.dsc_plot.setSizePolicy(sizePolicy)
self.dsc_plot.setObjectName("dsc_plot")
self.gridLayout_2.addWidget(self.dsc_plot, 1, 0, 1, 2)
self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.label = QtWidgets.QLabel(self.page)
self.label.setObjectName("label")
self.horizontalLayout_4.addWidget(self.label)
self.calctg_button = QtWidgets.QPushButton(self.page)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.calctg_button.sizePolicy().hasHeightForWidth())
self.calctg_button.setSizePolicy(sizePolicy)
self.calctg_button.setObjectName("calctg_button")
self.horizontalLayout_4.addWidget(self.calctg_button)
self.gridLayout_2.addLayout(self.horizontalLayout_4, 0, 0, 1, 2)
self.stackedWidget.addWidget(self.page)
self.page_2 = QtWidgets.QWidget()
self.page_2.setObjectName("page_2")
self.gridLayout_3 = QtWidgets.QGridLayout(self.page_2)
self.gridLayout_3.setObjectName("gridLayout_3")
self.label_4 = QtWidgets.QLabel(self.page_2)
self.label_4.setObjectName("label_4")
self.gridLayout_3.addWidget(self.label_4, 2, 0, 1, 1)
self.tghodge_graph = NMRPlotWidget(self.page_2)
self.tghodge_graph.setObjectName("tghodge_graph")
self.gridLayout_3.addWidget(self.tghodge_graph, 1, 0, 1, 1)
self.tau_plot = NMRPlotWidget(self.page_2)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.tau_plot.sizePolicy().hasHeightForWidth())
self.tau_plot.setSizePolicy(sizePolicy)
self.tau_plot.setObjectName("tau_plot")
self.gridLayout_3.addWidget(self.tau_plot, 1, 1, 1, 1)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.label_2 = QtWidgets.QLabel(self.page_2)
self.label_2.setObjectName("label_2")
self.horizontalLayout_3.addWidget(self.label_2)
self.hodge_button = QtWidgets.QPushButton(self.page_2)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.hodge_button.sizePolicy().hasHeightForWidth())
self.hodge_button.setSizePolicy(sizePolicy)
self.hodge_button.setObjectName("hodge_button")
self.horizontalLayout_3.addWidget(self.hodge_button)
self.gridLayout_3.addLayout(self.horizontalLayout_3, 0, 0, 1, 2)
self.export_hodge_button = QtWidgets.QPushButton(self.page_2)
self.export_hodge_button.setObjectName("export_hodge_button")
self.gridLayout_3.addWidget(self.export_hodge_button, 5, 1, 1, 1)
self.hodge_graph_combo = QtWidgets.QComboBox(self.page_2)
self.hodge_graph_combo.setEnabled(False)
self.hodge_graph_combo.setObjectName("hodge_graph_combo")
self.gridLayout_3.addWidget(self.hodge_graph_combo, 4, 1, 1, 1)
self.hodge_graph_check = QtWidgets.QCheckBox(self.page_2)
self.hodge_graph_check.setChecked(True)
self.hodge_graph_check.setObjectName("hodge_graph_check")
self.gridLayout_3.addWidget(self.hodge_graph_check, 4, 0, 1, 1)
self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
self.horizontalLayout_5.setObjectName("horizontalLayout_5")
self.onset_check = QtWidgets.QCheckBox(self.page_2)
self.onset_check.setChecked(True)
self.onset_check.setObjectName("onset_check")
self.horizontalLayout_5.addWidget(self.onset_check)
self.mid_check = QtWidgets.QCheckBox(self.page_2)
self.mid_check.setChecked(True)
self.mid_check.setObjectName("mid_check")
self.horizontalLayout_5.addWidget(self.mid_check)
self.end_check = QtWidgets.QCheckBox(self.page_2)
self.end_check.setChecked(True)
self.end_check.setObjectName("end_check")
self.horizontalLayout_5.addWidget(self.end_check)
self.inflection_check = QtWidgets.QCheckBox(self.page_2)
self.inflection_check.setChecked(True)
self.inflection_check.setObjectName("inflection_check")
self.horizontalLayout_5.addWidget(self.inflection_check)
self.fictive_check = QtWidgets.QCheckBox(self.page_2)
self.fictive_check.setChecked(True)
self.fictive_check.setObjectName("fictive_check")
self.horizontalLayout_5.addWidget(self.fictive_check)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_5.addItem(spacerItem1)
self.gridLayout_3.addLayout(self.horizontalLayout_5, 3, 0, 1, 2)
self.stackedWidget.addWidget(self.page_2)
self.page_3 = QtWidgets.QWidget()
self.page_3.setObjectName("page_3")
self.gridLayout_6 = QtWidgets.QGridLayout(self.page_3)
self.gridLayout_6.setObjectName("gridLayout_6")
self.tnmh_graphics = NMRPlotWidget(self.page_3)
self.tnmh_graphics.setObjectName("tnmh_graphics")
self.gridLayout_6.addWidget(self.tnmh_graphics, 1, 0, 1, 2)
self.tnmh_tree = QtWidgets.QTreeWidget(self.page_3)
self.tnmh_tree.setObjectName("tnmh_tree")
self.tnmh_tree.headerItem().setText(0, "1")
self.tnmh_tree.header().setVisible(False)
self.gridLayout_6.addWidget(self.tnmh_tree, 2, 0, 1, 1)
self.gridLayout_5 = QtWidgets.QGridLayout()
self.gridLayout_5.setObjectName("gridLayout_5")
self.tnmh_graph_check = QtWidgets.QCheckBox(self.page_3)
self.tnmh_graph_check.setChecked(True)
self.tnmh_graph_check.setObjectName("tnmh_graph_check")
self.gridLayout_5.addWidget(self.tnmh_graph_check, 1, 0, 1, 1)
self.tnmhfit_export_check = QtWidgets.QCheckBox(self.page_3)
self.tnmhfit_export_check.setChecked(True)
self.tnmhfit_export_check.setObjectName("tnmhfit_export_check")
self.gridLayout_5.addWidget(self.tnmhfit_export_check, 0, 0, 1, 1)
self.tnmh_export_button = QtWidgets.QPushButton(self.page_3)
self.tnmh_export_button.setObjectName("tnmh_export_button")
self.gridLayout_5.addWidget(self.tnmh_export_button, 2, 0, 1, 2)
self.fictive_export_check = QtWidgets.QCheckBox(self.page_3)
self.fictive_export_check.setChecked(True)
self.fictive_export_check.setObjectName("fictive_export_check")
self.gridLayout_5.addWidget(self.fictive_export_check, 0, 1, 1, 1)
self.tnmh_graph_combo = QtWidgets.QComboBox(self.page_3)
self.tnmh_graph_combo.setEnabled(False)
self.tnmh_graph_combo.setObjectName("tnmh_graph_combo")
self.gridLayout_5.addWidget(self.tnmh_graph_combo, 1, 1, 1, 1)
spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout_5.addItem(spacerItem2, 3, 0, 1, 1)
self.gridLayout_6.addLayout(self.gridLayout_5, 2, 1, 1, 1)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.label_3 = QtWidgets.QLabel(self.page_3)
self.label_3.setObjectName("label_3")
self.horizontalLayout_2.addWidget(self.label_3)
self.tnhm_fitbutton = QtWidgets.QPushButton(self.page_3)
self.tnhm_fitbutton.setObjectName("tnhm_fitbutton")
self.horizontalLayout_2.addWidget(self.tnhm_fitbutton)
self.gridLayout_6.addLayout(self.horizontalLayout_2, 0, 0, 1, 2)
self.stackedWidget.addWidget(self.page_3)
self.gridLayout.addWidget(self.stackedWidget, 0, 1, 1, 1)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem3)
self.back_button = QtWidgets.QToolButton(DSCEvalDialog)
self.back_button.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
self.back_button.setArrowType(QtCore.Qt.LeftArrow)
self.back_button.setObjectName("back_button")
self.horizontalLayout.addWidget(self.back_button)
self.next_button = QtWidgets.QToolButton(DSCEvalDialog)
self.next_button.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
self.next_button.setArrowType(QtCore.Qt.RightArrow)
self.next_button.setObjectName("next_button")
self.horizontalLayout.addWidget(self.next_button)
self.close_button = QtWidgets.QPushButton(DSCEvalDialog)
self.close_button.setObjectName("close_button")
self.horizontalLayout.addWidget(self.close_button)
self.gridLayout.addLayout(self.horizontalLayout, 1, 0, 1, 2)
self.retranslateUi(DSCEvalDialog)
self.stackedWidget.setCurrentIndex(0)
self.close_button.clicked.connect(DSCEvalDialog.close) # type: ignore
QtCore.QMetaObject.connectSlotsByName(DSCEvalDialog)
def retranslateUi(self, DSCEvalDialog):
_translate = QtCore.QCoreApplication.translate
DSCEvalDialog.setWindowTitle(_translate("DSCEvalDialog", "To boldly go where no man has gone before"))
self.tg_export_check.setText(_translate("DSCEvalDialog", "Export Tg"))
self.tglines_export_check.setText(_translate("DSCEvalDialog", "Export lines"))
self.tg_export_button.setText(_translate("DSCEvalDialog", "Export"))
self.label.setText(_translate("DSCEvalDialog", "<html><head/><body><p><span style=\" font-weight:600;\">Glass transition</span></p></body></html>"))
self.calctg_button.setText(_translate("DSCEvalDialog", "Calculate Tg"))
self.label_4.setText(_translate("DSCEvalDialog", "Export tau:"))
self.label_2.setText(_translate("DSCEvalDialog", "<html><head/><body><p><span style=\" font-weight:600;\">Hodge tau</span></p></body></html>"))
self.hodge_button.setText(_translate("DSCEvalDialog", "Make it so."))
self.export_hodge_button.setText(_translate("DSCEvalDialog", "Export"))
self.hodge_graph_check.setText(_translate("DSCEvalDialog", "New graph"))
self.onset_check.setText(_translate("DSCEvalDialog", "Onset"))
self.mid_check.setText(_translate("DSCEvalDialog", "Midpoint"))
self.end_check.setText(_translate("DSCEvalDialog", "End"))
self.inflection_check.setText(_translate("DSCEvalDialog", "Inflection"))
self.fictive_check.setText(_translate("DSCEvalDialog", "Fictive"))
self.tnmh_graph_check.setText(_translate("DSCEvalDialog", "New graph"))
self.tnmhfit_export_check.setText(_translate("DSCEvalDialog", "Export fit"))
self.tnmh_export_button.setText(_translate("DSCEvalDialog", "Export"))
self.fictive_export_check.setText(_translate("DSCEvalDialog", "Export dTf / dT"))
self.label_3.setText(_translate("DSCEvalDialog", "<html><head/><body><p><span style=\" font-weight:600;\">dTf/dT and TNMH </span></p></body></html>"))
self.tnhm_fitbutton.setText(_translate("DSCEvalDialog", "Engage!"))
self.back_button.setText(_translate("DSCEvalDialog", "Back"))
self.next_button.setText(_translate("DSCEvalDialog", "Next"))
self.close_button.setText(_translate("DSCEvalDialog", "Close"))
from ..lib.graph_items import NMRPlotWidget
from ..lib.listwidget import QListWidgetSelect

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,8 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/valueeditor.ui' # Form implementation generated from reading ui file 'src/resources/_ui/valueeditor.ui'
# #
# Created by: PyQt5 UI code generator 5.15.4 # Created by: PyQt5 UI code generator 5.15.9
# #
# WARNING: Any manual changes made to this file will be lost when pyuic5 is # WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing. # run again. Do not edit this file unless you know what you are doing.

View File

@ -8,8 +8,10 @@ from pyqtgraph import mkPen
from nmreval.data.points import Points from nmreval.data.points import Points
from nmreval.data.signals import Signal from nmreval.data.signals import Signal
from nmreval.lib.logger import logger
from nmreval.utils.text import convert from nmreval.utils.text import convert
from nmreval.data.bds import BDS from nmreval.data.bds import BDS
from nmreval.data.dsc import DSC
from nmreval.lib.colors import BaseColor, TUColors from nmreval.lib.colors import BaseColor, TUColors
from nmreval.lib.lines import LineStyle from nmreval.lib.lines import LineStyle
from nmreval.lib.symbols import SymbolStyle, symbolcycle from nmreval.lib.symbols import SymbolStyle, symbolcycle
@ -32,6 +34,7 @@ class ExperimentContainer(QtCore.QObject):
self.id = str(identifier) self.id = str(identifier)
self._fits = [] self._fits = []
self._relations = kwargs.get('relations', {})
self._data = data self._data = data
self._manager = kwargs.get('manager') self._manager = kwargs.get('manager')
self.graph = '' self.graph = ''
@ -44,6 +47,7 @@ class ExperimentContainer(QtCore.QObject):
self.actions = {} self.actions = {}
self._update_actions() self._update_actions()
@plot_update
def _init_plot(self): def _init_plot(self):
raise NotImplementedError raise NotImplementedError
@ -226,6 +230,8 @@ class ExperimentContainer(QtCore.QObject):
return self.plot_real, self.plot_imag, self.plot_error return self.plot_real, self.plot_imag, self.plot_error
def get_state(self): def get_state(self):
# TODO preserve relationships
ret_dic = { ret_dic = {
'id': self.id, 'id': self.id,
'data': self._data.get_state(), 'data': self._data.get_state(),
@ -267,11 +273,39 @@ class ExperimentContainer(QtCore.QObject):
else: else:
self._fits.extend(value) self._fits.extend(value)
def has_relation(self, relation_type):
return relation_type in self._relations
def get_relation(self, relation_type: int):
return self._relations.get(relation_type, [])
def add_relation(self, relation_type: int, value: str):
if relation_type not in self._relations:
self._relations[relation_type] = []
self._relations[relation_type].append(value)
def remove_relation(self, relation_type: int, value: str):
if relation_type not in self._relations:
raise ValueError(f'Relationship {relation_type!r} with id {value!r} doe not exist for {self.name}')
related_id_value = self._relations[relation_type]
idx = related_id_value.index(value)
if idx == -1:
raise ValueError(f'Relationship {relation_type!r} with id {value!r} doe not exist for {self.name}')
related_id_value.pop(idx)
if len(related_id_value) == 0:
self._relations.pop(relation_type)
def _update_actions(self): def _update_actions(self):
self.actions.update({'sort': self._data.sort, self.actions.update({
'sort': self._data.sort,
'cut': self._data.cut, 'cut': self._data.cut,
'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):
@ -279,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()
@ -312,12 +348,10 @@ class ExperimentContainer(QtCore.QObject):
err_pen.setColor(QtGui.QColor(*self.plot_real.symbolcolor.rgb())) err_pen.setColor(QtGui.QColor(*self.plot_real.symbolcolor.rgb()))
self.plot_error.setData(pen=err_pen) self.plot_error.setData(pen=err_pen)
elif mode == 'imag' and self.plot_imag is not None: if mode in ['imag', 'all'] and self.plot_imag is not None:
self.plot_imag.set_color(color, symbol=symbol, line=line) self.plot_imag.set_color(color, symbol=symbol, line=line)
else:
print('Updating color failed for ' + str(self.id))
def setSymbol(self, symbol=None, color=None, size=None, mode='real'): def setSymbol(self, *, symbol=None, color=None, size=None, mode='real'):
if mode in ['real', 'all']: if mode in ['real', 'all']:
self.plot_real.set_symbol(symbol=symbol, size=size, color=color) self.plot_real.set_symbol(symbol=symbol, size=size, color=color)
if color is not None and self.plot_error is not None and self.plot_real.symbol != SymbolStyle.No: if color is not None and self.plot_error is not None and self.plot_real.symbol != SymbolStyle.No:
@ -327,9 +361,9 @@ class ExperimentContainer(QtCore.QObject):
elif mode in ['imag', 'all'] and self.plot_imag is not None: elif mode in ['imag', 'all'] and self.plot_imag is not None:
self.plot_imag.set_symbol(symbol=symbol, size=size, color=color) self.plot_imag.set_symbol(symbol=symbol, size=size, color=color)
else: else:
print('Updating symbol failed for ' + str(self.id)) logger.warning(f'Updating symbol failed for {self.id}')
def setLine(self, width=None, style=None, color=None, mode='real'): def setLine(self, *, width=None, style=None, color=None, mode='real'):
if mode in ['real', 'all']: if mode in ['real', 'all']:
self.plot_real.set_line(width=width, style=style, color=color) self.plot_real.set_line(width=width, style=style, color=color)
if color is not None and self.plot_error is not None and self.plot_real.symbol == SymbolStyle.No: if color is not None and self.plot_error is not None and self.plot_real.symbol == SymbolStyle.No:
@ -339,7 +373,7 @@ class ExperimentContainer(QtCore.QObject):
elif mode in ['imag', 'all'] and self.plot_imag is not None: elif mode in ['imag', 'all'] and self.plot_imag is not None:
self.plot_imag.set_line(width=width, style=style, color=color) self.plot_imag.set_line(width=width, style=style, color=color)
else: else:
print('Updating line failed for ' + str(self.id)) logger.warning(f'Updating line failed for {self.id}')
def update_property(self, key1: str, key2: str, value: Any): def update_property(self, key1: str, key2: str, value: Any):
keykey = key2.split() keykey = key2.split()
@ -434,16 +468,28 @@ class ExperimentContainer(QtCore.QObject):
return offset return offset
@plot_update
def shift_scale(self, shift_factor: tuple[float, float], scaling_factor: tuple[float, float]):
scale_x, scale_y = scaling_factor
shift_x, shift_y = shift_factor
self.data.x = self.data.x * scale_x + shift_x
self.data.y = self.data.y * scale_y + shift_y
self.data.y_err = self.data.y_err * scale_y
self.update({'shift': scaling_factor, 'scale': shift_factor})
def get_namespace(self, i: int = None, j: int = None) -> dict: def get_namespace(self, i: int = None, j: int = None) -> dict:
if (i is None) and (j is None): if (i is None) and (j is None):
prefix = '' prefix = ''
else: else:
prefix = 'g[%i].s[%i].' % (i, j) prefix = f'g[{i}].s[{j}].'
namespace = {prefix + 'x': (self.x, 'x values'), namespace = {
prefix + 'x': (self.x, 'x values'),
prefix + 'y': [self.y, 'y values'], prefix + 'y': [self.y, 'y values'],
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({
@ -459,27 +505,50 @@ class ExperimentContainer(QtCore.QObject):
return namespace return namespace
def eval_expression(self, cmds, namespace): def eval_expression(self, cmds, namespace, i=None, j=None):
namespace.update({'x': self.x, 'y': self.y, 'y_err': self.y_err, 'value': self.value}) if i is not None:
namespace['i'] = i
if len(self._fits) == 1: if j is not None:
namespace.update({"fit['%s']" % (convert(pname, old='tex', new='str')): pvalue.value namespace['j'] = j
for (pname, pvalue) in self._manager[self._fits[0]].parameter.items()})
namespace.update({'x': self._data.x, 'y': self._data.y, 'y_err': self._data.y_err, 'value': self.value})
namespace['fit'] = {}
if isinstance(self, FitContainer):
namespace['fit'].update({
'%s' % convert(pname, old='latex', new='plain'): pvalue.value
for (pname, pvalue) in self._data.parameter.items()
})
elif len(self._fits) == 1:
namespace['fit'].update({
'%s' % convert(pname, old='tex', new='str'): pvalue.value
for (pname, pvalue) in self._manager[self._fits[0]].parameter.items()
})
else: else:
for k, f in enumerate(self._fits): for k, f in enumerate(self._fits):
namespace.update({"fit['%s_%i']" % (convert(pname, old='tex', new='str'), k): pvalue.value namespace['fit'].update({
for (pname, pvalue) in self._manager[f].parameter.items()}) "%s_%i" % (convert(pname, old='tex', new='str'), k): pvalue.value
for (pname, pvalue) in self._manager[f].parameter.items()
})
new_data = self.copy() new_data = self.copy()
for c in cmds: for c in cmds:
if c: if c:
exec(c, globals(), namespace) exec(c, globals(), namespace)
new_data.set_data(x=namespace['x'], y=namespace['y'], y_err=namespace['y_err']) new_data.set_data(x=namespace['x'], y=namespace['y'], y_err=namespace['y_err'], replace_mask=False)
new_data.value = namespace['value'] new_data.value = namespace['value']
return new_data return new_data
def binning(self, digits: float):
new_data = self.copy(full=True)
new_data.data = self._data.binning(value=digits)
return new_data
class PointContainer(ExperimentContainer): class PointContainer(ExperimentContainer):
symbols = symbolcycle() symbols = symbolcycle()
@ -490,6 +559,9 @@ class PointContainer(ExperimentContainer):
self.mode = 'pts' self.mode = 'pts'
self._init_plot(**kwargs) self._init_plot(**kwargs)
if isinstance(self._data, DSC):
self.mode = 'dsc'
def _init_plot(self, **kwargs): def _init_plot(self, **kwargs):
self.plot_imag = None self.plot_imag = None
@ -526,17 +598,17 @@ class PointContainer(ExperimentContainer):
line_kwargs['style'] = LineStyle.No line_kwargs['style'] = LineStyle.No
sym_kwargs['symbol'] = next(PointContainer.symbols) sym_kwargs['symbol'] = next(PointContainer.symbols)
self.plot_real = PlotItem(x=self._data.x, y=self._data.y, name=self.name, self.plot_real = PlotItem(x=self.x, y=self.y, name=self.name,
symbol=None, pen=None, connect='finite') symbol=None, pen=None, connect='finite')
self.setSymbol(mode='real', **sym_kwargs) self.setSymbol(mode='real', **sym_kwargs)
self.setLine(mode='real', **line_kwargs) self.setLine(mode='real', **line_kwargs)
if sym_kwargs['symbol'] != SymbolStyle.No: if sym_kwargs['symbol'] != SymbolStyle.No:
self.plot_error = ErrorBars(x=self._data.x, y=self._data.y, top=self._data.y_err, bottom=self._data.y_err, self.plot_error = ErrorBars(x=self.x, y=self.y, top=self.y_err, bottom=self.y_err,
pen=mkPen({'color': self.plot_real.symbolcolor.rgb()})) pen=mkPen({'color': self.plot_real.symbolcolor.rgb()}))
else: else:
self.plot_error = ErrorBars(x=self._data.x, y=self._data.y, top=self._data.y_err, bottom=self._data.y_err, self.plot_error = ErrorBars(x=self.x, y=self.y, top=self.y_err, bottom=self.y_err,
pen=mkPen({'color': self.plot_real.linecolor.rgb()})) pen=mkPen({'color': self.plot_real.linecolor.rgb()}))
@ -559,12 +631,12 @@ class FitContainer(ExperimentContainer):
if isinstance(color, BaseColor): if isinstance(color, BaseColor):
color = color.rgb() color = color.rgb()
self.plot_real = PlotItem(x=self._data.x, y=self._data.y.real, name=self.name, self.plot_real = PlotItem(x=self.x, y=self.y.real, name=self.name,
pen=mkPen({'color': color}), pen=mkPen({'color': color}),
connect='finite', symbol=None) connect='finite', symbol=None)
if np.iscomplexobj(self._data.y): if np.iscomplexobj(self._data.y):
self.plot_imag = PlotItem(x=self._data.x, y=self._data.y.imag, name=self.name, self.plot_imag = PlotItem(x=self.x, y=self.y.imag, name=self.name,
pen=mkPen({'color': color}), pen=mkPen({'color': color}),
connect='finite', symbol=None) connect='finite', symbol=None)
@ -597,9 +669,9 @@ class SignalContainer(ExperimentContainer):
self._init_plot(symbol=symbol, **kwargs) self._init_plot(symbol=symbol, **kwargs)
def _init_plot(self, **kwargs): def _init_plot(self, **kwargs):
self.plot_real = PlotItem(x=self._data.x, y=self._data.y.real, name=self.name, self.plot_real = PlotItem(x=self.x, y=self.y.real, name=self.name,
symbol=None, pen=None, connect='finite') symbol=None, pen=None, connect='finite')
self.plot_imag = PlotItem(x=self._data.x, y=self._data.y.imag, name=self.name, self.plot_imag = PlotItem(x=self.x, y=self.y.imag, name=self.name,
symbol=None, pen=None, connect='finite') symbol=None, pen=None, connect='finite')
color = kwargs.get('color', None) color = kwargs.get('color', None)
@ -683,3 +755,46 @@ class SignalContainer(ExperimentContainer):
self._update_actions() self._update_actions()
return self return self
@plot_update
def edit_signal(
self: SignalContainer,
baseline: tuple[None | bool],
leftshift: tuple[None | float, str],
zerofill: tuple[None, int],
apod: tuple[None, list[float], type[object]],
phase: tuple[None | float, float, float],
fourier: tuple[None | bool]
):
"""
Function for EditUndoCommand to call if a timesignal or spectra must be worked on.
This avoids to update the plot for every action we do and makes it slightly faster.
"""
if baseline[0] is not None:
self._data.baseline()
if leftshift[0] is not None:
self._data.shift(*leftshift)
if zerofill[0] is not None:
self._data.zerofill(*zerofill)
if apod[0] is not None:
self._data.apod(*apod)
# ft with three options: None, True, False
if fourier[0] is None:
# ft None -> only phase correct
if phase[0] is not None:
self._data.manual_phase(*phase)
elif fourier[0] == True:
# ft True -> first phase correct then fft
if phase[0] is not None:
self._data.manual_phase(*phase)
self.fourier()
else:
# ft False -> first fft then phase correct
self.fourier()
if phase[0] is not None:
self._data.manual_phase(*phase)

View File

@ -5,7 +5,7 @@ from nmreval.lib.colors import available_cycles
from .properties import PropWidget from .properties import PropWidget
from ...Qt import QtWidgets, QtGui, QtCore from ...Qt import QtWidgets, QtGui, QtCore
from ..._py.datawidget import Ui_DataWidget from ..._py.datawidget import Ui_DataWidget
from ...lib import make_action_icons from ...lib.iconloading import make_action_icons
from ...lib.delegates import HeaderDelegate from ...lib.delegates import HeaderDelegate
@ -17,8 +17,9 @@ class DataTree(QtWidgets.QTreeWidget):
moveItem = QtCore.pyqtSignal(list, str, str, int) # items, from, to, new row moveItem = QtCore.pyqtSignal(list, str, str, int) # items, from, to, new row
copyItem = QtCore.pyqtSignal(list, str) copyItem = QtCore.pyqtSignal(list, str)
saveFits = QtCore.pyqtSignal(list) saveFits = QtCore.pyqtSignal(list)
extendFits = QtCore.pyqtSignal(list) extendFits = QtCore.pyqtSignal(list, bool)
# noinspection PyUnresolvedReferences
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent=parent) super().__init__(parent=parent)
@ -48,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)
@ -60,31 +66,37 @@ class DataTree(QtWidgets.QTreeWidget):
self.update_indexes() self.update_indexes()
def add_item(self, items: (tuple | list[tuple]), gid: str): def add_item(self, items: (tuple | list[tuple]), gid: str, update: bool = True):
if isinstance(items, tuple): if isinstance(items, tuple):
items = [items] items = [items]
for row in range(self.invisibleRootItem().childCount()): for row in range(self.invisibleRootItem().childCount()):
graph = self.invisibleRootItem().child(row) graph = self.invisibleRootItem().child(row)
if graph.data(0, QtCore.Qt.UserRole) == gid: if graph.data(0, QtCore.Qt.ItemDataRole.UserRole) == gid:
for (idd, name) in items: for (idd, name, value) in items:
item = QtWidgets.QTreeWidgetItem([name]) item = QtWidgets.QTreeWidgetItem([name])
item.setData(0, QtCore.Qt.UserRole, idd) item.setToolTip(0, f'Value: {value}')
item.setCheckState(0, QtCore.Qt.Checked) item.setData(0, QtCore.Qt.ItemDataRole.UserRole, idd)
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsEditable | item.setCheckState(0, QtCore.Qt.CheckState.Checked)
QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable) item.setFlags(
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)
self.resizeColumnToContents(0) self.resizeColumnToContents(0)
break break
if update:
self.update_indexes() self.update_indexes()
@QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem) @QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem)
def data_change(self, item: QtWidgets.QTreeWidgetItem) -> tuple[set, set]: def data_change(self, item: QtWidgets.QTreeWidgetItem, emit: bool = True) -> tuple[set, set]:
idd = item.data(0, QtCore.Qt.UserRole) idd = item.data(0, QtCore.Qt.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()
@ -102,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
@ -113,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)
@ -140,9 +152,10 @@ class DataTree(QtWidgets.QTreeWidget):
pass pass
else: else:
if emit:
self.keyChanged.emit(idd, item.text(0)) self.keyChanged.emit(idd, item.text(0))
if to_be_shown or to_be_hidden: if (to_be_shown or to_be_hidden) and emit:
self.stateChanged.emit(list(to_be_shown), list(to_be_hidden)) self.stateChanged.emit(list(to_be_shown), list(to_be_hidden))
return to_be_shown, to_be_hidden return to_be_shown, to_be_hidden
@ -150,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())
@ -176,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:
@ -188,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()
@ -204,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
@ -228,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])]
@ -240,18 +253,18 @@ 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()
self.blockSignals(False) self.blockSignals(False)
def update_indexes(self): def update_indexes(self):
graph_cnt = -1 graph_cnt = -1
set_cnt = 0 set_cnt = 0
iterator = QtWidgets.QTreeWidgetItemIterator(self) iterator = QtWidgets.QTreeWidgetItemIterator(self)
self.blockSignals(True)
while iterator.value(): while iterator.value():
item = iterator.value() item = iterator.value()
if item is not None: if item is not None:
@ -266,13 +279,14 @@ class DataTree(QtWidgets.QTreeWidget):
iterator += 1 iterator += 1
self.resizeColumnToContents(1) self.resizeColumnToContents(1)
self.blockSignals(False)
def set_name(self, sid, name): def set_name(self, sid, name):
iterator = QtWidgets.QTreeWidgetItemIterator(self) iterator = QtWidgets.QTreeWidgetItemIterator(self)
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)
@ -281,24 +295,31 @@ 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():
if idx.column() == 1: if idx.column() == 1:
continue continue
item = self.itemFromIndex(idx) item = self.itemFromIndex(idx)
if item.parent() is None: if item.parent() is None:
for c_i in range(item.childCount()): for c_i in range(item.childCount()):
rm_sets.append(item.child(c_i).data(0, QtCore.Qt.UserRole)) # add sets inside graph to removal
rm_graphs.append(item.data(0, QtCore.Qt.UserRole)) child_data = item.child(c_i).data(0, QtCore.Qt.ItemDataRole.UserRole)
if child_data not in rm_sets:
rm_sets.append(child_data)
rm_graphs.append(item.data(0, QtCore.Qt.ItemDataRole.UserRole))
else: else:
rm_sets.append(item.data(0, QtCore.Qt.UserRole)) item_data = item.data(0, QtCore.Qt.ItemDataRole.UserRole)
if item_data not in rm_sets:
rm_sets.append(item_data)
# self.deleteItem.emit(rm_sets+rm_graphs) # self.deleteItem.emit(rm_sets+rm_graphs)
self.management.delete_sets(rm_sets+rm_graphs) self.management.delete_sets(rm_sets+rm_graphs)
elif evt.key() == QtCore.Qt.Key_Space: elif evt.key() == QtCore.Qt.Key.Key_Space:
sets = [] sets = []
from_parent = [] from_parent = []
@ -306,26 +327,24 @@ class DataTree(QtWidgets.QTreeWidget):
if idx.column() != 0: if idx.column() != 0:
continue continue
item = self.itemFromIndex(idx) item = self.itemFromIndex(idx)
if item.parent() is None: if item.parent() is None:
is_selected = item.checkState(0)
self.blockSignals(True)
for i in range(item.childCount()): for i in range(item.childCount()):
child = item.child(i) child = item.child(i)
from_parent.append(child) from_parent.append(child)
self.blockSignals(False)
if is_selected == QtCore.Qt.Checked:
item.setCheckState(0, QtCore.Qt.Unchecked)
else:
item.setCheckState(0, QtCore.Qt.Checked)
else:
sets.append(item) sets.append(item)
to_be_hidden = set()
to_be_shown = set()
self.blockSignals(True)
for it in sets: for it in sets:
if it in from_parent: if it in from_parent:
continue continue
it.setCheckState(0, QtCore.Qt.Unchecked if it.checkState(0) == QtCore.Qt.Checked else QtCore.Qt.Checked) it.setCheckState(0, QtCore.Qt.CheckState.Unchecked if it.checkState(0) == QtCore.Qt.CheckState.Checked else QtCore.Qt.CheckState.Checked)
s1, s2 = self.data_change(it, emit=False)
to_be_hidden |= s2
to_be_shown |= s1
self.blockSignals(False)
self.stateChanged.emit(list(to_be_shown), list(to_be_hidden))
else: else:
super().keyPressEvent(evt) super().keyPressEvent(evt)
@ -335,27 +354,47 @@ class DataTree(QtWidgets.QTreeWidget):
self.setDragEnabled(idx.column() == 0) self.setDragEnabled(idx.column() == 0)
super().mousePressEvent(evt) super().mousePressEvent(evt)
def remove_item(self, ids: list): def remove_item(self, ids: list[str]):
iterator = QtWidgets.QTreeWidgetItemIterator(self) iterator = QtWidgets.QTreeWidgetItemIterator(self)
toberemoved = []
graph_removal = []
# find all items that have to be removed
while iterator.value(): while iterator.value():
item = iterator.value() item = iterator.value()
_id = item.data(0, QtCore.Qt.UserRole) _id = item.data(0, QtCore.Qt.ItemDataRole.UserRole)
if _id in ids: if _id in ids:
try: try:
if item.parent() is None: item_parent = item.parent()
if item_parent is None:
raise AttributeError raise AttributeError
idx = item.parent().indexOfChild(item) idx = item_parent.indexOfChild(item)
item.parent().takeChild(idx) # item.parent().takeChild(idx)
toberemoved.append((item_parent, idx))
if _id in self._checked_sets: if _id in self._checked_sets:
self._checked_sets.remove(_id) self._checked_sets.remove(_id)
except AttributeError: except AttributeError:
idx = self.invisibleRootItem().indexOfChild(item) idx = self.invisibleRootItem().indexOfChild(item)
self.invisibleRootItem().takeChild(idx) # self.invisibleRootItem().takeChild(idx)
graph_removal.append(idx)
if _id in self._checked_graphs:
self._checked_graphs.remove(_id) self._checked_graphs.remove(_id)
iterator += 1 iterator += 1
for (item, set_idx) in sorted(toberemoved, key=lambda x: x[1], reverse=True):
item.takeChild(set_idx)
for graph_idx in sorted(graph_removal, reverse=True):
self.invisibleRootItem().takeChild(graph_idx)
self.update_indexes() self.update_indexes()
def contextMenuEvent(self, evt): def contextMenuEvent(self, evt):
@ -381,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')
@ -401,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())
@ -421,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():
@ -439,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
@ -453,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())
@ -460,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)
@ -477,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())
@ -489,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())
@ -498,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)
@ -519,6 +573,7 @@ class DataWidget(QtWidgets.QWidget, Ui_DataWidget):
self.setupUi(self) self.setupUi(self)
self.tree = DataTree(self) self.tree = DataTree(self)
self.verticalLayout.addWidget(self.tree) self.verticalLayout.addWidget(self.tree)
# noinspection PyUnresolvedReferences
self.tree.selectionModel().selectionChanged.connect(lambda x, y: self.show_property(x)) self.tree.selectionModel().selectionChanged.connect(lambda x, y: self.show_property(x))
self.tree.keyChanged.connect(lambda x, y: self.keyChanged.emit(x, y)) self.tree.keyChanged.connect(lambda x, y: self.keyChanged.emit(x, y))
@ -529,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):
@ -536,9 +593,9 @@ class DataWidget(QtWidgets.QWidget, Ui_DataWidget):
self.tree.add_graph(idd, name) self.tree.add_graph(idd, name)
self.tree.blockSignals(False) self.tree.blockSignals(False)
def add_item(self, idd: str, name: str, gid: str): def add_item(self, idd: str, name: str, value: str, gid: str, update: bool= True):
self.tree.blockSignals(True) self.tree.blockSignals(True)
self.tree.add_item((idd, name), gid) self.tree.add_item((idd, name, value), gid, update=update)
self.tree.blockSignals(False) self.tree.blockSignals(False)
def add_item_list(self, loi: list, gid: str): def add_item_list(self, loi: list, gid: str):
@ -546,7 +603,7 @@ class DataWidget(QtWidgets.QWidget, Ui_DataWidget):
self.tree.add_item(loi, gid) self.tree.add_item(loi, gid)
self.tree.blockSignals(False) self.tree.blockSignals(False)
def remove_item(self, key): def remove_item(self, key: list[str]):
self.tree.remove_item(key) self.tree.remove_item(key)
def show_property(self, _: QtCore.QModelIndex = None): def show_property(self, _: QtCore.QModelIndex = None):
@ -556,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)
@ -565,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]):
@ -582,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

@ -42,6 +42,8 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
self.max_y = inf self.max_y = inf
self.min_y = -inf self.min_y = -inf
self.treeWidget.itemChanged.connect(self._update_by_tree)
def __call__(self, graph_name, items): def __call__(self, graph_name, items):
self.label_2.setText(f'Connected to {graph_name}\nChanging tab will remove all integration limits.') self.label_2.setText(f'Connected to {graph_name}\nChanging tab will remove all integration limits.')
@ -77,7 +79,6 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
for idx, rnge in enumerate(self.ranges): for idx, rnge in enumerate(self.ranges):
self._update_values(idx, rnge) self._update_values(idx, rnge)
def add(self, pos): def add(self, pos):
x = pos[0] x = pos[0]
self.ranges.append((x, x*1.1)) self.ranges.append((x, x*1.1))
@ -108,6 +109,9 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
item_list = [] item_list = []
for text, val in [('Start', pts_i[0]), ('Stop', pts_i[1]), ('Areas', 0), ('Ratio', 1.)]: for text, val in [('Start', pts_i[0]), ('Stop', pts_i[1]), ('Areas', 0), ('Ratio', 1.)]:
child = QtWidgets.QTreeWidgetItem() child = QtWidgets.QTreeWidgetItem()
if text.startswith('S'):
child.setFlags(child.flags() | QtCore.Qt.ItemIsEditable)
else:
child.setFlags(QtCore.Qt.NoItemFlags) child.setFlags(QtCore.Qt.NoItemFlags)
child.setText(0, f'{text}: {val:.5g}') child.setText(0, f'{text}: {val:.5g}')
child.setForeground(0, QtGui.QBrush(QtGui.QColor('black'))) child.setForeground(0, QtGui.QBrush(QtGui.QColor('black')))
@ -121,8 +125,27 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
self._update_values(len(self.ranges) - 1, pts_i) self._update_values(len(self.ranges) - 1, pts_i)
def _update_by_tree(self, item: QtWidgets.QTreeWidgetItem) -> None:
parent_item = item.parent()
idx = self.treeWidget.invisibleRootItem().indexOfChild(parent_item)
is_left_border = parent_item.indexOfChild(item) == 0
current_region = self.lines[idx][0]
current_limits = current_region.getRegion()
new_value = item.text(0)
try:
new_value = float(new_value)
if is_left_border:
current_region.setRegion((new_value, current_limits[1]))
else:
current_region.setRegion((current_limits[0], new_value))
except ValueError:
self._update_values(idx, current_limits)
def _update_integral(self): def _update_integral(self):
idx = None idx = None
reg = None
sender = self.sender() sender = self.sender()
for i, (reg, _) in enumerate(self.lines): for i, (reg, _) in enumerate(self.lines):
if sender == reg: if sender == reg:
@ -132,19 +155,20 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
if idx is None: if idx is None:
return return
self._update_values(idx, sender.getRegion()) self._update_values(idx, reg.getRegion())
def _update_values(self, idx, new_range): def _update_values(self, idx, new_range):
self.ranges[idx] = new_range self.ranges[idx] = new_range
area = self.make_integral(idx, *new_range) area = self.make_integral(idx, *new_range)
self.treeWidget.blockSignals(True)
item = self.treeWidget.topLevelItem(idx) item = self.treeWidget.topLevelItem(idx)
item.child(0).setText(0, f'Start: {new_range[0]:.5g}') item.child(0).setText(0, f'Start: {new_range[0]:.5g}')
item.child(1).setText(0, f'Stop: {new_range[1]:.5g}') item.child(1).setText(0, f'Stop: {new_range[1]:.5g}')
if area is not None: if area is not None:
self.areas[idx] = area
item.child(2).setText(0, f'Area: {area:.5g}') item.child(2).setText(0, f'Area: {area:.5g}')
if self.max_area > 0: if self.max_area > 0:
self._set_ratios(idx, self.max_area) self._set_ratios(idx, self.max_area)
@ -157,9 +181,12 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
self._set_ratios(i, curr_max) self._set_ratios(i, curr_max)
self.max_area = curr_max self.max_area = curr_max
self.treeWidget.blockSignals(False)
def _set_ratios(self, idx, max_value): def _set_ratios(self, idx, max_value):
item = self.treeWidget.invisibleRootItem().child(idx) item = self.treeWidget.invisibleRootItem().child(idx)
area_i = self.areas[idx] area_i = self.areas[idx]
item.child(3).setText(0, f'Ratio: {area_i / max_value:.3g}') item.child(3).setText(0, f'Ratio: {area_i / max_value:.3g}')
integral_line = self.lines[idx][1] integral_line = self.lines[idx][1]
@ -178,6 +205,8 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
scale = (self.max_y-self.min_y) / area scale = (self.max_y-self.min_y) / area
self.lines[idx][1].setData(x=integral[:, 0], y=integral[:, 1]*scale + self.min_y) self.lines[idx][1].setData(x=integral[:, 0], y=integral[:, 1]*scale + self.min_y)
self.areas[idx] = area
return area return area
else: else:

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)
@ -177,7 +186,7 @@ class PointSelectWidget(QtWidgets.QWidget, Ui_Form):
it.setText(f'{pos[0]:.5g} - {pos[1]:.5g}') it.setText(f'{pos[0]:.5g} - {pos[1]:.5g}')
self.peaktable.blockSignals(False) self.peaktable.blockSignals(False)
it_pts.blockSignals(True) it_pts.blockSignals(True)
it_pts.setRegion(pos) it_pts.setRegion(pos, use_log=True)
it_pts.blockSignals(False) it_pts.blockSignals(False)
undo = False undo = False
@ -187,10 +196,34 @@ class PointSelectWidget(QtWidgets.QWidget, Ui_Form):
self.peaktable.blockSignals(False) self.peaktable.blockSignals(False)
def set_graphs(self, graphs: list): def set_graphs(self, graphs: list):
last_graph = self.graph_combobox.currentData()
self.graph_combobox.clear() self.graph_combobox.clear()
for g in graphs: idx = 0
for i, g in enumerate(graphs):
self.graph_combobox.addItem(g[1], userData=g[0]) self.graph_combobox.addItem(g[1], userData=g[0])
if g[0] == last_graph:
idx = i
self.graph_combobox.setCurrentIndex(idx)
@QtCore.pyqtSlot(int, name='on_graph_checkbox_stateChanged') @QtCore.pyqtSlot(int, name='on_graph_checkbox_stateChanged')
def changed_state(self, checked): def changed_state(self, checked):
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

@ -1,7 +1,7 @@
import numpy as np import numpy as np
from itertools import cycle from itertools import cycle
from pyqtgraph import mkColor, mkPen from pyqtgraph import mkColor, mkPen, mkBrush
from nmreval.lib.colors import Tab10 from nmreval.lib.colors import Tab10
@ -42,11 +42,17 @@ class QShift(QtWidgets.QDialog, Ui_shift_dialog):
def add_item(self, idx, name, x, y): def add_item(self, idx, name, x, y):
color = mkColor(next(self._colors).rgb()) color = mkColor(next(self._colors).rgb())
if np.iscomplexobj(y):
pl = [PlotItem(x=x, y=y.real, name=name, pen=mkPen(color=color)), if len(y) == 1:
PlotItem(x=x, y=y.imag, name=name, pen=mkPen(color=color))] sym_kwds = {'symbol': 'o', 'symbolBrush': mkBrush(color=color), 'symbolPen': mkPen(color=color)}
else: else:
pl = [PlotItem(x=x, y=y, name=name, pen=mkPen(color=color))] sym_kwds = {'symbol': None, 'symbolBrush': mkBrush(color=color), 'symbolPen': mkPen(color=color)}
if np.iscomplexobj(y):
pl = [PlotItem(x=x, y=y.real, name=name, pen=mkPen(color=color), **sym_kwds),
PlotItem(x=x, y=y.imag, name=name, pen=mkPen(color=color), **sym_kwds)]
else:
pl = [PlotItem(x=x, y=y, name=name, pen=mkPen(color=color), **sym_kwds)]
self.data[idx] = (pl, x, y) self.data[idx] = (pl, x, y)

View File

@ -1,2 +1,2 @@
from .phase_dialog import QApodDialog, QPhasedialog from .phase_dialog import QPreviewDialog
from .baseline_dialog import QBaselineDialog from .baseline_dialog import QBaselineDialog

View File

@ -1,3 +1,4 @@
from nmreval.lib.logger import logger
from nmreval.math import apodization from nmreval.math import apodization
from nmreval.lib.importer import find_models from nmreval.lib.importer import find_models
from nmreval.utils.text import convert from nmreval.utils.text import convert
@ -46,7 +47,7 @@ class EditSignalWidget(QtWidgets.QWidget, Ui_Form):
stype = 'pts' stype = 'pts'
else: else:
try: try:
_nop = float(self.lineEdit.text()) _nop = float(self.ls_lineEdit.text())
except ValueError: except ValueError:
_nop = 0.0 _nop = 0.0
stype = 'time' stype = 'time'
@ -67,7 +68,7 @@ class EditSignalWidget(QtWidgets.QWidget, Ui_Form):
self.do_something.emit(sender, (ph0, ph1, pvt)) self.do_something.emit(sender, (ph0, ph1, pvt))
else: else:
print('You should never reach this by accident.') logger.warning(f'You should never reach this by accident, invalid sender {sender!r}')
@QtCore.pyqtSlot(int, name='on_apodcombobox_currentIndexChanged') @QtCore.pyqtSlot(int, name='on_apodcombobox_currentIndexChanged')
def change_apodization(self, index): def change_apodization(self, index):

View File

@ -1,119 +1,43 @@
from __future__ import annotations from __future__ import annotations
import numpy as np
from pyqtgraph import mkPen from pyqtgraph import mkPen
from numpy import inf, linspace import numpy as np
from numpy.fft import fft, fftfreq, fftshift from numpy import pi
from numpy.fft import fft, fftshift, fftfreq
from nmreval.data import FID, Spectrum
from ...lib.pg_objects import PlotItem, LogInfiniteLine from ...lib.pg_objects import PlotItem, LogInfiniteLine
from nmreval.lib.importer import find_models from nmreval.lib.importer import find_models
from nmreval.math import apodization as apodization from nmreval.math import apodization as apodization
from nmreval.utils.text import convert from nmreval.utils.text import convert
from ...Qt import QtCore, QtWidgets from ...Qt import QtCore, QtWidgets, QtGui
from ..._py.apod_dialog import Ui_ApodEdit from ..._py.apod_dialog import Ui_ApodEdit
from ..._py.phase_corr_dialog import Ui_SignalEdit
from ...lib.forms import FormWidget from ...lib.forms import FormWidget
class QPreviewDialogs(QtWidgets.QDialog): class QPreviewDialog(QtWidgets.QDialog, Ui_ApodEdit):
finished = QtCore.pyqtSignal(str, tuple) finished = QtCore.pyqtSignal(str, tuple)
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent=parent) super().__init__(parent=parent)
self.data = []
self.graphs = []
self.mode = ''
def setRange(self, xlim: list, ylim: list, logmode: list[bool]):
self.graphicsView.getPlotItem().setLogMode(x=logmode[0], y=logmode[1])
if logmode[0]:
xlim = [np.log10(x) for x in xlim]
if logmode[1]:
ylim = [np.log10(y) for y in ylim]
self.graphicsView.setRange(xRange=xlim, yRange=ylim, padding=0, disableAutoRange=True)
def add_data(self, x, y):
self.data.append((x, y))
real_plt = PlotItem(x=x, y=y.real, pen=mkPen('b'), )
imag_plt = PlotItem(x=x, y=y.imag, pen=mkPen('r'))
self.graphs.append((real_plt, imag_plt))
self.graphicsView.addItem(real_plt)
self.graphicsView.addItem(imag_plt)
def done(self, val):
self.cleanup()
super().done(val)
def close(self):
self.cleanup()
super().close()
def accept(self):
self.finished.emit(self.mode, self.get_value())
super().accept()
def get_value(self):
raise NotImplementedError
def cleanup(self):
self.blockSignals(True)
for line in self.graphs:
for g in line:
self.graphicsView.removeItem(g)
del g
self.graphicsView.clear()
self.data = []
self.graphs = []
self.blockSignals(False)
class QPhasedialog(QPreviewDialogs, Ui_SignalEdit):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setupUi(self) self.setupUi(self)
self.mode = 'ph' self.data = []
self.graphs = []
self._tmp_data_bl = []
self._tmp_data_zf = []
self._tmp_data_ls = []
self._tmp_data_ap = []
self._tmp_data_ph = []
self.pvt_line = LogInfiniteLine(pos=0, movable=True) self.pvt_line = LogInfiniteLine(pos=0, movable=True)
self.graphicsView.addItem(self.pvt_line) self.freq_graph.addItem(self.pvt_line)
self.pvt_line.sigPositionChanged.connect(self.move_line) self.pvt_line.sigPositionChanged.connect(self.move_line)
@QtCore.pyqtSlot(float, name='on_ph1slider_valueChanged') self.ls_lineedit.hide()
@QtCore.pyqtSlot(float, name='on_ph0slider_valueChanged')
def _temp_phase(self, *args):
ph0, ph1, pvt = self.get_value()
self.pvt_line.setValue(pvt)
for i, (x, y) in enumerate(self.data):
phasecorr = np.exp(-1j * (ph0 + ph1*(x-pvt)/np.max(x))*np.pi/180.)
_y = y * phasecorr
self.graphs[i][0].setData(x=x, y=_y.real)
self.graphs[i][1].setData(x=x, y=_y.imag)
def get_value(self):
return float(self.ph0slider.text()), float(self.ph1slider.text()), float(self.pivot_lineedit.text())
def move_line(self, evt):
self.pivot_lineedit.setText(f'{evt.value():.5g}')
class QApodDialog(QPreviewDialogs, Ui_ApodEdit):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setupUi(self)
self._limits = (-inf, inf), -inf
self.apods = []
self.apods = find_models(apodization) self.apods = find_models(apodization)
self.apodcombobox.blockSignals(True) self.apodcombobox.blockSignals(True)
@ -122,72 +46,288 @@ class QApodDialog(QPreviewDialogs, Ui_ApodEdit):
self.apodcombobox.blockSignals(False) self.apodcombobox.blockSignals(False)
self.apod_graph = PlotItem(x=[], y=[]) self.apod_graph = PlotItem(x=[], y=[])
self.graphicsView.addItem(self.apod_graph) self.time_graph.addItem(self.apod_graph)
self.mode = 'ap' for g in [self.freq_graph, self.time_graph]:
pl = g.getPlotItem()
pl.hideButtons()
pl.setMenuEnabled(False)
self._all_time = None
self._all_freq = None
self.change_apodization(0) self.change_apodization(0)
def add_data(self, x, y): self.shift_box.clicked.connect(self._update_shift)
real_plt = PlotItem(x=x, y=y.real, pen=mkPen('b')) self.ls_spinbox.valueChanged.connect(self._update_shift)
# imag_plt = (x=x, y=y.imag, pen=pg.mkPen('r')) self.ls_lineedit.setValidator(QtGui.QDoubleValidator())
self.graphicsView.addItem(real_plt) self.ls_lineedit.textChanged.connect(self._update_shift)
# self.graphicsView.addItem(imag_plt)
self.zerofill_box.clicked.connect(self._update_zf)
self.zf_spinbox.valueChanged.connect(self._update_zf)
self.apod_box.clicked.connect(self._update_apod)
self.phase_box.clicked.connect(self._update_phase)
self.ph0_spinbox.valueChanged.connect(self._update_phase)
self.ph1_spinbox.valueChanged.connect(self._update_phase)
self.pivot_lineedit.setValidator(QtGui.QDoubleValidator())
self.pivot_lineedit.textChanged.connect(self._update_phase)
self.pivot_lineedit.textEdited.connect(lambda x: self.pvt_line.setValue(float(x)))
def add_data(self: QPreviewDialog, data: FID | Spectrum) -> bool:
if isinstance(data, FID):
valid, (real_plt, imag_plt, real_plt_fft, imag_plt_fft) = self._prep_time(data)
elif isinstance(data, Spectrum):
valid, (real_plt, imag_plt, real_plt_fft, imag_plt_fft) = self._prep_freq(data)
else:
return False
if not valid:
return False
x_len = data.x.size
self.zf_spinbox.setMaximum(min(2**17//x_len, 3))
self.time_graph.addItem(imag_plt)
self.time_graph.addItem(real_plt)
self.freq_graph.addItem(imag_plt_fft)
self.freq_graph.addItem(real_plt_fft)
self.data.append(data)
for p in [self._tmp_data_bl, self._tmp_data_ls]:
p.append(data.y.copy())
for p in [self._tmp_data_zf, self._tmp_data_ap]:
p.append((data.x, data.y.copy()))
self.graphs.append((real_plt, imag_plt, real_plt_fft, imag_plt_fft))
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')
def _update_bl(self):
if self.baseline_box.isChecked():
for y in self._tmp_data_bl:
self._temp_baseline(y)
else:
for i, d in enumerate(self.data):
self._tmp_data_bl[i] = d.y.copy()
self._update_shift()
def _update_shift(self):
if self.shift_box.isChecked():
if self.ls_combobox.currentIndex() == 0:
num_points = self.ls_spinbox.value()
is_time = False
else:
num_points = float(self.ls_lineedit.text())
is_time = True
for i, y in enumerate(self._tmp_data_bl):
self._tmp_data_ls[i] = self._temp_leftshift(self.data[i].dx, y, num_points, is_time)
else:
for i, y in enumerate(self._tmp_data_bl):
self._tmp_data_ls[i] = y
self._update_zf()
def _update_zf(self):
zf_padding = self.zf_spinbox.value()
if self.zerofill_box.isChecked():
for i, y in enumerate(self._tmp_data_ls):
self._tmp_data_zf[i] = self._temp_zerofill(self.data[i].x, y, zf_padding)
else:
for i, y in enumerate(self._tmp_data_ls):
self._tmp_data_zf[i] = self.data[i].x, y
self._update_apod()
def _update_apod(self):
if self.apod_box.isChecked():
model = self.apods[self.apodcombobox.currentIndex()]
p = self._get_parameter()
x_limit = np.inf, -np.inf
y_limit = -np.inf
for i, (x, y) in enumerate(self._tmp_data_zf):
self._tmp_data_ap[i] = x, y * model.apod(x, *p)
y_limit = max(y.real.max(), y_limit)
x_limit = min(x_limit[0], x.min()), max(x_limit[1], x.max())
_x_apod = np.linspace(*x_limit, num=150)
_y_apod = model.apod(_x_apod, *p)
self.apod_graph.setData(x=_x_apod, y=y_limit * _y_apod)
self.apod_graph.show()
else:
for i, (x, y) in enumerate(self._tmp_data_zf):
self._tmp_data_ap[i] = x, y
self.apod_graph.hide()
self._update_phase()
def _update_phase(self):
if self.phase_box.isChecked():
pvt = float(self.pivot_lineedit.text())
self.pvt_line.show()
ph0 = self.ph0_spinbox.value()
ph1 = self.ph1_spinbox.value()
for i, (x, y) in enumerate(self._tmp_data_ap):
x_fft, y_fft = self._temp_fft(x, y, self.baseline_box.isChecked())
if ph0 != 0:
y = self._temp_phase(x, y, ph0, 0, 0)
y_fft = self._temp_phase(x, y_fft, ph0, ph1, pvt)
elif ph1 != 0:
y_fft = self._temp_phase(x, y_fft, ph0, ph1, pvt)
self._tmp_data_ph[i] = x, y, x_fft, y_fft
else:
self.pvt_line.hide()
for i, (x, y) in enumerate(self._tmp_data_ap):
self._tmp_data_ph[i] = x, y, *self._temp_fft(x, y, self.baseline_box.isChecked())
self._update_plots()
def _update_plots(self):
for i, (x, y, xf, yf) in enumerate(self._tmp_data_ph):
self.graphs[i][0].setData(x=x, y=y.real)
self.graphs[i][1].setData(x=x, y=y.imag)
self.graphs[i][2].setData(x=xf, y=yf.real)
self.graphs[i][3].setData(x=xf, y=yf.imag)
@staticmethod
def _temp_baseline_time(y):
y -= y[int(-0.12 * y.size):].mean()
@staticmethod
def _temp_baseline_freq(y):
region = int(0.12 * y.size)
y -= np.mean([y[-region:], y[:region]])
@staticmethod
def _temp_phase(x: np.ndarray, y: np.ndarray, ph0: float, ph1: float, pvt: float) -> np.ndarray:
phase_correction = np.exp(-1j * (ph0 + ph1 * (x - pvt) / x.max()) * pi / 180.)
_y = y * phase_correction
return _y
@staticmethod
def _temp_zerofill(x: np.ndarray, y: np.ndarray, num_padding: int) -> tuple[np.ndarray, np.ndarray]:
length = x.size
factor = 2**num_padding
_y = np.r_[y, np.zeros((factor-1) * length)]
_temp_x = np.arange(1, (factor-1) * length+1) * (x[1]-x[0]) + np.max(x)
_x = np.r_[x, _temp_x]
return _x, _y
@staticmethod
def _temp_leftshift(dx: np.ndarray, y: np.ndarray, points: float | int, is_time: bool) -> np.ndarray:
if is_time:
points = int(points//dx)
_y = np.roll(y, -points)
_y[-points-1:] = 0
return _y
@staticmethod
def _temp_fft_time(x: np.ndarray, y: np.ndarray, baseline: bool = False) -> tuple[np.ndarray, np.ndarray]:
y_fft = fftshift(fft(y)) y_fft = fftshift(fft(y))
x_fft = fftshift(fftfreq(len(x), d=x[1]-x[0])) x_fft = fftshift(fftfreq(len(x), d=x[1]-x[0]))
real_plt_fft = PlotItem(x=x_fft, y=y_fft.real, pen=mkPen('b'))
# imag_plt_fft = pg.PlotDataItem(x=x_fft, y=y_fft.imag, pen=pg.mkPen('b'))
self.graphicsView_2.addItem(real_plt_fft)
# self.graphicsView_2.addItem(imag_plt_fft)
self.graphs.append((real_plt, real_plt_fft)) if baseline:
self.data.append((x, y, x_fft)) QPreviewDialog._temp_baseline_freq(y_fft)
xlimits = (max(x.min(), self._limits[0][0]), min(x.max(), self._limits[0][1])) return x_fft, y_fft
ylimit = max(self._limits[1], y.real.max())
self._limits = xlimits, ylimit @staticmethod
def _temp_fft_freq(x: np.ndarray, y: np.ndarray, _=None):
return x, y
def move_line(self, evt):
self.pivot_lineedit.setText(f'{evt.value():.5g}')
@QtCore.pyqtSlot(int, name='on_apodcombobox_currentIndexChanged') @QtCore.pyqtSlot(int, name='on_apodcombobox_currentIndexChanged')
def change_apodization(self, index): def change_apodization(self, index: int) -> None:
# delete old widgets # delete old widgets
self.eqn_label.setText(convert(self.apods[index].equation)) self.eqn_label.setText(convert(self.apods[index].equation))
while self.widget_layout.count(): while self.widget_layout.count():
item = self.widget_layout.takeAt(0) item = self.widget_layout.takeAt(0)
if isinstance(item, FormWidget):
item.disconnect()
try: try:
item.widget().deleteLater() item.widget().deleteLater()
except AttributeError: except AttributeError:
pass pass
# set up parameter widgets for new model # set up parameter widgets for new model
for k, v in enumerate(self.apods[index]().params): for k, v in enumerate(self.apods[index].params):
widgt = FormWidget(name=v) widget = FormWidget(name=v)
widgt.valueChanged.connect(self._temp_apod) widget.value = 1
self.widget_layout.addWidget(widgt) widget.valueChanged.connect(self._update_apod)
self.widget_layout.addWidget(widget)
self.widget_layout.addStretch() self.widget_layout.addStretch()
self._temp_apod() self._update_apod()
def _temp_apod(self):
apodmodel = self.apods[self.apodcombobox.currentIndex()]
p = self._get_parameter()
if self.data:
for i, (x, y, x_fft) in enumerate(self.data):
y2 = apodmodel.apod(x, *p)
_y = y2 * y
self.graphs[i][0].setData(x=x, y=_y.real)
# self.graphs[i][1].setData(y=_y.imag)
y_fft = fftshift(fft(_y))
self.graphs[i][1].setData(x=x_fft, y=y_fft.real)
# self.graphs[i][3].setData(y=y_fft.imag)
_x_apod = linspace(self._limits[0][0], self._limits[0][1])
try:
_y_apod = apodmodel.apod(_x_apod, *p)
self.apod_graph.setData(x=_x_apod, y=self._limits[1]*_y_apod)
except IndexError:
pass
def _get_parameter(self): def _get_parameter(self):
p = [] p = []
@ -201,8 +341,104 @@ class QApodDialog(QPreviewDialogs, Ui_ApodEdit):
return p return p
def get_value(self): @QtCore.pyqtSlot(int, name='on_ls_combobox_currentIndexChanged')
apodmodel = self.apods[self.apodcombobox.currentIndex()] def change_ls(self, idx: int) -> None:
p = self._get_parameter() self.ls_lineedit.setVisible(bool(idx))
self.ls_spinbox.setVisible(not bool(idx))
@QtCore.pyqtSlot(bool, name='on_ft_checkbox_stateChanged')
def change_ft(self, state: bool):
self.ph1_spinbox.setEnabled(state)
self.pivot_lineedit.setEnabled(state)
def cleanup(self):
self.blockSignals(True)
for line in self.graphs:
for g in line:
self.time_graph.removeItem(g)
self.freq_graph.removeItem(g)
del g
self.time_graph.clear()
self.freq_graph.clear()
self._tmp_data_ap = []
self._tmp_data_bl = []
self._tmp_data_ls = []
self._tmp_data_ph = []
self._tmp_data_zf = []
self.data = []
self.graphs = []
self.freq_graph.removeItem(self.pvt_line)
self.blockSignals(False)
def get_value(self):
edits = [(None,), (None,), (None,), (None,), (None,), (None,)]
if self.baseline_box.isChecked():
edits[0] = (True,)
if self.zerofill_box.isChecked():
edits[2] = (self.zf_spinbox.value(),)
if self.shift_box.isChecked():
if self.ls_combobox.currentIndex() == 0:
edits[1] = (self.ls_spinbox.value(), 'pts')
else:
edits[1] = (float(self.ls_lineedit.text()), 'time')
if self.apod_box.isChecked():
edits[3] = (self._get_parameter(), self.apods[self.apodcombobox.currentIndex()])
if self.phase_box.isChecked():
edits[4] = (self.ph0_spinbox.value(), self.ph1_spinbox.value(), float(self.pivot_lineedit.text()))
if self.ft_box.isChecked():
edits[5] = (self.phase_before_button.isChecked(),)
return edits
def exec(self):
self._prepare_ui()
return super().exec()
def _prepare_ui(self):
"""Stuff we have to do before showing the window but after all the data was added"""
vb = self.freq_graph.getPlotItem().getViewBox()
vb.disableAutoRange(axis=vb.YAxis)
vb = self.time_graph.getPlotItem().getViewBox()
vb.disableAutoRange(axis=vb.YAxis)
if self._all_time is not None:
self.zerofill_box.setVisible(self._all_time)
self.apod_box.setVisible(self._all_time)
self.shift_box.setVisible(self._all_time)
self.time_graph.setVisible(self._all_time)
self.logtime_widget.setVisible(self._all_time)
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_logy_time_stateChanged')
@QtCore.pyqtSlot(int, name='on_logx_freq_stateChanged')
@QtCore.pyqtSlot(int, name='on_logy_freq_stateChanged')
def set_log(self, state: int):
switch = {
self.logx_time: lambda _x: self.time_graph.setLogMode(x=_x),
self.logy_time: lambda _x: self.time_graph.setLogMode(y=_x),
self.logx_freq: lambda _x: self.freq_graph.setLogMode(x=_x),
self.logy_freq: lambda _x: self.freq_graph.setLogMode(y=_x),
}[self.sender()]
switch(state == QtCore.Qt.Checked)
vb = self.freq_graph.getPlotItem().getViewBox()
vb.disableAutoRange(axis=vb.YAxis)
vb = self.time_graph.getPlotItem().getViewBox()
vb.disableAutoRange(axis=vb.YAxis)
return p, apodmodel

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)
@ -188,7 +188,15 @@ class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog):
new_value = complex(val) new_value = complex(val)
new_value = new_value.real if new_value.imag == 0 else new_value new_value = new_value.real if new_value.imag == 0 else new_value
# table view loses focus when itemChanged is emitted
# if edit of item is cause of change resume editing at next item
prev_state = self.tableView.state()
idx = self.tableView.currentIndex()
idx = idx.sibling((col+1)//3+row, (col+1) % 3)
self.itemChanged.emit(sid, (col, row), new_value) self.itemChanged.emit(sid, (col, row), new_value)
if prev_state == self.tableView.State.EditingState:
self.tableView.setCurrentIndex(idx)
self.tableView.edit(idx)
@QtCore.pyqtSlot(QtCore.QItemSelection, QtCore.QItemSelection) @QtCore.pyqtSlot(QtCore.QItemSelection, QtCore.QItemSelection)
def show_position(self, *_): def show_position(self, *_):
@ -221,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)
@ -232,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
@ -250,28 +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()]
if isinstance(val, complex): return self.as_string(val)
return f'{val.real:.8g}{val.imag:+.8g}j'
else:
return f'{val:.8g}'
elif role == QtCore.Qt.BackgroundRole: elif role == QtCore.Qt.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)
@ -284,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:
@ -294,12 +302,17 @@ 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]):
return True
try: try:
value = complex(value) value = complex(value)
except ValueError: except ValueError:
# not a number # not a number
return False return False
value = value.real if value.imag == 0 else value
self._data[row][col] = value.real if value.imag == 0 else value self._data[row][col] = value.real if value.imag == 0 else value
self.itemChanged.emit(col, row, str(value)) self.itemChanged.emit(col, row, str(value))
self.dataChanged.emit(self.index(0, 0), self.index(0, 1), [role]) self.dataChanged.emit(self.index(0, 0), self.index(0, 1), [role])
@ -312,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)
@ -336,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)
@ -368,3 +381,10 @@ class ValueModel(QtCore.QAbstractTableModel):
def unmask(self): def unmask(self):
self.mask = [True] * self.total_rows self.mask = [True] * self.total_rows
self.dataChanged.emit(self.index(0, 0), self.index(0, 1), [ValueModel.maskRole]) self.dataChanged.emit(self.index(0, 0), self.index(0, 1), [ValueModel.maskRole])
@staticmethod
def as_string(value) -> str:
if isinstance(value, complex):
return f'{value.real:.13g}{value.imag:+.13g}j'
else:
return f'{value:.13g}'

View File

View File

@ -0,0 +1,375 @@
from itertools import cycle
from numpy import array, nan, isnan
from pyqtgraph import mkPen, mkBrush
from nmreval.dsc.hodge import tau_hodge
from nmreval.lib.colors import Tab10
from ..Qt import QtWidgets, QtCore
from .._py.tnmh_dialog import Ui_DSCEvalDialog
from ..lib.pg_objects import PlotItem, RegionItem
from nmreval.data import DSC, Points
class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
newData = QtCore.pyqtSignal(dict, str)
def __init__(self, management, parent=None):
super().__init__(parent=parent)
self.setupUi(self)
self._management = management
self._colors = cycle(Tab10)
self._dsc = {}
self._plots = {}
self._tg_value = {}
self._fit = {}
self._hodge = {
'onset': (
PlotItem(x=[], y=[], pen=None, symbol='o', symbolBrush=Tab10.TabBlue.rgb(), name='Onset'),
None,
(PlotItem(x=[], y=[], pen=mkPen({'color': Tab10.TabBlue.rgb()})),
PlotItem(x=[], y=[], pen=None, symbol='o', symbolBrush=Tab10.TabBlue.rgb())),
None,
),
'midpoint': (
PlotItem(x=[], y=[], pen=None, symbol='s', symbolBrush=Tab10.TabOrange.rgb(), name='Midpoint'),
None,
(PlotItem(x=[], y=[], pen=mkPen({'color': Tab10.TabOrange.rgb()})),
PlotItem(x=[], y=[], pen=None, symbol='s', symbolBrush=Tab10.TabOrange.rgb())),
None,
),
'end': (
PlotItem(x=[], y=[], pen=None, symbol='t', symbolBrush=Tab10.TabGreen.rgb(), name='End'),
None,
(PlotItem(x=[], y=[], pen=mkPen({'color': Tab10.TabGreen.rgb()})),
PlotItem(x=[], y=[], pen=None, symbol='t', symbolBrush=Tab10.TabGreen.rgb())),
None,
),
'inflection': (
PlotItem(x=[], y=[], pen=None, symbol='d', symbolBrush=Tab10.TabRed.rgb(), name='Inflection'),
None,
(PlotItem(x=[], y=[], pen=mkPen({'color': Tab10.TabRed.rgb()})),
PlotItem(x=[], y=[], pen=None, symbol='d', symbolBrush=Tab10.TabRed.rgb())),
None,
),
'fictive': (
PlotItem(x=[], y=[], pen=None, symbol='t1', symbolBrush=Tab10.TabPurple.rgb(), name='Fictive'),
None,
(PlotItem(x=[], y=[], pen=mkPen({'color': Tab10.TabPurple.rgb()})),
PlotItem(x=[], y=[], pen=None, symbol='t1', symbolBrush=Tab10.TabPurple.rgb())),
None,
),
}
self._lines = {}
self.tau_plot.getPlotItem().addLegend()
for plt, _, fitplt, _ in self._hodge.values():
self.tau_plot.addItem(plt)
self.tghodge_graph.addItem(fitplt[0])
self.tghodge_graph.addItem(fitplt[1])
self.tau_plot.setLogMode(y=True)
self.tghodge_graph.setLogMode(y=True)
self.limits = RegionItem(), RegionItem()
for lim in self.limits:
self.dsc_plot.addItem(lim)
self._limitless = True
self.add_sets()
self.tnmh_graph_check.stateChanged.connect(lambda state: self.tnmh_graph_combo.setEnabled(not bool(state)))
self.hodge_graph_check.stateChanged.connect(lambda state: self.hodge_graph_combo.setEnabled(not bool(state)))
self.next_button.clicked.connect(lambda: self.stackedWidget.setCurrentIndex((self.stackedWidget.currentIndex() + 1) % 3))
self.back_button.clicked.connect(lambda: self.stackedWidget.setCurrentIndex((self.stackedWidget.currentIndex() + 2) % 3))
self.listWidget.itemChanged.connect(self.change_visibility)
def __call__(self):
self.clear()
self._colors = cycle(Tab10)
self.add_sets()
return self
def clear(self):
self.listWidget.clear()
self.tg_tree.clear()
self.tnmh_tree.clear()
for plots in self._plots.values():
for val in plots:
self.dsc_plot.removeItem(val)
self.tnmh_graphics.removeItem(val)
for key, plt in self._hodge.items():
plt[0].setData(x=[], y=[])
plt[2][0].setData(x=[], y=[])
plt[2][1].setData(x=[], y=[])
self._hodge[key] = (plt[0], None, plt[2], None)
self._dsc = {}
self._plots = {}
self._tg_value = {}
self._lines = {}
self._fit = {}
self.stackedWidget.setCurrentIndex(0)
def add_sets(self):
for w in (self.tnmh_graph_combo, self.hodge_graph_combo):
w.clear()
for graphs in self._management.graphs.list():
w.addItem(graphs[1], userData=graphs[0])
min_x = 10_000_000
max_x = -10_000_000
for (key, name), c in zip(self._management.active_sets, self._colors):
data = self._management[key].data
if not isinstance(data, DSC):
continue
min_x = min(min_x, data.x.min())
max_x = max(max_x, data.x.max())
item = QtWidgets.QListWidgetItem(name)
item.setCheckState(QtCore.Qt.CheckState.Checked)
item.setData(QtCore.Qt.ItemDataRole.UserRole, key)
item.setForeground(mkBrush(c.rgb()))
self.listWidget.addItem(item)
self._dsc[key] = (data, None)
data_plot = PlotItem(x=data.x, y=data.y, pen=mkPen(c.rgb()))
self.dsc_plot.addItem(data_plot)
glass = PlotItem()
glass.set_line(style=2, color=c)
self.dsc_plot.addItem(glass)
liquid = PlotItem()
liquid.set_line(style=2, color=c)
self.dsc_plot.addItem(liquid)
tangent = PlotItem()
tangent.set_line(style=2, color=c)
self.dsc_plot.addItem(tangent)
tg_plot = PlotItem(pen=None, symbolBrush=c.rgb(), symbol='o')
self.dsc_plot.addItem(tg_plot)
fictive_cp = PlotItem(pen=mkPen(c.rgb()))
self.tnmh_graphics.addItem(fictive_cp)
tnmh_fit = PlotItem()
tnmh_fit.set_line(style=2, color=c)
self.tnmh_graphics.addItem(tnmh_fit)
self._plots[key] = (data_plot, tg_plot, glass, liquid, tangent, fictive_cp, tnmh_fit)
self._tg_value[key] = {
'onset': (nan, nan),
'midpoint': (nan, nan),
'end': (nan, nan),
'inflection': (nan, nan),
# 'fictive': (nan, nan),
}
if self._limitless and max_x != -10000000 and min_x != 10000000 :
dist = max_x - min_x
self.limits[0].setRegion((min_x, min_x+min(0.1*dist, 5)))
self.limits[1].setRegion((max_x-min(5, 0.1*dist), max_x))
self._limitless = False
@QtCore.pyqtSlot(name='on_calctg_button_clicked')
def calc_tg(self):
baselines = tuple(lim.getRegion() for lim in self.limits)
if baselines[0][0] > baselines[1][0]:
baselines = baselines[1], baselines[0]
for idx in range(self.listWidget.count()):
item = self.listWidget.item(idx)
if item.checkState() == QtCore.Qt.CheckState.Unchecked:
continue
key = item.data(QtCore.Qt.ItemDataRole.UserRole)
plot = self._plots[key]
data, _ = self._dsc[key]
tg_results, glass, liquid, tangent = data.glass_transition(*baselines)
self._lines[key] = (glass, liquid, tangent)
for i, line in enumerate((glass, liquid, tangent)):
plot[i+2].setData(x=line.x, y=line.y)
self._tg_value[key].update(tg_results)
self._update_tg_plots()
def _update_tg_plots(self):
self.tg_tree.clear()
for idx in range(self.listWidget.count()):
item = self.listWidget.item(idx)
tree_item = QtWidgets.QTreeWidgetItem([item.text()])
values = self._tg_value.get(item.data(QtCore.Qt.ItemDataRole.UserRole))
if values is not None:
for name, pos in values.items():
child_item = QtWidgets.QTreeWidgetItem([f'{name.capitalize()}: {pos[0]:.2f} K'])
tree_item.addChild(child_item)
self.tg_tree.addTopLevelItem(tree_item)
key = item.data(QtCore.Qt.ItemDataRole.UserRole)
plot = self._plots[key]
data, _ = self._dsc[key]
plot[1].setData(array(list(self._tg_value[key].values())))
@QtCore.pyqtSlot(name='on_tg_export_button_clicked')
def export_tg(self):
ret_dic = {}
for key, tg in self._tg_value.items():
tgx = [x for x, y in tg.values()]
tgy = [y for x, y in tg.values()]
if self.tg_export_check.isChecked():
tg_pts = Points(x=tgx, y=tgy, name=self._management[key].name + ' (Tg)', value=self._management[key].value)
else:
tg_pts = None
line = []
if self.tglines_export_check.isChecked():
line = self._lines.get(key, [])
ret_dic[key] = (tg_pts, line)
self.newData.emit(ret_dic, 'tg')
@QtCore.pyqtSlot(QtWidgets.QListWidgetItem)
def change_visibility(self, item: QtWidgets.QListWidgetItem):
is_checked = bool(item.checkState())
plot = self._plots[item.data(QtCore.Qt.ItemDataRole.UserRole)]
for val in plot:
val.setVisible(is_checked)
def get_fictive(self, key, baselines):
plot = self._plots[key]
data, _ = self._dsc[key]
cp, tg = data.get_fictive_cp(*baselines)
plot[5].setData(cp.x, cp.y)
self._dsc[key] = (data, cp)
return cp
@QtCore.pyqtSlot(name='on_tnhm_fitbutton_clicked')
def make_tnmh(self):
baselines = tuple(lim.getRegion() for lim in self.limits)
if baselines[0][0] > baselines[1][0]:
baselines = baselines[1], baselines[0]
self.tnmh_tree.clear()
for idx in range(self.listWidget.count()):
item = self.listWidget.item(idx)
if item.checkState() == QtCore.Qt.CheckState.Unchecked:
continue
key = item.data(QtCore.Qt.ItemDataRole.UserRole)
data = self.get_fictive(key, baselines)
res = data.calculate_tnmh([60, 0.5, 1, 2e5], *baselines, return_fictive=False)
self._fit[key] = res
plot = self._plots[key]
plot[-1].setData(res.x, res.y)
for idx in range(self.listWidget.count()):
item = self.listWidget.item(idx)
tree_item = QtWidgets.QTreeWidgetItem([item.text()])
values = self._fit.get(item.data(QtCore.Qt.ItemDataRole.UserRole))
if values is not None:
child_item = QtWidgets.QTreeWidgetItem([values.parameter_string()])
tree_item.addChild(child_item)
self.tnmh_tree.addTopLevelItem(tree_item)
@QtCore.pyqtSlot(name='on_tnmh_export_button_clicked')
def export_tnmh(self):
ret_dic = {}
for idx in range(self.listWidget.count()):
item = self.listWidget.item(idx)
if item.checkState() == QtCore.Qt.CheckState.Unchecked:
continue
key = item.data(QtCore.Qt.ItemDataRole.UserRole)
cp = None
if self.fictive_export_check.isChecked():
_, cp = self._dsc[key]
line = None
if self.tnmhfit_export_check.isChecked():
line = self._fit.get(key)
ret_dic[key] = (cp, line)
ret_dic['graph'] = '' if self.tnmh_graph_check.isChecked() else self.tnmh_graph_combo.currentData()
self.newData.emit(ret_dic, 'tnmh')
@QtCore.pyqtSlot(name='on_hodge_button_clicked')
def hodge(self):
for tg_type, (plot, data, fitplots, fit) in self._hodge.items():
m = []
for idx in range(self.listWidget.count()):
item = self.listWidget.item(idx)
if item.checkState() == QtCore.Qt.CheckState.Unchecked:
continue
key = item.data(QtCore.Qt.ItemDataRole.UserRole)
data, _ = self._dsc[key]
try:
tg_value = self._tg_value[key][tg_type][0]
if isnan(tg_value):
continue
except KeyError:
continue
m.append([tg_value, data.value])
if len(m) > 1:
data, fit = tau_hodge(*array(m).T)
data.name = f'{data.name} ({tg_type.capitalize()})'
plot.setData(data.x, data.y)
fitplots[0].setData(fit.x, fit.y)
fitplots[1].setData(fit.x_data, fit.y_data)
self._hodge[tg_type] = (plot, data, fitplots, fit)
@QtCore.pyqtSlot(name='on_export_hodge_button_clicked')
def export_hodge(self):
ret_dic = {}
for cb in (self.onset_check, self.mid_check, self.end_check, self.inflection_check, self.fictive_check):
if cb.isChecked():
item = cb.text().lower()
data = self._hodge.get(item)
if data[1] is not None:
ret_dic[item] = data[1]
ret_dic['graph'] = '' if self.hodge_graph_check.isChecked() else self.hodge_graph_combo.currentData()
self.newData.emit(ret_dic, 'hodge')
def close(self) -> bool:
self.clear()
return super().close()

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, 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,191 +1,9 @@
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 import get_icon from ..lib.iconloading import get_icon
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()
def __init__(self, label: str = 'Fitparameter', parent=None, fixed: bool = False):
super().__init__(parent)
self.setupUi(self)
self.parametername.setText(label + ' ')
validator = QtGui.QDoubleValidator()
validator.setDecimals(9)
self.parameter_line.setValidator(validator)
self.parameter_line.setText('1')
self.parameter_line.setMaximumWidth(60)
self.lineEdit.setMaximumWidth(60)
self.lineEdit_2.setMaximumWidth(60)
self.label_3.setText(f'&lt; {label} &lt;')
self.checkBox.stateChanged.connect(self.enableBounds)
self.global_checkbox.stateChanged.connect(lambda: self.state_changed.emit())
self.parameter_line.values_requested.connect(lambda: self.value_requested.emit(self))
self.parameter_line.editingFinished.connect(lambda: self.value_changed.emit(self.parameter_line.text()))
self.fixed_check.toggled.connect(self.set_fixed)
if fixed:
self.fixed_check.hide()
self.menu = QtWidgets.QMenu(self)
self.add_links()
self.is_linked = None
self.parameter_pos = None
self.func_idx = None
self._linetext = '1'
@property
def name(self):
return convert(self.parametername.text().strip(), old='html', new='str')
def set_parameter_string(self, p: str):
self.parameter_line.setText(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):
if p is None:
# bad hack: linked parameter return (None, linked parameter)
# if p is None -> parameter is linked to argument given by bds
self.link_parameter(linkto=bds)
else:
ptext = f'{p:.4g}'
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):
if self.is_linked:
try:
p = float(self._linetext)
except ValueError:
p = 1.0
else:
try:
p = float(self.parameter_line.text().replace(',', '.'))
except ValueError:
_ = QtWidgets.QMessageBox().warning(self, 'Invalid value',
f'{self.parametername.text()} contains invalid values',
QtWidgets.QMessageBox.Cancel)
return None
if self.checkBox.isChecked():
try:
lb = float(self.lineEdit.text().replace(',', '.'))
except ValueError:
lb = None
try:
rb = float(self.lineEdit_2.text().replace(',', '.'))
except ValueError:
rb = None
else:
lb = rb = None
bounds = (lb, rb)
return p, bounds, not self.fixed_check.isChecked(), self.global_checkbox.isChecked(), self.is_linked
@QtCore.pyqtSlot(bool)
def set_fixed(self, state: bool):
# self.global_checkbox.setVisible(not state)
self.frame.setVisible(not state)
def add_links(self, parameter: dict = None):
if parameter is None:
parameter = {}
self.menu.clear()
ac = QtWidgets.QAction('Link to...', self)
ac.triggered.connect(self.link_parameter)
self.menu.addAction(ac)
for model_key, model_funcs in parameter.items():
m = QtWidgets.QMenu('Model ' + model_key, self)
for func_name, func_params in model_funcs.items():
m2 = QtWidgets.QMenu(func_name, m)
for p_name, idx in func_params:
ac = QtWidgets.QAction(p_name, m2)
ac.setData((model_key, *idx))
ac.triggered.connect(self.link_parameter)
m2.addAction(ac)
m.addMenu(m2)
self.menu.addMenu(m)
self.toolButton.setMenu(self.menu)
@QtCore.pyqtSlot()
def link_parameter(self, linkto=None):
if linkto is None:
action = self.sender()
else:
action = False
for m in self.menu.actions():
if m.menu():
for a in m.menu().actions():
if a.data() == linkto:
action = a
break
if action:
break
if (self.func_idx, self.parameter_pos) == action.data():
return
try:
new_text = f'Linked to {action.parentWidget().title()}.{action.text()}'
self._linetext = self.parameter_line.text()
self.parameter_line.setText(new_text)
self.parameter_line.setEnabled(False)
self.global_checkbox.hide()
self.global_checkbox.blockSignals(True)
self.global_checkbox.setCheckState(QtCore.Qt.Checked)
self.global_checkbox.blockSignals(False)
self.frame.hide()
self.is_linked = action.data()
except AttributeError:
self.parameter_line.setText(self._linetext)
self.parameter_line.setEnabled(True)
if self.fixed_check.isEnabled():
self.global_checkbox.show()
self.frame.show()
self.is_linked = None
self.state_changed.emit()
class QSaveModelDialog(QtWidgets.QDialog, Ui_SaveDialog): class QSaveModelDialog(QtWidgets.QDialog, Ui_SaveDialog):
@ -227,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())
@ -280,8 +105,17 @@ class FitModelTree(QtWidgets.QTreeWidget):
idx = item.data(0, self.counterRole) idx = item.data(0, self.counterRole)
self.itemRemoved.emit(idx) self.itemRemoved.emit(idx)
def add_function(self, idx: int, cnt: int, op: int, name: str, color: QtGui.QColor | str | tuple, def add_function(self,
parent: QtWidgets.QTreeWidgetItem = None, children: list = None, active: bool = True, **kwargs): idx: int,
cnt: int,
op: int,
name: str,
color: QtGui.QColor | str | tuple,
parent: QtWidgets.QTreeWidgetItem = None,
children: list = None,
active: bool = True,
param_names: list[str] = None,
**kwargs):
""" """
Add function to tree and dictionary of functions. Add function to tree and dictionary of functions.
""" """
@ -292,14 +126,18 @@ 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)
if param_names is not None:
it.setToolTip(0,
'Parameter names:\n' +
'\n'.join(f'{pn}({cnt})' for pn in param_names))
it.setForeground(0, QtGui.QBrush(color)) it.setForeground(0, QtGui.QBrush(color))
it.setIcon(0, get_icon(self.icons[op])) it.setIcon(0, get_icon(self.icons[op]))
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)
@ -319,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:
@ -342,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': []
} }
@ -365,7 +203,7 @@ class FitModelTree(QtWidgets.QTreeWidget):
return funcs return funcs
class FitTableWidget(QtWidgets.QTableWidget): class FitTableWidget(TableWidget):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent=parent) super().__init__(parent=parent)
@ -413,8 +251,8 @@ class FitTableWidget(QtWidgets.QTableWidget):
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)
@ -432,15 +270,15 @@ class FitTableWidget(QtWidgets.QTableWidget):
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] = []
@ -453,8 +291,8 @@ class FitTableWidget(QtWidgets.QTableWidget):
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,22 +1,24 @@
from __future__ import annotations from __future__ import annotations
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 = []
@ -27,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)
@ -62,8 +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):
name = convert(v) widgt = ParameterGlobalWidget(name=v, parent=self.scrollwidget)
widgt = FitModelWidget(label=name, parent=self.scrollwidget)
widgt.parameter_pos = k widgt.parameter_pos = k
widgt.func_idx = idx widgt.func_idx = idx
try: try:
@ -78,11 +78,12 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
widgt.state_changed.connect(self.make_global) widgt.state_changed.connect(self.make_global)
widgt.value_requested.connect(self.look_for_value) widgt.value_requested.connect(self.look_for_value)
widgt.value_changed.connect(self.change_global_parameter) widgt.value_changed.connect(self.change_global_parameter)
widgt.replace_single_value.connect(self.delete_single_parameter)
self.global_parameter.append(widgt) self.global_parameter.append(widgt)
self.scrollwidget.layout().addWidget(widgt) self.scrollwidget.layout().addWidget(widgt)
widgt2 = ParameterSingleWidget(name=name, parent=self.scrollwidget2) widgt2 = ParameterSingleWidget(name=v, parent=self.scrollwidget2)
widgt2.valueChanged.connect(self.change_single_parameter) widgt2.valueChanged.connect(self.change_single_parameter)
widgt2.removeSingleValue.connect(self.change_single_parameter) widgt2.removeSingleValue.connect(self.change_single_parameter)
widgt2.installEventFilter(self) widgt2.installEventFilter(self)
@ -92,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
@ -114,20 +115,22 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
self.scrollwidget.layout().addStretch(1) self.scrollwidget.layout().addStretch(1)
self.scrollwidget2.layout().addStretch(1) self.scrollwidget2.layout().addStretch(1)
def set_links(self, parameter): # def set_links(self, parameter):
for w in self.global_parameter: # for w in self.global_parameter:
if isinstance(w, FitModelWidget): # if isinstance(w, FitModelWidget):
w.add_links(parameter) # w.add_links(parameter)
@QtCore.pyqtSlot(str) @QtCore.pyqtSlot(str)
def change_global_parameter(self, value: str, idx: int = None): def change_global_parameter(self, value: str, idx: int = None):
if idx is None: if idx is None:
idx = self.global_parameter.index(self.sender()) idx = self.global_parameter.index(self.sender())
self.glob_values[idx] = float(value) # self.glob_values[idx] = float(value)
self.glob_values[idx] = value
if self.data_values[self.comboBox.currentData()][idx] is None: if self.data_values[self.comboBox.currentData()][idx] is None:
self.data_parameter[idx].blockSignals(True) self.data_parameter[idx].blockSignals(True)
self.data_parameter[idx].value = float(value) # self.data_parameter[idx].value = float(value)
self.data_parameter[idx].value = value
self.data_parameter[idx].blockSignals(False) self.data_parameter[idx].blockSignals(False)
@QtCore.pyqtSlot(str, object) @QtCore.pyqtSlot(str, object)
@ -148,6 +151,13 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
if value is None: if value is None:
self.change_data(self.comboBox.currentIndex()) self.change_data(self.comboBox.currentIndex())
def delete_single_parameter(self):
idx = self.global_parameter.index(self.sender())
for i in range(self.comboBox.count()):
set_id = self.comboBox.itemData(i)
self.data_values[set_id][idx] = None
self.change_data(self.comboBox.currentIndex())
def change_single_choice(self, _, value, sender=None): def change_single_choice(self, _, value, sender=None):
if sender is None: if sender is None:
sender = self.sender() sender = self.sender()
@ -163,7 +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) and (widget.is_linked is None) 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):
@ -181,6 +191,11 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
for i, value in enumerate(self.data_values[sid]): for i, value in enumerate(self.data_values[sid]):
w = self.data_parameter[i] w = self.data_parameter[i]
w.blockSignals(True) w.blockSignals(True)
try:
w.show_as_local_parameter(value is not None)
except AttributeError:
pass
if value is None: if value is None:
w.value = self.glob_values[i] w.value = self.glob_values[i]
else: else:
@ -191,64 +206,50 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
if sid not in self.data_values: if sid not in self.data_values:
self.data_values[sid] = [None] * len(self.data_parameter) self.data_values[sid] = [None] * len(self.data_parameter)
def get_parameter(self, use_func=None): def get_parameter(self, use_func=None) -> tuple[dict, list]:
bds = [] bds = []
is_global = [] is_global = []
is_fixed = [] is_fixed = []
globs = [] param_general = []
is_linked = []
for g in self.global_parameter: for g in self.global_parameter:
if isinstance(g, FitModelWidget): if isinstance(g, ParameterGlobalWidget):
p_i, bds_i, fixed_i, global_i, link_i = g.get_parameter() p_i, bds_i, fixed_i, global_i = g.get_parameter()
parameter_i = Parameter(name=g.name, value=p_i, lb=bds_i[0], ub=bds_i[1], var=fixed_i)
param_general.append(parameter_i)
globs.append(p_i)
bds.append(bds_i) bds.append(bds_i)
is_fixed.append(fixed_i) is_fixed.append(fixed_i)
is_global.append(global_i) is_global.append(global_i)
is_linked.append(link_i)
lb, ub = list(zip(*bds))
data_parameter = {} data_parameter = {}
if use_func is None: if use_func is None:
use_func = list(self.data_values.keys()) use_func = list(self.data_values.keys())
global_p = None
for sid, parameter in self.data_values.items(): for sid, parameter in self.data_values.items():
if sid not in use_func: if sid not in use_func:
continue continue
kw_p = {} kw_p = {}
p = [] p = []
if global_p is None:
global_p = {'p': [], 'idx': [], 'var': [], 'ub': [], 'lb': []}
for i, (p_i, g) in enumerate(zip(parameter, self.global_parameter)): for i, (p_i, g) in enumerate(zip(parameter, self.global_parameter)):
if isinstance(g, FitModelWidget): if isinstance(g, ParameterGlobalWidget):
if (p_i is None) or is_global[i]: if (p_i is None) or is_global[i]:
p.append(globs[i]) # set has no oen value
if is_global[i]: p.append(param_general[i].copy())
if i not in global_p['idx']:
global_p['p'].append(globs[i])
global_p['idx'].append(i)
global_p['var'].append(is_fixed[i])
global_p['ub'].append(ub[i])
global_p['lb'].append(lb[i])
else: else:
p.append(p_i) lb, ub = bds[i]
try: try:
if p[i] > ub[i]: if not ((lb < p_i < ub) or (not is_fixed[i])):
raise ValueError(f'Parameter {g.name} is outside bounds ({lb[i]}, {ub[i]})') raise ValueError(f'Parameter {g.name} is outside bounds ({lb}, {ub})')
except TypeError: except TypeError:
pass pass
try: # create Parameter
if p[i] < lb[i]: p.append(
raise ValueError(f'Parameter {g.name} is outside bounds ({lb[i]}, {ub[i]})') Parameter(name=g.name, value=p_i, lb=lb, ub=ub, var=is_fixed[i])
except TypeError: )
pass
else: else:
if p_i is None: if p_i is None:
@ -260,20 +261,28 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
data_parameter[sid] = (p, kw_p) data_parameter[sid] = (p, kw_p)
return data_parameter, lb, ub, is_fixed, global_p, is_linked global_parameter = []
for param, global_flag in zip(param_general, is_global):
if global_flag:
param.is_global = True
global_parameter.append(param)
else:
global_parameter.append(None)
return data_parameter, global_parameter
def set_parameter(self, set_id: str | None, parameter: list[float]) -> int: def set_parameter(self, set_id: str | None, parameter: list[float]) -> int:
param_len = len(list(filter(lambda g: not isinstance(g, SelectionWidget), self.global_parameter))) num_parameter = list(filter(lambda g: not isinstance(g, SelectionWidget), self.global_parameter))
param_len = len(num_parameter)
if set_id is None: if set_id is None:
for val, g in zip(parameter, self.global_parameter): for i, g in enumerate(num_parameter):
if isinstance(g, SelectionWidget): val = parameter[i]
continue
g.set_parameter(val) g.set_parameter(val)
self.glob_values[i] = val
else: else:
new_param = self.data_values[set_id] new_param = self.data_values[set_id]
min_len = min(param_len, len(new_param), len(new_param)) min_len = min(param_len, len(new_param))
for i in range(min_len): for i in range(min_len):
new_param[i] = parameter[i] new_param[i] = parameter[i]
@ -291,10 +300,14 @@ 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.parametername.setToolTip(
'If this is bold then this parameter is only for this data. '
'Otherwise, the general parameter is used and displayed'
)
self.value_line.setValidator(QtGui.QDoubleValidator()) # self.value_line.setValidator(QtGui.QDoubleValidator())
self.value_line.textChanged.connect(lambda: self.valueChanged.emit(self.value) if self.value is not None else 0) self.value_line.textChanged.connect(lambda: self.valueChanged.emit(self.value) if self.value is not None else 0)
self.reset_button.clicked.connect(lambda x: self.removeSingleValue.emit()) self.reset_button.clicked.connect(lambda x: self.removeSingleValue.emit())
@ -303,16 +316,18 @@ 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))
self.value_line = QtWidgets.QLineEdit(self) self.value_line = QtWidgets.QLineEdit(self)
self.value_line.textEdited.connect(lambda x: self.show_as_local_parameter(True))
layout.addWidget(self.value_line) layout.addWidget(self.value_line)
self.reset_button = QtWidgets.QToolButton(self) self.reset_button = QtWidgets.QToolButton(self)
self.reset_button.setText('Use global') self.reset_button.setText('Use global')
self.reset_button.clicked.connect(lambda: self.show_as_local_parameter(False))
layout.addWidget(self.reset_button) layout.addWidget(self.reset_button)
self.setLayout(layout) self.setLayout(layout)
@ -326,4 +341,139 @@ class ParameterSingleWidget(QtWidgets.QWidget):
@value.setter @value.setter
def value(self, val): def value(self, val):
self.value_line.setText(f'{float(val):.5g}') # self.value_line.setText(f'{float(val):.5g}')
self.value_line.setText(f'{val}')
self.value_line.setCursorPosition(0)
def show_as_local_parameter(self, is_local: bool):
if is_local:
self.parametername.setStyleSheet('font-weight: bold;')
else:
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,12 +6,14 @@ 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 import get_icon from ..lib.iconloading import get_icon
from .._py.fitfunctionwidget import Ui_Form from .._py.fitfunctionwidget import Ui_Form
from ..Qt import QtWidgets, QtCore, QtGui from ..Qt import QtWidgets, QtCore, QtGui
class QFunctionWidget(QtWidgets.QWidget, Ui_Form): class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
func_cnt = count() func_cnt = count()
func_colors = cycle(Tab10) func_colors = cycle(Tab10)
@ -49,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__
@ -127,7 +138,7 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
self.newFunction.emit(idx, cnt) self.newFunction.emit(idx, cnt)
self.add_function(idx, cnt, op, name, col) self.add_function(idx, cnt, op, name, col, param_names=self.functions[idx].params)
def add_function(self, idx: int, cnt: int, op: int, def add_function(self, idx: int, cnt: int, op: int,
name: str, color: str | tuple[float, float, float] | BaseColor, **kwargs): name: str, color: str | tuple[float, float, float] | BaseColor, **kwargs):
@ -140,6 +151,7 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
qcolor = QtGui.QColor.fromRgbF(*color) qcolor = QtGui.QColor.fromRgbF(*color)
else: else:
qcolor = QtGui.QColor(color) qcolor = QtGui.QColor(color)
self.functree.add_function(idx, cnt, op, name, qcolor, **kwargs) self.functree.add_function(idx, cnt, op, name, qcolor, **kwargs)
f = self.functions[idx] f = self.functions[idx]
@ -153,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
@ -214,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)]
@ -223,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,10 +9,13 @@ 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.model import Model
from nmreval.fit.parameter import Parameters
from nmreval.fit.result import FitResult from nmreval.fit.result import FitResult
from .fit_forms import FitTableWidget from .fit_forms import FitTableWidget
from .fit_parameter import QFitParameterWidget from .fit_parameter import QFitParameterWidget
from ..lib import Relations
from ..lib.pg_objects import PlotItem from ..lib.pg_objects import PlotItem
from ..Qt import QtGui, QtCore, QtWidgets from ..Qt import QtGui, QtCore, QtWidgets
from .._py.fitdialog import Ui_FitDialog from .._py.fitdialog import Ui_FitDialog
@ -38,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)
@ -49,7 +52,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
self._func_list = {} self._func_list = {}
self._complex = {} self._complex = {}
self.connected_figure = '' self.connected_figure = None
self.model_frame.hide() self.model_frame.hide()
self.preview_button.hide() self.preview_button.hide()
@ -74,19 +77,18 @@ 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()
if len(self.functionwidget) == 0: self.get_functions()
self._current_function = None
if len(self.param_widgets) == 0:
# empty model # empty model
self.newmodel_button.setEnabled(False) self.newmodel_button.setEnabled(False)
self.deletemodel_button.setEnabled(False) self.deletemodel_button.setEnabled(False)
self._current_function = None
else:
f_tree = self.functionwidget.functree
func_idx = f_tree.currentItem().data(0, f_tree.counterRole)
self._current_function = self.functionwidget.functions[func_idx]
@QtCore.pyqtSlot(int) @QtCore.pyqtSlot(int)
def show_function_parameter(self, function_id: int, function_idx: int = None): def show_function_parameter(self, function_id: int, function_idx: int = None):
@ -106,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)
@ -120,7 +122,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
# collect parameter names etc. to allow linkage # collect parameter names etc. to allow linkage
self._func_list[self._current_model] = self.functionwidget.get_parameter_list() self._func_list[self._current_model] = self.functionwidget.get_parameter_list()
dialog.set_links(self._func_list) # dialog.set_links(self._func_list)
# show same tab (general parameter/Data parameter) # show same tab (general parameter/Data parameter)
tab_idx = 0 tab_idx = 0
@ -148,6 +150,13 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
Add name and id of dataset to list. Add name and id of dataset to list.
""" """
self.data_table.load(ids) self.data_table.load(ids)
# deselect all fit sets
for i in range(self.data_table.rowCount()):
data_id = self.data_table.item(i, 0).data(QtCore.Qt.ItemDataRole.UserRole+1)
if self._management[data_id].mode == 'fit' or self._management[data_id].has_relation(Relations.isFitPartOf):
self.data_table.item(i, 0).setCheckState(QtCore.Qt.CheckState.Unchecked)
if self.models: if self.models:
for m in self.models.keys(): for m in self.models.keys():
self.data_table.add_model(m) self.data_table.add_model(m)
@ -170,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
@ -184,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)
@ -201,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)
@ -214,59 +221,57 @@ 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': {}, 'lb': (), 'ub': (), 'var': [], parameter = {
'glob': {'idx': [], 'p': [], 'var': [], 'lb': [], 'ub': []}, 'data_parameter': {},
'links': [], 'color': []} 'global_parameter': [],
'links': [],
'color': [],
}
for i, f in enumerate(model): for i, f in enumerate(model):
if not f['active']: if not f['active']:
continue continue
try: try:
p, lb, ub, var, glob, links = self.param_widgets[f['cnt']].get_parameter(function_use) p, glob = self.param_widgets[f['cnt']].get_parameter(function_use)
except ValueError as e: except ValueError as e:
_ = QtWidgets.QMessageBox().warning(self, 'Invalid value', str(e), _ = QtWidgets.QMessageBox().warning(self, 'Invalid value', str(e),
QtWidgets.QMessageBox.Ok) QtWidgets.QMessageBox.Ok)
return {}, -1 return {}, -1
p_len = len(parameter['lb']) parameter['color'].append(f['color'])
parameter['global_parameter'].extend(glob)
parameter['lb'] += lb
parameter['ub'] += ub
parameter['var'] += var
parameter['links'] += links
parameter['color'] += [f['color']]
cnt = f['cnt'] cnt = f['cnt']
for p_k, v_k in p.items(): for p_k, v_k in p.items():
if add_idx: if add_idx:
kw_k = {f'{k}_{cnt}': v for k, v in v_k[1].items()} kw_k = {f'{k}_{cnt}': v for k, v in v_k[1].items()}
else: else:
kw_k = v_k[1] kw_k = v_k[1]
if p_k in parameter['parameter']: if p_k in parameter['data_parameter']:
params, kw = parameter['parameter'][p_k] params, kw = parameter['data_parameter'][p_k]
params += v_k[0] params += v_k[0]
kw.update(kw_k) kw.update(kw_k)
else: else:
parameter['parameter'][p_k] = (v_k[0], kw_k) parameter['data_parameter'][p_k] = (v_k[0], kw_k)
for g_k, g_v in glob.items():
if g_k != 'idx':
parameter['glob'][g_k] += g_v
else:
parameter['glob']['idx'] += [idx_i + p_len for idx_i in g_v]
if add_idx: if add_idx:
cnt += 1 cnt += 1
if f['children']: if f['children']:
# recurse for children # recurse for children
child_parameter, cnt = self._prepare(f['children'], parameter=parameter, add_idx=add_idx, cnt=cnt) _, cnt = self._prepare(f['children'], parameter=parameter, function_use=function_use, add_idx=add_idx, cnt=cnt)
return parameter, cnt return parameter, cnt
@ -277,30 +282,46 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
data = self.data_table.collect_data(default=self.default_combobox.currentData()) data = self.data_table.collect_data(default=self.default_combobox.currentData())
func_dict = {} func_dict = {}
for k, mod in self.models.items(): for model_name, model_parameter in self.models.items():
func, order, param_len = ModelFactory.create_from_list(mod) func, order, param_len, _ = ModelFactory.create_from_list(model_parameter)
multiple_funcs = isinstance(func, MultiModel)
if func is None: if func is None:
continue continue
if k in data: func = Model(func)
parameter, _ = self._prepare(mod, function_use=data[k], add_idx=isinstance(func, MultiModel))
if parameter is None: if model_name in data:
parameter, _ = self._prepare(model_parameter, function_use=data[model_name], add_idx=multiple_funcs)
if parameter is None or not isinstance(parameter, dict):
return return
if ('data_parameter' not in parameter) or ('global_parameter' not in parameter):
return
for (data_parameter, _) in parameter['data_parameter'].values():
for pname, param in zip(func.params, data_parameter):
param.name = pname
if self._complex[model_name] is not None:
for p_k, p_v in parameter['data_parameter'].items():
p_v[1].update({'complex_mode': self._complex[model_name]})
parameter['data_parameter'][p_k] = p_v[0], p_v[1]
for pname, param_value in zip(func.params, parameter['global_parameter']):
if param_value is not None:
param_value.name = pname
func.set_global_parameter(param_value)
parameter['func'] = func parameter['func'] = func
parameter['order'] = order parameter['order'] = order
parameter['len'] = param_len parameter['len'] = param_len
parameter['complex'] = self._complex[k] parameter['complex'] = self._complex[model_name]
if self._complex[k] is not None:
for p_k, p_v in parameter['parameter'].items():
p_v[1].update({'complex_mode': self._complex[k]})
parameter['parameter'][p_k] = p_v[0], p_v[1]
func_dict[k] = parameter func_dict[model_name] = parameter
replaceable = [] replaceable = []
for k, v in func_dict.items(): for model_name, v in func_dict.items():
for i, link_i in enumerate(v['links']): for i, link_i in enumerate(v['links']):
if link_i is None: if link_i is None:
continue continue
@ -331,7 +352,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
QtWidgets.QMessageBox.Ok) QtWidgets.QMessageBox.Ok)
return return
replaceable.append((k, i, rep_model, repl_idx)) replaceable.append((model_name, i, rep_model, repl_idx))
replace_value = None replace_value = None
for p_k in f['parameter'].values(): for p_k in f['parameter'].values():
@ -374,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:
@ -409,31 +430,37 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
def make_previews(self, x, models_parameters: dict): def make_previews(self, x, models_parameters: dict):
self.preview_lines = [] self.preview_lines = []
# needed to create namespace
param_dict = Parameters()
cnt = 0
for model in models_parameters.values():
f = model['func']
for parameter_list in model['data_parameter'].values():
for i, p_value in enumerate(parameter_list[0]):
p_value.name = f.params[i]
param_dict.add_parameter(f'a{cnt}', p_value)
cnt += 1
for k, model in models_parameters.items(): for k, model in models_parameters.items():
f = model['func'] f = model['func']
is_complex = self._complex[k] is_complex = self._complex[k]
parameters = model['parameter'] parameters = model['data_parameter']
color = model['color'] color = model['color']
seen_parameter = []
for p, kwargs in parameters.values(): for p, kwargs in parameters.values():
if (p, kwargs) in seen_parameter: p_value = [pp.value for pp in p]
# plot only previews with different parameter
continue
seen_parameter.append((p, kwargs))
if is_complex is not None: if is_complex is not None:
y = f.func(x, *p, complex_mode=is_complex, **kwargs) y = f.func(x, *p_value, complex_mode=is_complex, **kwargs)
if np.iscomplexobj(y): if np.iscomplexobj(y):
self.preview_lines.append(PlotItem(x=x, y=y.real, pen=mkPen(width=3))) self.preview_lines.append(PlotItem(x=x, y=y.real, pen=mkPen(width=3)))
self.preview_lines.append(PlotItem(x=x, y=y.imag, pen=mkPen(width=3))) self.preview_lines.append(PlotItem(x=x, y=y.imag, pen=mkPen(width=3)))
else: else:
self.preview_lines.append(PlotItem(x=x, y=y, pen=mkPen(width=3))) self.preview_lines.append(PlotItem(x=x, y=y, pen=mkPen(width=3)))
else: else:
y = f.func(x, *p, **kwargs) y = f.func(x, *p_value, **kwargs)
self.preview_lines.append(PlotItem(x=x, y=y, pen=mkPen(width=3))) self.preview_lines.append(PlotItem(x=x, y=y, pen=mkPen(width=3)))
if isinstance(f, MultiModel): if isinstance(f, MultiModel):
@ -441,7 +468,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
if is_complex is not None: if is_complex is not None:
sub_kwargs.update({'complex_mode': is_complex}) sub_kwargs.update({'complex_mode': is_complex})
for i, s in enumerate(f.subs(x, *p, **sub_kwargs)): for i, s in enumerate(f.subs(x, *p_value, **sub_kwargs)):
pen_i = mkPen(QtGui.QColor.fromRgbF(*color[i])) pen_i = mkPen(QtGui.QColor.fromRgbF(*color[i]))
if np.iscomplexobj(s): if np.iscomplexobj(s):
self.preview_lines.append(PlotItem(x=x, y=s.real, pen=pen_i)) self.preview_lines.append(PlotItem(x=x, y=s.real, pen=pen_i))
@ -449,15 +476,17 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
else: else:
self.preview_lines.append(PlotItem(x=x, y=s, pen=pen_i)) self.preview_lines.append(PlotItem(x=x, y=s, pen=pen_i))
param_dict.clear()
return self.preview_lines return self.preview_lines
def set_parameter(self, parameter: dict[str, FitResult]): def set_parameter(self, parameter: dict[str, FitResult]):
# which data uses which model # which data uses which model
data = self.data_table.collect_data(default=self.default_combobox.currentData()) data = self.data_table.collect_data(default=self.default_combobox.currentData())
for fitted_model, fitted_data in data.items():
glob_fit_parameter = [] glob_fit_parameter = []
for fitted_model, fitted_data in data.items():
for fit_id, fit_curve in parameter.items(): for fit_id, fit_curve in parameter.items():
if fit_id in fitted_data: if fit_id in fitted_data:
fit_parameter = list(fit_curve.parameter.values()) fit_parameter = list(fit_curve.parameter.values())
@ -474,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,13 +8,13 @@ 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']
validator = QtGui.QRegExpValidator(QtCore.QRegExp('[A-Za-z]\S*')) validator = QtGui.QRegExpValidator(QtCore.QRegExp('[_A-Za-z][_A-Za-z0-9]*'))
pattern = re.compile(r'def func\(.*\):', flags=re.MULTILINE) pattern = re.compile(r'def func\(.*\):', flags=re.MULTILINE)
@ -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]}')
@ -145,6 +148,7 @@ class QUserFitCreator(QtWidgets.QDialog, Ui_Dialog):
self.classCreated.emit() self.classCreated.emit()
super().accept() super().accept()
class KwargsWidget(QtWidgets.QWidget): class KwargsWidget(QtWidgets.QWidget):
Changed = QtCore.pyqtSignal() Changed = QtCore.pyqtSignal()
@ -209,12 +213,12 @@ class KwargsWidget(QtWidgets.QWidget):
def get_strings(self) -> str: def get_strings(self) -> str:
kwargs = [] kwargs = []
if self.use_nuclei.isChecked(): if self.use_nuclei.isChecked():
kwargs.append("(r'\gamma', 'nucleus', gamma)") kwargs.append(r"(r'\gamma', 'nucleus', gamma)")
for i in range(self.choices.count()): for i in range(self.choices.count()):
kwargs.append(self.choices.widget(i).get_strings()) kwargs.append(self.choices.widget(i).get_strings())
if kwargs: if kwargs:
return f" choices = {', '.join(kwargs)}\n" return f" choices = [{', '.join(kwargs)}]\n"
else: else:
return '' return ''
@ -300,7 +304,7 @@ class ChoiceWidget(QtWidgets.QWidget):
def get_strings(self) -> str: def get_strings(self) -> str:
opts = [] opts = []
for i in range(self.table.rowCount()): for i in range(self.table.rowCount()):
name = self.table.item(i, 0).text() name = self.table.cellWidget(i, 0).text()
val = self._make_value(i) val = self._make_value(i)
opts.append(f'{name!r}: {val!r}') opts.append(f'{name!r}: {val!r}')
@ -471,15 +475,6 @@ class DescWidget(QtWidgets.QWidget):
stringi = f'class {self.klass_lineedit.text()}:\n' \ stringi = f'class {self.klass_lineedit.text()}:\n' \
f' name = {self.name_lineedit.text()!r}\n' \ f' name = {self.name_lineedit.text()!r}\n' \
f' type = {self.group_lineedit.text()!r}\n' \ f' type = {self.group_lineedit.text()!r}\n' \
f' equation = {self.eq_lineedit.text()!r}\n' f" equation = r'{self.eq_lineedit.text()}'\n"
return stringi return stringi
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication([])
win = QUserFitCreator()
win.show()
sys.exit(app.exec())

View File

@ -1,8 +1,12 @@
from __future__ import annotations
from math import isnan from math import isnan
from pyqtgraph import mkBrush 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.utils import RdBuCMap from ..lib.utils import RdBuCMap
from ..Qt import QtWidgets, QtGui, QtCore from ..Qt import QtWidgets, QtGui, QtCore
@ -14,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)
@ -26,71 +30,98 @@ 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._prevs = {} self._previous_fits = {}
self._models = {} self._opts = []
self._results = {}
self.graph_opts = {}
self.last_idx = None
self.func_colors = sub_colors
self.fit_plot = self.graphicsView.addPlot(row=1, col=0, title='Fit')
self.resid_plot = self.graphicsView.addPlot(row=0, col=0, title='Residual')
for orient in ['top', 'bottom', 'left', 'right']:
self.fit_plot.getAxis(orient).logTickValues = logTickValues
self.resid_plot.getAxis(orient).logTickValues = logTickValues
self.graphicsView.ci.layout.setRowStretchFactor(0, 1)
self.graphicsView.ci.layout.setRowStretchFactor(1, 2)
self.resid_graph = PlotItem(
x=[], y=[],
symbol='o', symbolPen=None, symbolBrush=mkBrush(color=(31, 119, 180)),
pen=None
)
self.resid_graph_imag = PlotItem(
x=[], y=[],
symbol='s', symbolPen=None, symbolBrush=mkBrush(color=(255, 127, 14)),
pen=None
)
self.resid_plot.addItem(self.resid_graph)
self.resid_plot.addItem(self.resid_graph_imag)
self.data_graph = PlotItem(
x=[], y=[],
symbol='o', symbolPen=None, symbolBrush=mkBrush(color=(31, 119, 180)),
pen=None
)
self.data_graph_imag = PlotItem(
x=[], y=[],
symbol='s', symbolPen=None, symbolBrush=mkBrush(color=(255, 127, 14)),
pen=None
)
self.fit_plot.addItem(self.data_graph)
self.fit_plot.addItem(self.data_graph_imag)
self.fit_graph = PlotItem(x=[], y=[])
self.fit_graph_imag = PlotItem(x=[], y=[])
self.fit_plot.addItem(self.fit_graph)
self.fit_plot.addItem(self.fit_graph_imag)
self.cmap = RdBuCMap(vmin=-1, vmax=1)
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.logx_box.stateChanged.connect(lambda x: self.fit_plot.setLogMode(x=bool(x)))
self.resid_plot.setXLink(self.fit_plot)
self.buttonGroup.buttonToggled.connect(self._plot_residuals)
self.set_results(results)
def __call__(self, results: list, sub_colors: dict):
self._previous_fits = {}
self.sets_comboBox.blockSignals(True)
self.sets_comboBox.clear()
self.sets_comboBox.blockSignals(False)
self._results = {}
self._opts = {}
self.func_colors = sub_colors
self.set_results(results)
def set_results(self, results: list):
self.sets_comboBox.blockSignals(True)
for res in results: for res in results:
idx = res.idx idx = res.idx
data_k = management.data[idx] data_k = self._management.data[idx]
if res.name not in self._models: self._previous_fits[idx] = []
self._models[res.name] = []
self._models[res.name].append(idx)
self._prevs[idx] = []
for fit in data_k.get_fits(): for fit in data_k.get_fits():
self._prevs[idx].append((fit.name, fit.statistics, fit.nobs-fit.nvar)) self._previous_fits[idx].append((fit.name, fit.statistics, fit.nobs - fit.nvar))
self.sets_comboBox.addItem(data_k.name, userData=idx)
self.sets_comboBox.blockSignals(False)
self._results = {res.idx: res for res in results} self._results = {res.idx: res for res in results}
self._opts = [(False, False) for _ in range(len(self._results))] self._opts = [(False, False) for _ in range(len(self._results))]
self.residplot = self.graphicsView.addPlot(row=0, col=0)
self.resid_graph = PlotItem(x=[], y=[],
symbol='o', symbolPen=None, symbolBrush=mkBrush(color=(31, 119, 180)),
pen=None)
self.resid_graph_imag = PlotItem(x=[], y=[],
symbol='s', symbolPen=None, symbolBrush=mkBrush(color=(255, 127, 14)),
pen=None)
self.residplot.addItem(self.resid_graph)
self.residplot.addItem(self.resid_graph_imag)
self.residplot.setLabel('left', 'Residual')
self.fitplot = self.graphicsView.addPlot(row=1, col=0)
self.data_graph = PlotItem(x=[], y=[],
symbol='o', symbolPen=None, symbolBrush=mkBrush(color=(31, 119, 180)),
pen=None)
self.data_graph_imag = PlotItem(x=[], y=[],
symbol='s', symbolPen=None, symbolBrush=mkBrush(color=(255, 127, 14)),
pen=None)
self.fitplot.addItem(self.data_graph)
self.fitplot.addItem(self.data_graph_imag)
self.fitplot.setLabel('left', 'Function')
self.fit_graph = PlotItem(x=[], y=[])
self.fit_graph_imag = PlotItem(x=[], y=[])
self.fitplot.addItem(self.fit_graph)
self.fitplot.addItem(self.fit_graph_imag)
self.cmap = RdBuCMap(vmin=-1, vmax=1)
self.sets_comboBox.blockSignals(True)
for n in self._models.keys():
self.sets_comboBox.addItem(n)
self.sets_comboBox.blockSignals(False)
self.set_parameter(0) self.set_parameter(0)
self.buttonBox.accepted.connect(self.accept)
self.param_tableWidget.itemClicked.connect(self.show_results)
self.param_tableWidget.horizontalHeader().sectionClicked.connect(lambda i: self.show_results(None, idx=i))
self.graph_checkBox.stateChanged.connect(lambda x: self.graph_comboBox.setEnabled(x == QtCore.Qt.Unchecked))
self.logy_box.stateChanged.connect(lambda x: self.fitplot.setLogMode(y=bool(x)))
self.logx_box.stateChanged.connect(lambda x: self.fitplot.setLogMode(x=bool(x)))
self.residplot.setXLink(self.fitplot)
def add_graphs(self, graphs: list): def add_graphs(self, graphs: list):
self.graph_comboBox.clear() self.graph_comboBox.clear()
@ -99,56 +130,40 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
@QtCore.pyqtSlot(int, name='on_sets_comboBox_currentIndexChanged') @QtCore.pyqtSlot(int, name='on_sets_comboBox_currentIndexChanged')
def set_parameter(self, idx: int): def set_parameter(self, idx: int):
model_name = self.sets_comboBox.itemText(idx) set_id = self.sets_comboBox.itemData(idx, QtCore.Qt.ItemDataRole.UserRole)
sets = self._models[model_name]
self.param_tableWidget.setColumnCount(len(sets))
r = self._results[sets[0]]
self.param_tableWidget.setRowCount(len(r.parameter))
for i, pval in enumerate(r.parameter.values()):
name = pval.full_name
p_header = QtWidgets.QTableWidgetItem(convert(name, 'tex', 'html', brackets=False))
self.param_tableWidget.setVerticalHeaderItem(i, p_header)
for i, set_id in enumerate(sets):
data_i = self._management[set_id]
header_item = QtWidgets.QTableWidgetItem(data_i.name)
header_item.setData(QtCore.Qt.UserRole, set_id)
self.param_tableWidget.setHorizontalHeaderItem(i, header_item)
res = self._results[set_id] res = self._results[set_id]
for j, pvalue in enumerate(res.parameter.values()): self.param_tableWidget.setRowCount(len(res.parameter))
for j, (pkey, pvalue) in enumerate(res.parameter.items()):
name = pkey
p_header = QtWidgets.QTableWidgetItem(convert(name, 'tex', 'str', brackets=True))
self.param_tableWidget.setVerticalHeaderItem(j, p_header)
item_text = f'{pvalue.value:.4g}' item_text = f'{pvalue.value:.4g}'
if pvalue.error is not None: if pvalue.error is not None:
item_text += f' \u00b1 {pvalue.error:.4g}' item_text += f' \u00b1 {pvalue.error:.4g}'
self.param_tableWidget.setItem(2*j+1, i, QtWidgets.QTableWidgetItem('-')) self.param_tableWidget.setItem(2*j+1, 0, QtWidgets.QTableWidgetItem('-'))
else: else:
self.param_tableWidget.setItem(2*j+1, i, QtWidgets.QTableWidgetItem()) self.param_tableWidget.setItem(2*j+1, 0, QtWidgets.QTableWidgetItem())
item = QtWidgets.QTableWidgetItem(item_text) item = QtWidgets.QTableWidgetItem(item_text)
self.param_tableWidget.setItem(j, i, item) self.param_tableWidget.setItem(j, 0, item)
self.param_tableWidget.resizeColumnsToContents() self.param_tableWidget.resizeColumnToContents(0)
self.param_tableWidget.selectColumn(0) self.show_results(idx)
self.show_results(None, idx=0)
@QtCore.pyqtSlot(int, name='on_reject_fit_checkBox_stateChanged') @QtCore.pyqtSlot(int, name='on_reject_fit_checkBox_stateChanged')
@QtCore.pyqtSlot(int, name='on_del_prev_checkBox_stateChanged') @QtCore.pyqtSlot(int, name='on_del_prev_checkBox_stateChanged')
def change_opts(self, _): def change_opts(self, _):
idx = self.param_tableWidget.currentIndex().column() idx = self.sets_comboBox.currentIndex()
self._opts[idx] = (self.reject_fit_checkBox.checkState() == QtCore.Qt.Checked, self._opts[idx] = (self.reject_fit_checkBox.checkState() == QtCore.Qt.CheckState.Checked,
self.del_prev_checkBox.checkState() == QtCore.Qt.Checked) self.del_prev_checkBox.checkState() == QtCore.Qt.CheckState.Checked)
def show_results(self, item, idx=None): def show_results(self, idx):
if item is not None: set_id = self.sets_comboBox.itemData(idx, QtCore.Qt.ItemDataRole.UserRole)
idx = self.param_tableWidget.indexFromItem(item).column()
set_id = self.param_tableWidget.horizontalHeaderItem(idx).data(QtCore.Qt.UserRole)
self.set_plot(set_id) self.set_plot(set_id)
self.set_correlation(set_id) self.set_correlation(set_id)
self.set_statistics(set_id) self.set_statistics(set_id)
self.reject_fit_checkBox.blockSignals(True) self.reject_fit_checkBox.blockSignals(True)
self.reject_fit_checkBox.setChecked(self._opts[idx][0]) self.reject_fit_checkBox.setChecked(self._opts[idx][0])
self.reject_fit_checkBox.blockSignals(False) self.reject_fit_checkBox.blockSignals(False)
@ -156,27 +171,97 @@ 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:
self.graph_opts[self.last_idx] = (
self.fit_plot.viewRange(),
self.logx_box.isChecked(),
self.logy_box.isChecked(),
)
self.last_idx = idx
res = self._results[idx] res = self._results[idx]
iscomplex = res.iscomplex iscomplex = res.iscomplex
sub_funcs = res.sub(res.x)
for item in self.fit_plot.items[::-1]:
if item not in [self.data_graph, self.data_graph_imag, self.fit_graph, self.fit_graph_imag]:
self.fit_plot.removeItem(item)
if iscomplex: if iscomplex:
self.data_graph.setData(x=res.x_data, y=res.y_data.real) self.data_graph.setData(x=res.x_data, y=res.y_data.real)
self.data_graph_imag.setData(x=res.x_data, y=res.y_data.imag) self.data_graph_imag.setData(x=res.x_data, y=res.y_data.imag)
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 f, c in zip(sub_funcs, self.func_colors[idx]):
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)
item = PlotItem(x=f.x, y=f.y.imag, pen=mkPen({'color': col, 'style': 2}))
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=[])
self.fitplot.setLogMode(x=res.islog) for f, c in zip(sub_funcs, self.func_colors[idx]):
self.residplot.setLogMode(x=res.islog) 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._plot_residuals(idx)
self.logx_box.blockSignals(True)
self.logx_box.setChecked(res.islog)
self.logx_box.blockSignals(False)
self.fit_plot.setLogMode(x=res.islog)
self.resid_plot.setLogMode(x=res.islog)
if idx in self.graph_opts:
view_range, logx, logy = self.graph_opts[idx]
self.fit_plot.setRange(xRange=view_range[0], yRange=view_range[1], padding=0)
self.fit_plot.setLogMode(x=logx, y=logy)
self.logx_box.blockSignals(True)
self.logx_box.setChecked(logx)
self.logx_box.blockSignals(False)
self.logy_box.blockSignals(True)
self.logy_box.setChecked(logy)
self.logy_box.blockSignals(False)
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():
@ -207,35 +292,35 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
res = self._results[idx] res = self._results[idx]
self.stats_tableWidget.setColumnCount(1 + len(self._prevs[idx])) self.stats_tableWidget.setColumnCount(1 + len(self._previous_fits[idx]))
self.stats_tableWidget.setRowCount(len(res.statistics)+3) self.stats_tableWidget.setRowCount(len(res.statistics)+3)
it = QtWidgets.QTableWidgetItem(f'{res.dof}') it = QtWidgets.QTableWidgetItem(f'{res.dof}')
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._prevs[idx], start=1): for col, (name, _, dof) in enumerate(self._previous_fits[idx], start=1):
self.stats_tableWidget.setHorizontalHeaderItem(0, QtWidgets.QTableWidgetItem(name)) self.stats_tableWidget.setHorizontalHeaderItem(0, QtWidgets.QTableWidgetItem(name))
it = QtWidgets.QTableWidgetItem(f'{dof}') it = QtWidgets.QTableWidgetItem(f'{dof}')
it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable) it.setFlags(it.flags() ^ QtCore.Qt.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
best_val = v best_val = v
for col, (_, stats, _) in enumerate(self._prevs[idx], start=1): for col, (_, stats, _) in enumerate(self._previous_fits[idx], start=1):
if k in ['adj. R^2', 'R^2']: if k in ['adj. R^2', 'R^2']:
best_idx = col if best_val < stats[k] else max(0, best_idx) best_idx = col if best_val < stats[k] else max(0, best_idx)
else: else:
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:
@ -249,14 +334,14 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
self.stats_tableWidget.setVerticalHeaderItem(row+1, QtWidgets.QTableWidgetItem('Pr(>F)')) self.stats_tableWidget.setVerticalHeaderItem(row+1, QtWidgets.QTableWidgetItem('Pr(>F)'))
self.stats_tableWidget.setItem(row+1, 0, QtWidgets.QTableWidgetItem('-')) self.stats_tableWidget.setItem(row+1, 0, QtWidgets.QTableWidgetItem('-'))
for col, (_, stats, dof) in enumerate(self._prevs[idx], start=1): for col, (_, stats, dof) in enumerate(self._previous_fits[idx], start=1):
f_value, prob_f = res.f_test(stats['chi^2'], dof) f_value, prob_f = res.f_test(stats['chi^2'], dof)
it = QtWidgets.QTableWidgetItem(f'{f_value:.4g}') it = QtWidgets.QTableWidgetItem(f'{f_value:.4g}')
it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable) it.setFlags(it.flags() ^ QtCore.Qt.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'))
@ -272,32 +357,37 @@ 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.CheckState.Checked
parts = self.partial_checkBox.checkState() == QtCore.Qt.Checked extrapolate = [None, None, None, None]
error = []
extrapolate = [None, None, None]
if self.extrapolate_box.isChecked(): if self.extrapolate_box.isChecked():
try: try:
extrapolate[0] = float(self.minx_line.text()) extrapolate[0] = float(self.minx_line.text())
except TypeError: except (TypeError, ValueError):
pass error.append('Start value is missing')
try: try:
extrapolate[1] = float(self.maxx_line.text()) extrapolate[1] = float(self.maxx_line.text())
except TypeError: except (TypeError, ValueError):
pass error.append('End value is missing')
try: try:
extrapolate[2] = int(self.numx_line.text()) extrapolate[2] = int(self.numx_line.text())
except TypeError: except (TypeError, ValueError):
pass error.append('Number of points is missing')
extrapolate[3] = self.newx_log_checkbox.isChecked()
if error:
msg = QtWidgets.QMessageBox.warning(self, 'Error', 'Extrapolation failed because:\n' + '\n'.join(error))
return
else:
self.closed.emit(self._results, self._opts, graph, plot_fits, parts, extrapolate) self.closed.emit(self._results, self._opts, graph, plot_fits, parts, extrapolate)
self.accept() self.accept()
@ -331,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)
@ -342,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

@ -138,9 +138,7 @@ class DrawingsWidget(QtWidgets.QWidget, Ui_Form):
graph_id = self.graph_comboBox.currentData() graph_id = self.graph_comboBox.currentData()
current_lines = self.lines[graph_id] current_lines = self.lines[graph_id]
print(remove_rows)
for i in reversed(remove_rows): for i in reversed(remove_rows):
print(i)
self.tableWidget.removeRow(i) self.tableWidget.removeRow(i)
self.line_deleted.emit(current_lines[i], graph_id) self.line_deleted.emit(current_lines[i], graph_id)

View File

@ -4,19 +4,21 @@ import itertools
import os import os
import uuid import uuid
from math import isnan from math import isfinite
from pathlib import Path from pathlib import Path
import numpy as np
from numpy import errstate, floor, log10 from numpy import errstate, floor, log10
from pyqtgraph import GraphicsObject, getConfigOption, mkColor from pyqtgraph import GraphicsObject, getConfigOption, mkColor
from nmreval.lib.logger import logger
from nmreval.utils.text import convert from nmreval.utils.text import convert
from ..io.filedialog import FileDialog from ..io.filedialog import FileDialog
from ..lib.pg_objects import LegendItemBlock, RegionItem from ..lib.pg_objects import LegendItemBlock, RegionItem
from ..Qt import QtCore, QtWidgets, QtGui from ..Qt import QtCore, QtWidgets, QtGui
from .._py.graph import Ui_GraphWindow from .._py.graph import Ui_GraphWindow
from ..lib import make_action_icons from ..lib.iconloading import make_action_icons
from ..lib.configurations import GraceMsgBox from ..lib.configurations import GraceMsgBox
@ -24,7 +26,8 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
mousePositionChanged = QtCore.pyqtSignal(float, float) mousePositionChanged = QtCore.pyqtSignal(float, float)
mouseDoubleClicked = QtCore.pyqtSignal() mouseDoubleClicked = QtCore.pyqtSignal()
positionClicked = QtCore.pyqtSignal(tuple, bool) positionClicked = QtCore.pyqtSignal(tuple, bool)
aboutToClose = QtCore.pyqtSignal(str) aboutToClose = QtCore.pyqtSignal(list)
newData = QtCore.pyqtSignal(list, str)
counter = itertools.count() counter = itertools.count()
@ -43,7 +46,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
self.id = str(uuid.uuid4()) self.id = str(uuid.uuid4())
self.sets = [] self.sets = []
self.active = [] self._active = []
self.real_plots = {} self.real_plots = {}
self.imag_plots = {} self.imag_plots = {}
@ -53,12 +56,24 @@ 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.log = [False, False] self.log = [False, False]
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))
@ -71,6 +86,11 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
self.scene.contextMenu[0].disconnect() self.scene.contextMenu[0].disconnect()
self.scene.contextMenu[0].triggered.connect(self.export_dialog) self.scene.contextMenu[0].triggered.connect(self.export_dialog)
self.bwbutton.toggled.connect(self.change_background)
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)))
@ -103,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
@ -110,11 +158,11 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
return iter(self.active) return iter(self.active)
def __len__(self): def __len__(self):
return len(self.active) return len(self._active)
def curves(self) -> tuple: def curves(self) -> tuple:
for set_id in self.sets: for set_id in self.sets:
if set_id in self.active: if set_id in self._active:
if self.real_button.isChecked(): if self.real_button.isChecked():
if self.error_plots[set_id] is not None: if self.error_plots[set_id] is not None:
yield self.real_plots[set_id], self.error_plots[set_id] yield self.real_plots[set_id], self.error_plots[set_id]
@ -137,19 +185,44 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
r = self.plotItem.getViewBox().viewRange() r = self.plotItem.getViewBox().viewRange()
for i in [0, 1]: for i in [0, 1]:
if self.log[i]: if self.log[i]:
r[i] = tuple([10**x for x in r[i]]) tmp = [np.nan, np.nan]
for j, x in enumerate(r[i]):
try:
tmp[j] = 10**min(x, 199)
except OverflowError:
pass
r[i] = tuple(tmp)
else: else:
r[i] = tuple(r[i]) r[i] = tuple(r[i])
return tuple(r) return tuple(r)
@property
def active(self) -> list:
return [set_id for set_id in self.sets if set_id in self._active]
@active.setter
def active(self, value: list):
self._active = value
def block(self, state: bool):
self._block = state
if not self._block:
# self.graphic.enableAutoRange()
self._update_zorder()
self.show_legend()
else:
self.graphic.disableAutoRange()
def add(self, name: str | list, plots: list): def add(self, name: str | list, plots: list):
if isinstance(name, str): if isinstance(name, str):
name = [name] name = [name]
plots = [plots] plots = [plots]
for (real_plot, imag_plot, err_plot), n in zip(plots, name):
toplevel = len(self.sets) toplevel = len(self.sets)
self.listWidget.blockSignals(True)
for (real_plot, imag_plot, err_plot), n in zip(plots, name):
self.sets.append(n) self.sets.append(n)
if real_plot: if real_plot:
@ -164,12 +237,25 @@ 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
self.listWidget.blockSignals(False)
if len(name) < 200:
self.show_item(name) self.show_item(name)
else:
QtWidgets.QMessageBox.warning(self, 'Display disabled',
'If more than 200 sets are added at once, they are not displayed to avoid major performance issues.\n'
'The checkmark in the data tree is invalid.\n'
'Please display them manually in smaller batches, thank you!')
def remove(self, name: str | list): def remove(self, name: str | list):
if isinstance(name, str): if isinstance(name, str):
@ -181,19 +267,20 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
for plot in [self.real_plots, self.imag_plots, self.error_plots]: for plot in [self.real_plots, self.imag_plots, self.error_plots]:
self.graphic.removeItem(plot[n]) self.graphic.removeItem(plot[n])
if n in self.active: if n in self._active:
self.active.remove(n) self._active.remove(n)
# remove from label list # remove from label list
self.listWidget.blockSignals(True) self.listWidget.blockSignals(True)
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)
if not self._block:
self._update_zorder() self._update_zorder()
self.show_legend() self.show_legend()
@ -225,8 +312,8 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
return return
for a in idlist: for a in idlist:
if a not in self.active: if a not in self._active:
self.active.append(a) self._active.append(a)
for (bttn, plot_dic) in [ for (bttn, plot_dic) in [
(self.real_button, self.real_plots), (self.real_button, self.real_plots),
@ -245,14 +332,16 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
return return
for r in idlist: for r in idlist:
if r in self.active: if r in self._active:
self.active.remove(r) self._active.remove(r)
for plt in [self.real_plots, self.imag_plots, self.error_plots]: for plt in [self.real_plots, self.imag_plots, self.error_plots]:
item = plt[r] item = plt[r]
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):
@ -268,7 +357,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
else: else:
func = self.graphic.removeItem func = self.graphic.removeItem
for a in self.active: for a in self._active:
item = plots[a] item = plots[a]
if item is not None: if item is not None:
func(item) func(item)
@ -285,12 +374,12 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
return return
if visible: if visible:
for a in self.active: for a in self._active:
item = self.error_plots[a] item = self.error_plots[a]
if (item is not None) and (item not in self.graphic.items()): if (item is not None) and (item not in self.graphic.items()):
self.graphic.addItem(item) self.graphic.addItem(item)
else: else:
for a in self.active: for a in self._active:
item = self.error_plots[a] item = self.error_plots[a]
if (item is not None) and (item in self.graphic.items()): if (item is not None) and (item in self.graphic.items()):
self.graphic.removeItem(item) self.graphic.removeItem(item)
@ -354,7 +443,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
if res == QtWidgets.QMessageBox.Yes: if res == QtWidgets.QMessageBox.Yes:
self.aboutToClose.emit(self.id) self.aboutToClose.emit([self.id])
evt.accept() evt.accept()
else: else:
evt.ignore() evt.ignore()
@ -380,28 +469,44 @@ 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
self.plotItem.setXRange(*r[0])
self.plotItem.setYRange(*r[1])
if xmode is None: if xmode is None:
xmode = self.plotItem.ctrl.logXCheck.isChecked() xmode = self.plotItem.ctrl.logXCheck.isChecked()
else: else:
@ -418,8 +523,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
item.logmode[0] = self.log[:] item.logmode[0] = self.log[:]
self.plotItem.updateLogMode() self.plotItem.updateLogMode()
self.set_range(x=r[0], y=r[1])
self.plotItem.enableAutoRange()
def enable_picking(self, enabled: bool): def enable_picking(self, enabled: bool):
if enabled: if enabled:
@ -435,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]:
@ -460,9 +568,9 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
with errstate(all='ignore'): with errstate(all='ignore'):
xy = [log10(val) for val in xy] xy = [log10(val) for val in xy]
if isnan(xy[1]): if not isfinite(xy[1]):
xy = [-1, 1] xy = [-1, 1]
elif isnan(xy[0]): elif not isfinite(xy[0]):
xy[0] = xy[1]-4 xy[0] = xy[1]-4
func(xy[0], xy[1], padding=0) func(xy[0], xy[1], padding=0)
@ -494,11 +602,14 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
self.checkBox.setVisible(visible) self.checkBox.setVisible(visible)
def update_legend(self, sid, name): def update_legend(self, sid, name):
if self._block:
return
self.listWidget.blockSignals(True) self.listWidget.blockSignals(True)
for i in range(self.listWidget.count()): for i in range(self.listWidget.count()):
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)
@ -520,16 +631,14 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
elif other_item in self.graphic.items(): elif other_item in self.graphic.items():
self.legend.addItem(other_item, convert(other_item.opts.get('name', ''), old='tex', new='html')) self.legend.addItem(other_item, convert(other_item.opts.get('name', ''), old='tex', new='html'))
def export_dialog(self, path=None): def export_dialog(self, _=None):
filters = 'All files (*.*);;AGR (*.agr);;SVG (*.svg);;PDF (*.pdf)' filters = 'All files (*.*);;AGR (*.agr);;SVG (*.svg);;PDF (*.pdf)'
for imgformat in QtGui.QImageWriter.supportedImageFormats(): for imgformat in QtGui.QImageWriter.supportedImageFormats():
str_format = imgformat.data().decode('utf-8') str_format = imgformat.data().decode('utf-8')
filters += ';;' + str_format.upper() + ' (*.' + str_format + ')' filters += ';;' + str_format.upper() + ' (*.' + str_format + ')'
if path is None:
path = ''
outfile = None outfile = None
f = FileDialog(caption='Export graphic', directory=str(path), filter=filters, mode='save') f = FileDialog(caption='Export graphic', filter=filters, mode='save')
f.setOption(FileDialog.DontConfirmOverwrite) f.setOption(FileDialog.DontConfirmOverwrite)
mode = f.exec() mode = f.exec()
if mode == QtWidgets.QDialog.Accepted: if mode == QtWidgets.QDialog.Accepted:
@ -565,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
@ -608,21 +720,25 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
try: try:
item_dic = plot_item.get_data_opts() item_dic = plot_item.get_data_opts()
except Exception as e: except Exception as e:
print(f'{item} could not exported because {e.args}') logger.exception(f'{item} could not exported because {e.args}')
continue continue
if 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:
print(f'{item} could not be exported because {e.args}') logger.exception(f'{item} could not be exported because {e.args}')
continue continue
in_legend.append(False) in_legend.append(False)
@ -645,7 +761,8 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
'legend': self.legend.isVisible(), 'legend': self.legend.isVisible(),
'plots': (self.real_button.isChecked(), self.imag_button.isChecked(), self.error_button.isChecked()), 'plots': (self.real_button.isChecked(), self.imag_button.isChecked(), self.error_button.isChecked()),
'children': self.sets, 'children': self.sets,
'active': self.active, 'active': self._active,
'invert': (self.plotItem.vb.state['xInverted'], self.plotItem.vb.state['yInverted']),
} }
in_legend = [] in_legend = []
@ -692,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])
@ -708,7 +825,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
if background is not None: if background is not None:
self._bgcolor = mkColor(background) self._bgcolor = mkColor(background)
self.graphic.setBackground(self._bgcolor) self.graphic.setBackground(self._bgcolor)
self.legend.setBrush(self._bgcolor) # self.legend.setBrush(self._bgcolor)
if foreground is not None: if foreground is not None:
self._fgcolor = mkColor(foreground) self._fgcolor = mkColor(foreground)
@ -725,7 +842,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
self.show_legend() self.show_legend()
title = self.plotItem.titleLabel.text title = self.plotItem.titleLabel.text
if title is not None: if title is not None and title != '':
self.plotItem.setTitle(title, **{'size': '10pt', 'color': self._fgcolor}) self.plotItem.setTitle(title, **{'size': '10pt', 'color': self._fgcolor})
x = self.plotItem.getAxis('bottom').labelText x = self.plotItem.getAxis('bottom').labelText
@ -736,8 +853,12 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
if y is not None: if y is not None:
self.plotItem.setLabel('left', y, **{'font-size': '10pt', 'color': self._fgcolor.name()}) self.plotItem.setLabel('left', y, **{'font-size': '10pt', 'color': self._fgcolor.name()})
@QtCore.pyqtSlot(bool, name='on_bwbutton_toggled')
def change_background(self, _): def change_background(self, _):
temp = self._fgcolor, self._bgcolor temp = self._fgcolor, self._bgcolor
self.set_color(foreground=self._prev_colors[0], background=self._prev_colors[1]) self.set_color(foreground=self._prev_colors[0], background=self._prev_colors[1])
self._prev_colors = temp self._prev_colors = temp
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

@ -1,4 +1,9 @@
from __future__ import annotations
import re
from nmreval.io.asciireader import AsciiReader from nmreval.io.asciireader import AsciiReader
from nmreval.utils import NUMBER_RE, 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
@ -13,6 +18,9 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
self.setupUi(self) self.setupUi(self)
self.reader = None self.reader = None
self._matches = []
self.regex_input.setText(NUMBER_RE.pattern)
self.custom_input.setValidator(QtGui.QDoubleValidator())
self.comment_textfield = QtWidgets.QPlainTextEdit(self.header_widget) self.comment_textfield = QtWidgets.QPlainTextEdit(self.header_widget)
self.comment_textfield.setReadOnly(True) self.comment_textfield.setReadOnly(True)
@ -30,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
@ -41,6 +49,8 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
def __call__(self, fname, *args, **kwargs): def __call__(self, fname, *args, **kwargs):
self.reader = AsciiReader(fname) self.reader = AsciiReader(fname)
self.check_filename(self.regex_input.text())
if self.skip: if self.skip:
self.accept() self.accept()
return return
@ -52,9 +62,10 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
self.log_checkBox.setChecked(False) self.log_checkBox.setChecked(False)
self.set_gui() self.set_gui()
self.set_column_names(1)
self.skippy_checkbox.blockSignals(True) self.skippy_checkbox.blockSignals(True)
self.skippy_checkbox.setCheckState(QtCore.Qt.Unchecked) self.skippy_checkbox.setCheckState(QtCore.Qt.CheckState.Unchecked)
self.skippy_checkbox.blockSignals(False) self.skippy_checkbox.blockSignals(False)
return self return self
@ -63,12 +74,14 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
for text in self.reader.header: for text in self.reader.header:
self.comment_textfield.appendPlainText(text) self.comment_textfield.appendPlainText(text)
tmp = self.line_spinBox.value()
if self.reader.header: if self.reader.header:
self.line_spinBox.setMaximum(len(self.reader.header)) self.line_spinBox.setMaximum(len(self.reader.header))
else: else:
self.line_spinBox.setValue(0) self.line_spinBox.setValue(0)
self.line_spinBox.setEnabled(False) self.line_spinBox.setEnabled(False)
self.show_preview(10) self.show_preview(10)
self.line_spinBox.setValue(tmp)
if self.reader.delays is not None: if self.reader.delays is not None:
set_string = ''.join(str(d) + '\n' for d in self.reader.delays) set_string = ''.join(str(d) + '\n' for d in self.reader.delays)
@ -98,15 +111,19 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
@QtCore.pyqtSlot(int, name='on_preview_spinBox_valueChanged') @QtCore.pyqtSlot(int, name='on_preview_spinBox_valueChanged')
def show_preview(self, line_no: int): def show_preview(self, line_no: int):
preview, width = self.reader.make_preview(line_no) preview, width, comments = self.reader.make_preview(line_no)
self.ascii_table.setRowCount(min(line_no, len(preview))) self.ascii_table.setRowCount(min(line_no, len(preview)))
self.ascii_table.setColumnCount(width) self.ascii_table.setColumnCount(width + 1)
for i, line in enumerate(preview): for i, line in enumerate(preview):
comment_line = comments[i]
for j, field in enumerate(line): for j, field in enumerate(line):
it = QtWidgets.QTableWidgetItem(field) it = QtWidgets.QTableWidgetItem(field)
self.ascii_table.setItem(i, j, it) self.ascii_table.setItem(i, j, it)
it = QtWidgets.QTableWidgetItem(comment_line)
self.ascii_table.setItem(i, len(line), it)
self.ascii_table.resizeColumnsToContents() self.ascii_table.resizeColumnsToContents()
@QtCore.pyqtSlot(int, name='on_column_checkBox_stateChanged') @QtCore.pyqtSlot(int, name='on_column_checkBox_stateChanged')
@ -115,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):
@ -161,26 +181,49 @@ 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()
is_valid = True
if x:
try: try:
x = int(self.x_lineedit.text())-1 x = int(x)-1
print(x)
except ValueError: except ValueError:
pass
else:
x = None x = None
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 = []
@ -191,25 +234,103 @@ 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(x=x, y=y, yerr=y_err, ret_dic = self.reader.export(
mode=self.buttonGroup.checkedButton().text(), x=x,
col_names=col_header) y=y,
yerr=y_err,
mode=mode,
col_names=col_header,
num_value=self.get_numerical_value(),
)
self.data_read.emit(ret_dic) self.data_read.emit(ret_dic)
except Exception as e: except 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):
print('skippy das buschkänguru', _)
self.skip = self.skippy_checkbox.isChecked() self.skip = self.skippy_checkbox.isChecked()
@QtCore.pyqtSlot(str, name='on_regex_input_textChanged')
def check_filename(self, pattern: str = NUMBER_RE.pattern):
if self.reader is None:
return
success = True
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.setMaximum(len(self._matches))
self.re_match_index.blockSignals(False)
else:
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())
@QtCore.pyqtSlot(int, name='on_re_match_index_valueChanged')
def show_match(self, idx: int = 0):
fname = str(self.reader.fname.stem)
if self._matches:
m = self._matches[idx-1]
self.label_8.setText(f'{fname[:m.start()]}<b>{fname[m.start():m.end()]}</b>{fname[m.end():]}')
else:
self.label_8.setText(fname)
def get_numerical_value(self) -> float:
val = 0
if self.re_button.isChecked() and self._matches:
m = self._matches[self.re_match_index.value()-1]
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():
val = float(self.custom_input.text())
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

@ -5,7 +5,7 @@ from pathlib import Path
import numpy as np import numpy as np
from pyqtgraph import PlotDataItem from pyqtgraph import PlotDataItem
from nmreval.data.points import Points from nmreval.data import DSC
from nmreval.io.dsc import Cyclohexane, DSCCalibrator, DSCSample from nmreval.io.dsc import Cyclohexane, DSCCalibrator, DSCSample
from ..Qt import QtWidgets, QtCore from ..Qt import QtWidgets, QtCore
@ -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[1] / 60:.0f} min') item.setText(f'{opts[1]:.2f} K for {opts[2] / 60:.0f} min')
else: else:
item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable) item.setFlags(
QtCore.Qt.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')
self.empty_label.setText(str(self.empty.fname.name))
# 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())))
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
@ -158,20 +171,28 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
self.sample_idx = None self.sample_idx = None
self.clear_plots() self.clear_plots()
def get_data(self, ): def get_data(self):
if self.sample_idx is None: if self.sample_idx is None:
return return
rate = self.current_run[0] rate = self.current_run[0]
slope_type = {self.none_radioButton: None, slope_type = {
self.none_radioButton: None,
self.isotherm_radioButton: 'iso', self.isotherm_radioButton: 'iso',
self.slope_radioButton: 'curve'}[self.buttonGroup.checkedButton()] self.slope_radioButton: 'curve',
}[self.buttonGroup.checkedButton()]
limit = None
if slope_type == 'curve':
try:
limit = float(self.limit1_lineedit.text())*60, float(self.limit2_lineedit.text())*60
except ValueError:
limit = None
try: try:
raw_sample, drift_value, sample_data, empty_data, slope = self.calibrator.get_data(self.sample_idx, raw_sample, drift_value, sample_data, empty_data, slope = self.calibrator.get_data(self.sample_idx, slope=slope_type, limits=limit)
slope=slope_type)
except ValueError as e: except ValueError as e:
_msg = QtWidgets.QMessageBox.warning(self, 'No rate found', e.args[0]) _msg = QtWidgets.QMessageBox.warning(self, f'Data collection with error', e.args[0])
return return
self.calibrator.ref_list = [] self.calibrator.ref_list = []
@ -194,8 +215,14 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
@QtCore.pyqtSlot(QtWidgets.QAbstractButton, name='on_buttonGroup_buttonClicked') @QtCore.pyqtSlot(QtWidgets.QAbstractButton, name='on_buttonGroup_buttonClicked')
@QtCore.pyqtSlot(int, name='on_cp_checkBox_stateChanged') @QtCore.pyqtSlot(int, name='on_cp_checkBox_stateChanged')
@QtCore.pyqtSlot(str, name='on_limit1_lineedit_textChanged')
@QtCore.pyqtSlot(str, name='on_limit2_lineedit_textChanged')
def update_plots(self, _=None): def update_plots(self, _=None):
sample_data, raw_sample, empty_data, drift_value, slope, calib_x, calib_y, regions = self.get_data() res = self.get_data()
if res is None:
return
sample_data, raw_sample, empty_data, drift_value, slope, calib_x, calib_y, regions = res
self.raw_sample.setData(x=raw_sample[0], y=raw_sample[1]) self.raw_sample.setData(x=raw_sample[0], y=raw_sample[1])
self.drift_sample.setData(x=drift_value[0], y=drift_value[1]) self.drift_sample.setData(x=drift_value[0], y=drift_value[1])
@ -203,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()
@ -235,11 +264,17 @@ 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 = Points(sample_data[0], sample_data[1], value=rate, name=f'{self.fname.stem} {rate} ({mode})') new_val = DSC(sample_data[0], sample_data[1], value=rate, name=f'{self.fname.stem} {rate}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
else: else:
self.data_read.emit([new_val]) self.data_read.emit([new_val])
@ -250,7 +285,7 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
def button_clicked(self, bttn: QtWidgets.QAbstractButton): def button_clicked(self, bttn: QtWidgets.QAbstractButton):
bttn_value = self.buttonBox.standardButton(bttn) bttn_value = self.buttonBox.standardButton(bttn)
if bttn_value in (self.buttonBox.Ok, self.buttonBox.Apply, self.buttonBox.Save): if bttn_value in (self.buttonBox.Ok, self.buttonBox.Apply, self.buttonBox.Save):
self.export_data(filesave=bttn_value==self.buttonBox.Save, close_after=bttn_value==self.buttonBox.Ok) self.export_data(filesave=bttn_value == self.buttonBox.Save, close_after=bttn_value == self.buttonBox.Ok)
else: else:
super().close() super().close()

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:
@ -55,12 +60,17 @@ class GraceExporter:
break break
if c_num == -1: if c_num == -1:
c_num = max(colors.keys()) c_num = max(colors.keys())+1
colors[c_num + 1] = (f'color{c_num + 1}', sc) colors[c_num] = (f'color{c_num}', sc)
new_colors.append((c_num + 1, f'color{c_num + 1}', sc)) new_colors.append((c_num, f'color{c_num}', sc))
new_s.set_symbol(**{'symbol': item['symbol'].value, 'size': item['symbolsize'] / 10., 'color': c_num, new_s.set_symbol(**{
'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

@ -1,3 +1,5 @@
from __future__ import annotations
import pathlib import pathlib
from nmreval.io.fcbatchreader import FCReader from nmreval.io.fcbatchreader import FCReader
@ -20,46 +22,67 @@ 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
return super().eventFilter(src, evt) return super().eventFilter(src, evt)
def add_graphs(self, graphs: list[tuple[str, str]]):
for gid, graph_name in graphs:
self.graph_comboBox.addItem(graph_name, gid)
@QtCore.pyqtSlot(int, name='on_region_checkBox_stateChanged') @QtCore.pyqtSlot(int, name='on_region_checkBox_stateChanged')
def use_region(self, state: int): def use_region(self, state: int):
self.start_lineedit.setEnabled(state == QtCore.Qt.Checked) self.start_lineedit.setEnabled(state == QtCore.Qt.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)',
if infiles: )
self.listWidget.addItems(infiles)
self.label.setText(str(pathlib.Path(infiles[-1]).parent))
else: else:
infiles = QtWidgets.QFileDialog.getExistingDirectory(caption='Select input directory', infiles = QtWidgets.QFileDialog.getExistingDirectory(
caption='Select input directory',
directory=str(self.path), directory=str(self.path),
options=QtWidgets.QFileDialog.ShowDirsOnly) options=QtWidgets.QFileDialog.ShowDirsOnly,
)
infiles = [infiles] if infiles else infiles
if infiles: if infiles:
self.listWidget.addItem(infiles) self.listWidget.addItems(infiles)
self.label.setText(str(pathlib.Path(infiles).parent)) self.path = pathlib.Path(infiles[-1]).parent
self.label.setText(str(self.path))
@QtCore.pyqtSlot(name='on_savebutton_clicked') @QtCore.pyqtSlot(name='on_savebutton_clicked')
def save_path(self): def save_path(self):
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)
@ -72,15 +95,15 @@ class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog):
self.close() self.close()
def read(self, items): def read(self, items):
region = (None, None) region = None
if self.region_box.isChecked(): if self.region_box.isChecked():
start = None start = None
if self.start_lineedit.text(): if self.start_lineedit.text():
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)
@ -99,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

View File

@ -4,6 +4,7 @@ from nmreval.lib.lines import LineStyle
from nmreval.lib.symbols import SymbolStyle from nmreval.lib.symbols import SymbolStyle
from nmreval.data.points import Points from nmreval.data.points import Points
from nmreval.io.graceeditor import GraceEditor from nmreval.io.graceeditor import GraceEditor
from nmreval.utils.text import convert
from ..Qt import QtCore, QtWidgets, QtGui from ..Qt import QtCore, QtWidgets, QtGui
from .._py.gracereader import Ui_Dialog from .._py.gracereader import Ui_Dialog
@ -55,8 +56,12 @@ class QGraceReader(QtWidgets.QDialog, Ui_Dialog):
if ds is None: if ds is None:
continue continue
item_2 = QtWidgets.QTreeWidgetItem([f'Set {gset.idx} (Label: {gset.get_property("legend")}, ' legend = gset.get_property('legend')
legend_str = convert(legend, old='agr', new='str') if legend is not None else ''
item_2 = QtWidgets.QTreeWidgetItem([f'Set {gset.idx} (Label: {legend_str}, '
f'shape: {ds.shape})']) f'shape: {ds.shape})'])
item_2.setCheckState(0, QtCore.Qt.Checked) item_2.setCheckState(0, QtCore.Qt.Checked)
item_2.setData(0, QtCore.Qt.UserRole, (graphs.idx, gset.idx)) item_2.setData(0, QtCore.Qt.UserRole, (graphs.idx, gset.idx))
item.addChild(item_2) item.addChild(item_2)
@ -89,8 +94,12 @@ class QGraceReader(QtWidgets.QDialog, Ui_Dialog):
item = iterator.value() item = iterator.value()
key = (item.data(0, QtCore.Qt.UserRole)) key = (item.data(0, QtCore.Qt.UserRole))
s = self._reader.dataset(*key) s = self._reader.dataset(*key)
label = self._reader.get_property(*key, 'legend').replace('"', '') label = self._reader.get_property(*key, 'legend')
# label = self._reader.graphs[key[0]].sets[key[1]]['legend'].replace('"', '') if label is None:
label = ''
else:
label = label.replace('"', '')
label = convert(label, old='agr', new='str')
sd = s.data sd = s.data
sd = np.atleast_2d(sd) sd = np.atleast_2d(sd)
if s.type == 'xydy': if s.type == 'xydy':

View File

@ -1,80 +1 @@
import sys from .enums import Relations
if sys.version_info < (3, 7):
HAS_IMPORTLIB_RESOURCE = False
from pkg_resources import resource_filename
else:
HAS_IMPORTLIB_RESOURCE = True
from importlib.resources import path
from ..Qt import QtGui, QtWidgets
# def get_path_importlib(package, resource):
# return path(package, resource)
#
#
# def _get_path_pkg(package, resource):
# return resource_filename(package, resource)
#
#
# if HAS_IMPORTLIB_RESOURCE:
# get_path = get_path_importlib
# else:
# get_path = _get_path_pkg
def make_action_icons(widget):
global HAS_IMPORTLIB_RESOURCE
icon_type = QtWidgets.QApplication.instance().theme
from json import loads
if HAS_IMPORTLIB_RESOURCE:
with path('resources.icons', 'icons.json') as fp:
with fp.open('r') as f:
icon_list = loads(f.read())
for ac, img in icon_list[widget.objectName()].items():
dirname = 'resources.icons.%s_light' % icon_type
with path(dirname, img+'.png') as imgpath:
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(str(imgpath)), QtGui.QIcon.Normal, QtGui.QIcon.Off)
getattr(widget, ac).setIcon(icon)
else:
with open(resource_filename('resources.icons', 'icons.json'), 'r') as f:
icon_list = loads(f.read())
for ac, img in icon_list[widget.objectName()].items():
dirname = 'resources.icons.%s_light' % icon_type
imgpath = resource_filename(dirname, img+'.png')
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(str(imgpath)), QtGui.QIcon.Normal, QtGui.QIcon.Off)
getattr(widget, ac).setIcon(icon)
def get_icon(icon_name):
try:
icon_type = QtWidgets.QApplication.instance().theme
except AttributeError:
icon_type = 'normal'
global HAS_IMPORTLIB_RESOURCE
if icon_name != 'logo':
dirname = f'resources.icons.{icon_type}_light'
else:
dirname = 'resources.icons'
if HAS_IMPORTLIB_RESOURCE:
with path(dirname, icon_name+'.png') as imgpath:
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(str(imgpath)), QtGui.QIcon.Normal, QtGui.QIcon.Off)
return icon
else:
imgpath = resource_filename(dirname, icon_name+'.png')
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(imgpath), QtGui.QIcon.Normal, QtGui.QIcon.Off)
return icon

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

@ -30,7 +30,7 @@ class PropertyDelegate(QtWidgets.QStyledItemDelegate):
rect = options.rect rect = options.rect
rect.adjust(5, 0, -5, 0) rect.adjust(5, 0, -5, 0)
mid = (rect.bottom()+rect.top()) / 2 mid = int((rect.bottom()+rect.top()) / 2)
painter.drawLine(rect.left(), mid, rect.right(), mid) painter.drawLine(rect.left(), mid, rect.right(), mid)
painter.restore() painter.restore()
@ -42,7 +42,7 @@ class PropertyDelegate(QtWidgets.QStyledItemDelegate):
painter.setPen(pen) painter.setPen(pen)
pm = make_symbol_pixmap(r) pm = make_symbol_pixmap(r)
painter.drawPixmap(options.rect.topLeft()+QtCore.QPoint(3, (options.rect.height()-pm.height())/2), pm) painter.drawPixmap(options.rect.topLeft()+QtCore.QPoint(3, int((options.rect.height()-pm.height())/2)), pm)
style = QtWidgets.QApplication.style() style = QtWidgets.QApplication.style()
text_rect = style.subElementRect(QtWidgets.QStyle.SE_ItemViewItemText, options, None) text_rect = style.subElementRect(QtWidgets.QStyle.SE_ItemViewItemText, options, None)
@ -171,7 +171,7 @@ class LineStyleEditor(QtWidgets.QComboBox):
rect = painter.style().subControlRect(QtWidgets.QStyle.CC_ComboBox, rect = painter.style().subControlRect(QtWidgets.QStyle.CC_ComboBox,
opt, QtWidgets.QStyle.SC_ComboBoxEditField, None) opt, QtWidgets.QStyle.SC_ComboBoxEditField, None)
rect.adjust(+10, 0, -10, 0) rect.adjust(+10, 0, -10, 0)
mid = (rect.bottom() + rect.top()) / 2 mid = int((rect.bottom() + rect.top()) / 2)
painter.drawLine(rect.left(), mid, rect.right(), mid) painter.drawLine(rect.left(), mid, rect.right(), mid)
painter.end() painter.end()
else: else:
@ -193,7 +193,7 @@ class LineStyleDelegate(QtWidgets.QStyledItemDelegate):
rect = option.rect rect = option.rect
rect.adjust(+10, 0, -10, 0) rect.adjust(+10, 0, -10, 0)
mid = (rect.bottom()+rect.top()) / 2 mid = int((rect.bottom()+rect.top()) / 2)
painter.drawLine(rect.left(), mid, rect.right(), mid) painter.drawLine(rect.left(), mid, rect.right(), mid)
else: else:
QtWidgets.QStyledItemDelegate.paint(self, painter, option, index) QtWidgets.QStyledItemDelegate.paint(self, painter, option, index)

8
src/gui_qt/lib/enums.py Normal file
View File

@ -0,0 +1,8 @@
from enum import IntEnum, auto
class Relations(IntEnum):
isFitOf = auto()
hasFit = auto()
isFitPartOf = auto()
hasFitPart = auto()

View File

@ -1,3 +1,5 @@
from typing import Any
from numpy import inf from numpy import inf
from nmreval.utils.text import convert from nmreval.utils.text import convert
@ -50,19 +52,24 @@ class QDelayWidget(QtWidgets.QWidget):
class LineEdit(QtWidgets.QLineEdit): class LineEdit(QtWidgets.QLineEdit):
values_requested = QtCore.pyqtSignal() values_requested = QtCore.pyqtSignal()
replace_single_values = QtCore.pyqtSignal()
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent=parent) super().__init__(parent=parent)
def contextMenuEvent(self, evt): def contextMenuEvent(self, evt):
menu = self.createStandardContextMenu() menu = self.createStandardContextMenu()
request_action = menu.addAction('Use value of sets') request_action = menu.addAction('Use numeric value of sets')
set_value_action = menu.addAction('Replace single set values')
action = menu.exec(evt.globalPos()) action = menu.exec(evt.globalPos())
if action == request_action: if action == request_action:
self.values_requested.emit() self.values_requested.emit()
elif action == set_value_action:
self.replace_single_values.emit()
class LineEditPost(QtWidgets.QLineEdit): class LineEditPost(QtWidgets.QLineEdit):
values_requested = QtCore.pyqtSignal() values_requested = QtCore.pyqtSignal()
@ -404,3 +411,21 @@ class ElideComboBox(QtWidgets.QComboBox):
opt.currentText = painter.fontMetrics().elidedText(opt.currentText, QtCore.Qt.ElideRight, rect.width()) opt.currentText = painter.fontMetrics().elidedText(opt.currentText, QtCore.Qt.ElideRight, rect.width())
painter.drawControl(QtWidgets.QStyle.CE_ComboBoxLabel, opt) painter.drawControl(QtWidgets.QStyle.CE_ComboBoxLabel, opt)
class CheckCombobox(QtWidgets.QComboBox):
def addItem(self, text: str, userData: Any=None) -> None:
super().addItem(text, userData=userData)
item = self.model().item(self.count()-1)
item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable)
item.setCheckState(QtCore.Qt.Checked)
def addItems(self, text):
for text_i in text:
self.addItem(text_i)
def isChecked(self, idx: int) -> bool:
return bool(self.model().item(idx).checkState())

View File

@ -0,0 +1,53 @@
from numpy import log10, arange, floor, ceil
from pyqtgraph import PlotWidget, PlotItem
__all__ = ['NMRPlotWidget', 'logTickValues']
class NMRPlotWidget(PlotWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for orient in ['top', 'bottom', 'left', 'right']:
# BAD HACK!!! but seems to work, see function for explanation
self.plotItem.getAxis(orient).logTickValues = logTickValues
class NMRPlotItem(PlotItem):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for orient in ['top', 'bottom', 'left', 'right']:
self.plotItem.getAxis(orient).logTickValues = logTickValues
def logTickValues(minVal, maxVal, size, stdTicks):
# TODO FIND A BETTER SOLUTION!!!
# Sometimes minVal and maxVal are not log-scaled values and the loop from v1 to v2 is humongous,
# The minor list then fills the RAM completely and freezes everything
# Until there is a better solution, we overwrite this function for every AxesItem
# and do not draw minor ticks at all if there are too many
# start with the tick spacing given by tickValues().
# Any level whose spacing is < 1 needs to be converted to log scale
ticks = []
for (spacing, t) in stdTicks:
if spacing >= 1.0:
ticks.append((spacing, t))
if len(ticks) < 3:
v1 = int(floor(minVal))
v2 = int(ceil(maxVal))
# major = list(range(v1+1, v2))
minor = []
if v2 - v1 < 400:
for v in range(v1, v2):
minor.extend(v + log10(arange(1, 10)))
minor = [x for x in minor if minVal < x < maxVal]
ticks.append((None, minor))
return ticks

View File

@ -0,0 +1,66 @@
import sys
if sys.version_info < (3, 7):
HAS_IMPORTLIB_RESOURCE = False
from pkg_resources import resource_filename
else:
HAS_IMPORTLIB_RESOURCE = True
from importlib.resources import path
from ..Qt import QtGui, QtWidgets
def make_action_icons(widget):
global HAS_IMPORTLIB_RESOURCE
icon_type = QtWidgets.QApplication.instance().theme
from json import loads
if HAS_IMPORTLIB_RESOURCE:
with path('resources.icons', 'icons.json') as fp:
with fp.open('r') as f:
icon_list = loads(f.read())
for ac, img in icon_list[widget.objectName()].items():
dirname = 'resources.icons.%s_light' % icon_type
with path(dirname, img+'.png') as imgpath:
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(str(imgpath)), QtGui.QIcon.Normal, QtGui.QIcon.Off)
getattr(widget, ac).setIcon(icon)
else:
with open(resource_filename('resources.icons', 'icons.json'), 'r') as f:
icon_list = loads(f.read())
for ac, img in icon_list[widget.objectName()].items():
dirname = 'resources.icons.%s_light' % icon_type
imgpath = resource_filename(dirname, img+'.png')
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(str(imgpath)), QtGui.QIcon.Normal, QtGui.QIcon.Off)
getattr(widget, ac).setIcon(icon)
def get_icon(icon_name):
try:
icon_type = QtWidgets.QApplication.instance().theme
except AttributeError:
icon_type = 'normal'
global HAS_IMPORTLIB_RESOURCE
if icon_name != 'logo':
dirname = f'resources.icons.{icon_type}_light'
else:
dirname = 'resources.icons'
if HAS_IMPORTLIB_RESOURCE:
with path(dirname, icon_name+'.png') as imgpath:
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(str(imgpath)), QtGui.QIcon.Normal, QtGui.QIcon.Off)
return icon
else:
imgpath = resource_filename(dirname, icon_name+'.png')
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(imgpath), QtGui.QIcon.Normal, QtGui.QIcon.Off)
return icon

View File

@ -0,0 +1,20 @@
from ..Qt import QtWidgets, QtGui, QtCore
class QListWidgetSelect(QtWidgets.QListWidget):
"""
Extension of QListWidget to change the check state of all selected QListWidgetItems with space key
"""
def __init__(self, parent=None):
super().__init__(parent=parent)
def keyPressEvent(self, evt: QtGui.QKeyEvent):
if evt.key() == QtCore.Qt.Key.Key_Space:
for idx in self.selectedIndexes():
item = self.itemFromIndex(idx)
cs = item.checkState()
item.setCheckState(QtCore.Qt.CheckState.Unchecked if cs == QtCore.Qt.CheckState.Checked
else QtCore.Qt.CheckState.Checked)
else:
super().keyPressEvent(evt)

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()

57
src/gui_qt/lib/mdiarea.py Normal file
View File

@ -0,0 +1,57 @@
from __future__ import annotations
from ..Qt import QtWidgets, QtCore
from nmreval.lib.logger import logger
from ..graphs.graphwindow import QGraphWindow
class MdiAreaTile(QtWidgets.QMdiArea):
newData = QtCore.pyqtSignal(list)
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setAcceptDrops(True)
def tileSubWindowsVertically(self):
window_list = self.subWindowList()
rect = QtCore.QRect(0, 0, self.width(), int(self.height() / len(window_list)))
pos = QtCore.QPoint(0, 0)
for win in window_list:
win.setGeometry(rect)
win.move(pos)
pos.setY(pos.y() + win.height())
def tileSubWindowsHorizontally(self):
window_list = self.subWindowList()
rect = QtCore.QRect(0, 0, int(self.width() / len(window_list)), self.height())
pos = QtCore.QPoint(0, 0)
for win in window_list:
win.setGeometry(rect)
win.move(pos)
pos.setX(pos.x() + win.width())
def addSubWindow(self, widget: QtWidgets.QWidget, flags: QtCore.Qt.WindowFlags = QtCore.Qt.WindowFlags()) -> QtWidgets.QMdiSubWindow | None:
subwindow = super().addSubWindow(widget)
subwindow.setOption(QtWidgets.QMdiSubWindow.RubberBandMove, True)
subwindow.setOption(QtWidgets.QMdiSubWindow.RubberBandResize, True)
subwindow.setMinimumHeight(240)
subwindow.setMinimumWidth(360)
return subwindow
def setActiveSubWidget(self, key: str):
for win in self.subWindowList():
wdgt = win.widget()
if isinstance(wdgt, QGraphWindow) and wdgt.id == key:
self.setActiveSubWindow(win)
break
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_
@ -21,36 +23,62 @@ class Namespace:
self.top_levels = {} self.top_levels = {}
if basic: if basic:
self.add_namespace({'x': (None, 'x values'), 'y': (None, 'x values'), 'y_err': (None, 'y error values'), self.add_namespace(
'fit': (None, 'dictionary of fit parameter', 'fit["PIKA"]'), 'np': (np, 'numpy module')}, {
parents=('Basic', 'General')) 'x': (None, 'x values'),
'y': (None, 'x values'),
'y_err': (None, 'y error values'),
'fit': (None, 'dictionary of fit parameter', 'fit["PIKA"]'),
'np': (np, 'numpy module'),
'nmreval': (nmreval, 'built-in classes and stuff')
},
parents=('Basic', 'General'),
)
self.add_namespace({'sin': (np.sin, 'Sine', 'sin(PIKA)'), 'cos': (np.cos, 'Cosine', 'cos(PIKA)'), self.add_namespace(
'tan': (np.tan, 'Tangens', 'tan(PIKA)'), 'ln': (np.log, 'Natural Logarithm', 'ln(PIKA)'), {
'sin': (np.sin, 'Sine', 'sin(PIKA)'),
'cos': (np.cos, 'Cosine', 'cos(PIKA)'),
'tan': (np.tan, 'Tangens', 'tan(PIKA)'),
'ln': (np.log, 'Natural Logarithm', 'ln(PIKA)'),
'log': (np.log10, 'Logarithm (base 10)', 'log(PIKA)'), 'log': (np.log10, 'Logarithm (base 10)', 'log(PIKA)'),
'exp': (np.exp, 'Exponential', 'exp(PIKA)'), 'sqrt': (np.sqrt, 'Root', 'sqrt(PIKA)'), 'exp': (np.exp, 'Exponential', 'exp(PIKA)'),
'lin_range': (np.linspace, 'N evenly spaced over interval [start, stop]', 'sqrt': (np.sqrt, 'Root', 'sqrt(PIKA)'),
'lin_range(start, stop, N)'), 'lin_range': (np.linspace, 'N evenly spaced over interval [start, stop]', 'lin_range(start, stop, N)'),
'log_range': (np.geomspace, 'N evenly spaced (log-scale) over interval [start, stop]', 'log_range': (np.geomspace, 'N evenly spaced (log-scale) over interval [start, stop]', 'lin_range(start, stop, N)'),
'lin_range(start, stop, N)')}, },
parents=('Basic', 'Functions')) parents=('Basic', 'Functions'))
self.add_namespace({'max': (np.max, 'Maximum value', 'max(PIKA)'), self.add_namespace(
{
'max': (np.max, 'Maximum value', 'max(PIKA)'),
'min': (np.min, 'Minimum value', 'min(PIKA)'), 'min': (np.min, 'Minimum value', 'min(PIKA)'),
'argmax': (np.argmax, 'Index of maximum value', 'argmax(PIKA)'), 'argmax': (np.argmax, 'Index of maximum value', 'argmax(PIKA)'),
'argmin': (np.argmax, 'Index of minimum value', 'argmin(PIKA)')}, 'argmin': (np.argmax, 'Index of minimum value', 'argmin(PIKA)'),
parents=('Basic', 'Values')) },
parents=('Basic', 'Values')),
if const: if const:
self.add_namespace({'e': (constants.e, 'e / As'), 'eps0': (constants.epsilon0, 'epsilon0 / As/Vm'), self.add_namespace(
'Eu': (constants.Eu,), 'h': (constants.h, 'h / eVs'), {
'hbar': (constants.hbar, 'hbar / eVs'), 'kB': (constants.kB, 'kB / eV/K'), 'e': (constants.e, 'e / As'),
'mu0': (constants.mu0, 'mu0 / Vs/Am'), 'NA': (constants.NA, 'NA / 1/mol'), 'eps0': (constants.epsilon0, 'epsilon0 / As/Vm'),
'pi': (constants.pi,), 'R': (constants.R, 'R / eV')}, 'Eu': (constants.Eu,),
parents=('Constants', 'Maybe useful')) 'h': (constants.h, 'h / eVs'),
'hbar': (constants.hbar, 'hbar / eVs'),
'kB': (constants.kB, 'kB / eV/K'),
'mu0': (constants.mu0, 'mu0 / Vs/Am'),
'NA': (constants.NA, 'NA / 1/mol'),
'pi': (constants.pi,),
'R': (constants.R, 'R / eV'),
},
parents=('Constants', 'Maybe useful'),
)
self.add_namespace({f'gamma["{k}"]': (v, k, f'gamma["{k}"]') for k, v in constants.gamma.items()}, self.add_namespace(
parents=('Constants', 'Magnetogyric ratios (in 1/(sT))')) {f'gamma["{k}"]': (v, k, f'gamma["{k}"]') for k, v in constants.gamma.items()},
parents=('Constants', 'Gyromagnetic ratios (in 1/(sT))')
)
if fitfuncs: if fitfuncs:
self.make_dict_from_fitmodule(models) self.make_dict_from_fitmodule(models)
@ -104,9 +132,18 @@ class Namespace:
graph = namedtuple('graphs', ['s']) graph = namedtuple('graphs', ['s'])
sets = namedtuple('sets', ['x', 'y', 'y_err', 'value', 'fit'], defaults=(None,)) sets = namedtuple('sets', ['x', 'y', 'y_err', 'value', 'fit'], defaults=(None,))
gamma = {}
gs = re.compile(r'g\[(\d+)].s\[(\d+)].(x|y(?:_err)*|value|fit)') gs = re.compile(r'g\[(\d+)].s\[(\d+)].(x|y(?:_err)*|value|fit)')
gamma_re = re.compile(r'gamma\["(\w+)"\]')
for k, v in self.namespace.items(): for k, v in self.namespace.items():
m = gamma_re.match(k)
if m:
gamma[m.group(1)] = v[0]
continue
m = gs.match(k) m = gs.match(k)
if m: if m:
if 'g' not in ret_dic: if 'g' not in ret_dic:
@ -118,7 +155,7 @@ class Namespace:
gg = ret_dic['g'][int(g_num)] gg = ret_dic['g'][int(g_num)]
if int(s_num) not in gg.s: if int(s_num) not in gg.s:
gg.s[int(s_num)] = sets('', '', '', '') gg.s[int(s_num)] = sets('', '', '', '', {})
ss = gg.s[int(s_num)] ss = gg.s[int(s_num)]
if pos == 'fit': if pos == 'fit':
@ -130,9 +167,12 @@ class Namespace:
else: else:
ret_dic['g'][int(g_num)].s[int(s_num)] = ss._replace(**{pos: v[0]}) ret_dic['g'][int(g_num)].s[int(s_num)] = ss._replace(**{pos: v[0]})
else: continue
ret_dic[k] = v[0] ret_dic[k] = v[0]
ret_dic['gamma'] = gamma
return ret_dic return ret_dic
@ -170,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]
@ -185,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)
@ -212,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

@ -174,6 +174,7 @@ class PlotItem(PlotDataItem):
pen = self.opts['pen'] pen = self.opts['pen']
if isinstance(pen, tuple): if isinstance(pen, tuple):
self.opts['linecolor'] = pen self.opts['linecolor'] = pen
self.opts['pen'] = mkPen(color=pen)
else: else:
c = pen.color() c = pen.color()
self.opts['linecolor'] = c.red(), c.green(), c.blue() self.opts['linecolor'] = c.red(), c.green(), c.blue()
@ -182,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()
@ -279,7 +282,7 @@ class PlotItem(PlotDataItem):
else: else:
self.scatter.hide() self.scatter.hide()
def set_symbol(self, symbol=None, size=None, color=None): def set_symbol(self, *, symbol=None, size=None, color=None):
if symbol is not None: if symbol is not None:
if isinstance(symbol, int): if isinstance(symbol, int):
self.setSymbol(SymbolStyle(symbol).to_str()) self.setSymbol(SymbolStyle(symbol).to_str())
@ -313,14 +316,13 @@ class PlotItem(PlotDataItem):
self.opts['pen'] = pen self.opts['pen'] = pen
self.updateItems() self.updateItems()
def set_line(self, style=None, width=None, color=None): def set_line(self, *, style=None, width=None, color=None):
pen = self.opts['pen'] pen = self.opts['pen']
if pen is None: if pen is None:
pen = mkPen(style=QtCore.Qt.NoPen) pen = mkPen(style=QtCore.Qt.NoPen)
if width is not None: if width is not None:
pen.setWidthF(width) pen.setWidthF(width)
if style is not None: if style is not None:
if isinstance(style, LineStyle): if isinstance(style, LineStyle):
style = style.value style = style.value
@ -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'],
} }
@ -375,7 +378,6 @@ class PlotItem(PlotDataItem):
class RegionItem(LinearRegionItem): class RegionItem(LinearRegionItem):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.mode = kwargs.pop('mode', 'half') self.mode = kwargs.pop('mode', 'half')
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.logmode = False self.logmode = False
@ -383,6 +385,10 @@ class RegionItem(LinearRegionItem):
if not hasattr(self, '_bounds') and hasattr(self, '_boundingRectCache'): if not hasattr(self, '_bounds') and hasattr(self, '_boundingRectCache'):
self._bounds = self._boundingRectCache self._bounds = self._boundingRectCache
for l in self.lines:
# higher z for borderlines improves chances that you can move it when multiple regions overlap
l.setZValue(self.zValue() + 1)
def setLogMode(self, xmode, _): def setLogMode(self, xmode, _):
if self.logmode == xmode: if self.logmode == xmode:
return return
@ -418,6 +424,15 @@ class RegionItem(LinearRegionItem):
else: else:
return region return region
def setRegion(self, region, use_log=False):
if self.logmode and use_log:
region = np.log10(region[0]), np.log10(region[1])
if not np.all(np.isfinite(region)):
raise ValueError(f'Invalid region limits ({region[0]}, {region[1]})')
else:
super().setRegion(region)
def boundingRect(self): def boundingRect(self):
# overwrite to draw correct rect in logmode # overwrite to draw correct rect in logmode
@ -460,11 +475,18 @@ class LegendItemBlock(LegendItem):
def mouseDragEvent(self, ev): def mouseDragEvent(self, ev):
if ev.button() == QtCore.Qt.LeftButton: if ev.button() == QtCore.Qt.LeftButton:
ev.accept() ev.accept()
dpos = ev.pos() - ev.lastPos() dpos = ev.pos() - ev.lastPos()
upper_left = self.pos()
lower_right = self.pos()
lower_right.setX(lower_right.x() + self.width())
lower_right.setY(lower_right.y() + self.height())
vb_rect = self.parentItem().rect() vb_rect = self.parentItem().rect()
pos = self.pos()
# upper left corner and a point a little more to the bottom right must be inside # upper left and lower right corner must be inside viewbox
if vb_rect.contains(pos+dpos) and vb_rect.contains(pos+dpos+QtCore.QPointF(20., 20.)): if vb_rect.contains(upper_left + dpos) and vb_rect.contains(lower_right + dpos):
self.autoAnchor(pos + dpos) self.autoAnchor(upper_left + dpos)
else: else:
self.autoAnchor(pos) self.autoAnchor(upper_left)

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

@ -1,110 +0,0 @@
import os.path
import json
import urllib.request
import webbrowser
import random
from ..Qt import QtGui, QtCore, QtWidgets
from .._py.pokemon import Ui_Dialog
random.seed()
class QPokemon(QtWidgets.QDialog, Ui_Dialog):
def __init__(self, number=None, parent=None):
super().__init__(parent=parent)
self.setupUi(self)
self._js = json.load(open(os.path.join(path_to_module, 'utils', 'pokemon.json'), 'r'), encoding='UTF-8')
self._id = 0
if number is not None and number in range(1, len(self._js)+1):
poke_nr = f'{number:03d}'
self._id = number
else:
poke_nr = f'{random.randint(1, len(self._js)):03d}'
self._id = int(poke_nr)
self._pokemon = None
self.show_pokemon(poke_nr)
self.label_15.linkActivated.connect(lambda x: webbrowser.open(x))
self.buttonBox.clicked.connect(self.randomize)
self.next_button.clicked.connect(self.show_next)
self.prev_button.clicked.connect(self.show_prev)
def show_pokemon(self, nr):
self._pokemon = self._js[nr]
self.setWindowTitle('Pokémon: ' + self._pokemon['Deutsch'])
for i in range(self.tabWidget.count(), -1, -1):
print('i', self.tabWidget.count(), i)
try:
self.tabWidget.widget(i).deleteLater()
except AttributeError:
pass
for n, img in self._pokemon['Bilder']:
w = QtWidgets.QWidget()
vl = QtWidgets.QVBoxLayout()
l = QtWidgets.QLabel(self)
l.setAlignment(QtCore.Qt.AlignHCenter)
pixmap = QtGui.QPixmap()
try:
pixmap.loadFromData(urllib.request.urlopen(img, timeout=0.5).read())
except IOError:
l.setText(n)
else:
sc_pixmap = pixmap.scaled(256, 256, QtCore.Qt.KeepAspectRatio)
l.setPixmap(sc_pixmap)
vl.addWidget(l)
w.setLayout(vl)
self.tabWidget.addTab(w, n)
if len(self._pokemon['Bilder']) <= 1:
self.tabWidget.tabBar().setVisible(False)
else:
self.tabWidget.tabBar().setVisible(True)
self.tabWidget.adjustSize()
self.name.clear()
keys = ['National-Dex', 'Kategorie', 'Typ', 'Größe', 'Gewicht', 'Farbe', 'Link']
label_list = [self.pokedex_nr, self.category, self.poketype, self.weight, self.height, self.color, self.info]
for (k, label) in zip(keys, label_list):
v = self._pokemon[k]
if isinstance(v, list):
v = os.path.join('', *v)
if k == 'Link':
v = '<a href={}>{}</a>'.format(v, v)
label.setText(v)
for k in ['Deutsch', 'Japanisch', 'Englisch', 'Französisch']:
v = self._pokemon[k]
self.name.addItem(k + ': ' + v)
self.adjustSize()
def randomize(self, idd):
if idd.text() == 'Retry':
new_number = f'{random.randint(1, len(self._js)):03d}'
self._id = int(new_number)
self.show_pokemon(new_number)
else:
self.close()
def show_next(self):
new_number = self._id + 1
if new_number > len(self._js):
new_number = 1
self._id = new_number
self.show_pokemon(f'{new_number:03d}')
def show_prev(self):
new_number = self._id - 1
if new_number == 0:
new_number = len(self._js)
self._id = new_number
self.show_pokemon(f'{new_number:03d}')

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}')

100
src/gui_qt/lib/starter.py Normal file
View File

@ -0,0 +1,100 @@
from __future__ import annotations
import re
from shutil import copyfile
from pathlib import Path
import os
from configparser import ConfigParser
from importlib.resources import path as resource_path
from nmreval.configs import config_paths
from nmreval.lib.logger import logger
def make_starter(app_file: str | None):
if app_file is not None:
make_starter_appimage(Path(app_file))
else:
make_starter_src()
def make_starter_appimage(app_file: Path):
new_path = Path.home() / '.local' / 'bin'
if not new_path.exists():
new_path.mkdir(parents=True)
new_path /= app_file.name
if app_file != new_path:
app_file.rename(new_path)
create_desktop_file(new_path)
def make_starter_src():
home = Path.home()
p = Path.home()
for p in Path(__file__).parents:
if p.stem == 'src':
break
elif p == home:
break
success = p != Path.home()
if success:
bin_path = p.with_name('bin') / 'evaluate.py'
success = bin_path.exists()
if not success:
logger.warning('Location of evaluate.py could not be determined')
return False
create_desktop_file(bin_path)
def create_desktop_file(new_path: Path):
logo_path = config_paths() / 'logo.png'
if not logo_path.exists():
with resource_path('resources', 'logo.png') as fp:
copyfile(fp, logo_path)
desktop_entry = f"""\
[Desktop Entry]
Name=NMReval
Comment=Best program ever (maybe)
Exec={new_path}
Icon={logo_path}
Type=Application
Terminal=false
Categories=Science;NumericalAnalysis;Physics;DataVisualization;Other;
NoDisplay=false
"""
file_name = 'pkm.vogel.nmreval.desktop'
with Path('~/.local/share/applications/', file_name).expanduser().open('w') as f:
f.write(desktop_entry)
desktop_dir = get_xkg_user_dirs('desktop')
if desktop_dir is not None:
desk_file = Path(desktop_dir, file_name)
with desk_file.open('w') as f:
f.write(desktop_entry)
desk_file.chmod(0o755)
def get_xkg_user_dirs(dir_type: str) -> str | None:
xdg_conf_home = os.getenv('XDG_CONFIG_HOME') or str(Path.home() / '.config')
with Path(xdg_conf_home, 'user-dirs.dirs').open('r') as f:
conf_string = '[XDG_USER_DIRS]\n' + f.read()
conf_string = re.sub(r'\$HOME', str(Path.home()), conf_string)
conf_string = re.sub('"', '', conf_string)
config = ConfigParser()
config.read_string(conf_string)
return config['XDG_USER_DIRS'].get(f'xdg_{dir_type}_dir')
if __name__ == '__main__':
make_starter()

View File

@ -6,7 +6,7 @@ import numpy as np
from ..Qt import QtWidgets, QtCore, QtGui from ..Qt import QtWidgets, QtCore, QtGui
__all__ = ['Game'] __all__ = ['Game', 'QMines']
class Game(QtWidgets.QDialog): class Game(QtWidgets.QDialog):

View File

@ -1,4 +1,4 @@
from . import HAS_IMPORTLIB_RESOURCE from .iconloading import HAS_IMPORTLIB_RESOURCE
from ..Qt import QtGui, QtWidgets, QtCore from ..Qt import QtGui, QtWidgets, QtCore

View File

@ -29,3 +29,16 @@ class TreeWidget(QtWidgets.QTreeWidget):
it.setCheckState(0, QtCore.Qt.Unchecked if it.checkState(0) == QtCore.Qt.Checked else QtCore.Qt.Checked) it.setCheckState(0, QtCore.Qt.Unchecked if it.checkState(0) == QtCore.Qt.Checked else QtCore.Qt.Checked)
else: else:
super().keyPressEvent(evt) super().keyPressEvent(evt)
class TableWidget(QtWidgets.QTableWidget):
def keyPressEvent(self, evt: QtGui.QKeyEvent):
if evt.key() == QtCore.Qt.Key.Key_Space:
for idx in self.selectedIndexes():
item = self.itemFromIndex(idx)
cs = item.checkState()
item.setCheckState(QtCore.Qt.CheckState.Unchecked if cs == QtCore.Qt.CheckState.Checked
else QtCore.Qt.CheckState.Checked)
else:
super().keyPressEvent(evt)

View File

@ -1,7 +1,10 @@
from __future__ import annotations
import copy import copy
from numpy import argsort from numpy import argsort
from . import Relations
from ..Qt import QtWidgets, QtCore from ..Qt import QtWidgets, QtCore
from ..data.container import FitContainer from ..data.container import FitContainer
from ..graphs.graphwindow import QGraphWindow from ..graphs.graphwindow import QGraphWindow
@ -84,6 +87,21 @@ class ShiftCommand(QtWidgets.QUndoCommand):
self.__data.apply('ls', self.__args) self.__data.apply('ls', self.__args)
class EditCommand(QtWidgets.QUndoCommand):
def __init__(self, data, *args):
super().__init__('Edit signal')
self.__data = data
self.__arguments = args
self.__original = copy.deepcopy(self.__data.data)
def undo(self):
self.__data.data = copy.deepcopy(self.__original)
def redo(self):
self.__data.edit_signal(*self.__arguments)
class NormCommand(QtWidgets.QUndoCommand): class NormCommand(QtWidgets.QUndoCommand):
def __init__(self, data, mode): def __init__(self, data, mode):
super().__init__('Normalize') super().__init__('Normalize')
@ -216,33 +234,69 @@ class DeleteGraphCommand(QtWidgets.QUndoCommand):
class DeleteCommand(QtWidgets.QUndoCommand): class DeleteCommand(QtWidgets.QUndoCommand):
def __init__(self, container, key, signal1, signal2): def __init__(self, container: dict, keys: list[str], graphs: dict, graphid: str,
signal1: QtCore.pyqtSignal, signal2: QtCore.pyqtSignal):
super().__init__('Delete data') super().__init__('Delete data')
self.__container = container self.__container = container
self.__value = self.__container[key] self.__graph_container = graphs
self.__key = key self.__graph_key = graphid
self.__value = {}
for k in keys:
self.__value[k] = self.__container[k]
self.__keys = tuple(keys)
self.__signal_add = signal1 self.__signal_add = signal1
self.__signal_remove = signal2 self.__signal_remove = signal2
def redo(self): def redo(self):
self.__signal_remove.emit(self.__key) # stop graph from rescaling and updating legend
if isinstance(self.__value, FitContainer): self.__graph_container[self.__graph_key].block(True)
self.__signal_remove.emit(list(self.__keys[::-1]))
for sid in self.__keys[::-1]:
val = self.__value[sid]
if isinstance(val, FitContainer):
try: try:
self.__container[self.__value.fitted_key]._fits.remove(self.__key) self.__container[val.fitted_key]._fits.remove(sid)
except KeyError: except KeyError:
pass pass
del self.__container[self.__key]
for (flag1, flag2) in ((Relations.isFitPartOf, Relations.hasFitPart), (Relations.hasFitPart, Relations.isFitPartOf)):
for related_item in val.get_relation(flag1):
try:
self.__container[related_item].remove_relation(flag2, sid)
except KeyError:
pass
del self.__container[sid]
self.__graph_container[self.__graph_key].block(False)
def undo(self): def undo(self):
self.__container[self.__key] = self.__value # stop graph from rescaling and updating legend
if isinstance(self.__value, FitContainer): self.__graph_container[self.__graph_key].block(True)
for sid in self.__keys:
val = self.__value[sid]
self.__container[sid] = val
if isinstance(val, FitContainer):
try: try:
self.__container[self.__value.fitted_key]._fits.append(self.__key) self.__container[val.fitted_key]._fits.append(sid)
except KeyError: except KeyError:
pass pass
self.__signal_add.emit([self.__key], self.__value.graph) for (flag1, flag2) in (('isFitPartOf', 'hasFitPartOf'), ('hasFitPartOf', 'isFitPartOf')):
for related_item in val.get_relation(flag1):
try:
self.__container[related_item].add_relation(flag2, sid)
except KeyError:
pass
self.__signal_add.emit(list(self.__keys), self.__graph_key)
self.__graph_container[self.__graph_key].block(False)
class EvalCommand(QtWidgets.QUndoCommand): class EvalCommand(QtWidgets.QUndoCommand):

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,28 +1,16 @@
from __future__ import annotations from __future__ import annotations
import sys
from functools import lru_cache
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
import requests
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
@ -59,246 +47,8 @@ class RdBuCMap:
elif val < self.min: elif val < self.min:
col = QtGui.QColor.fromRgb(*RdBuCMap._rdbu[-1]) col = QtGui.QColor.fromRgb(*RdBuCMap._rdbu[-1])
else: else:
col = QtGui.QColor.fromRgb(*(float(self.spline[i](val)) for i in range(3))) col = QtGui.QColor.fromRgb(*(int(self.spline[i](val)) for i in range(3)))
return col return col
class UpdateDialog(QtWidgets.QDialog):
startDownload = QtCore.pyqtSignal(list)
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.success = False
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)
if m_time_zsync is None:
label_text = '<p>Retrieval of version information failed.</p>' \
'<p>Please try later (or complain to people that it does not work).</p>'
dialog_bttns = QtWidgets.QDialogButtonBox.Close
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>'
if is_updateble is None:
self.status.setText('Could not determine if this version is newer, please update manually (if necessary).')
dialog_bttns = QtWidgets.QDialogButtonBox.Close
elif is_updateble:
self.status.setText(f'<p>Newer version available. Press Ok to download new version, Cancel to ignore.')
dialog_bttns = QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Cancel
else:
self.status.setText(f'Version may be already up-to-date.')
dialog_bttns = QtWidgets.QDialogButtonBox.Close
self.label.setText(label_text)
self.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 = ['-i', self._appfile, self.updater.zsync_url]
args = [self.updater.zsync_url]
self.dialog_button.setEnabled(False)
self.startDownload.emit(args)
self.status.show()
@QtCore.pyqtSlot(int)
def finish_update(self, retcode: int):
# print('finished with', retcode)
self.success = retcode == 0
if retcode == 0:
self.status.setText('Download complete.')
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()
if self.success:
appname = self.updater.get_zsync()[2]
if self._appfile is not None:
appimage_path = appname
old_version = Path(self._appfile).rename(self._appfile+'.old')
appimage_path = Path(appimage_path).replace(self._appfile)
else:
appimage_path = Path().cwd() / appname
# rename to version-agnostic name
appimage_path = appimage_path.rename('NMReval.AppImage')
appimage_path.chmod(appimage_path.stat().st_mode | 73) # 73 = 0o111 = a+x
_ = QtWidgets.QMessageBox.information(self, 'Complete',
f'New AppImage available at<br>{appimage_path}')
super().closeEvent(evt)
class Downloader(QtCore.QObject):
started = QtCore.pyqtSignal()
finished = QtCore.pyqtSignal(int)
progressChanged = QtCore.pyqtSignal(str)
@QtCore.pyqtSlot(list)
def run_download(self, args: list[str]):
logger.info(f'Download with args {args}')
process = subprocess.Popen(['zsync'] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1, universal_newlines=True)
while True:
nextline = process.stdout.readline().strip()
if nextline:
self.progressChanged.emit(nextline)
# line = process.stderr.readline().strip()
if process.poll() is not None:
break
self.finished.emit(process.returncode)
class Updater:
host = 'mirror.infra.pkm'
bucket = 'nmreval'
version = 'NMReval-latest-x86_64'
@property
def zsync_url(self):
return f'http://{Updater.host}/{Updater.bucket}/{Updater.version}.AppImage.zsync'
@staticmethod
@lru_cache(3)
def get_zsync():
url_zsync = f'http://{Updater.host}/{Updater.bucket}/{Updater.version}.AppImage.zsync'
m_time_zsync = None
checksum_zsync = None
zsync_file = None
filename = None
try:
response = requests.get(url_zsync)
if response.status_code == requests.codes['\o/']:
zsync_file = response.content
else:
logger.error(f'Request for zsync returned code {response.statuse_code}')
except Exception:
logger.exception('Download of zsync failed')
if zsync_file is not None:
for line in zsync_file.split(b'\n'):
try:
kw, val = line.split(b': ')
if kw == b'MTime':
m_time_zsync = datetime.strptime(str(val, encoding='utf-8'), '%a, %d %b %Y %H:%M:%S %z').astimezone(None)
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()
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.info(f'zsync information {m_time_zsync}, {checksum_zsync}, {appname}')
logger.info(f'file information {m_time_file}, {checksum_zsync}')
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
def open_bug_report():
form_entries = {
'description': 'Please state the nature of the medical emergency.',
'title': 'Everything is awesome?',
'assign[0]': 'dominik',
'subscribers[0]': 'dominik',
'tag': 'nmreval',
'priority': 'normal',
'status': 'open',
}
full_url = 'https://chaos3.fkp.physik.tu-darmstadt.de/maniphest/task/edit/?'
for k, v in form_entries.items():
full_url += f'{k}={v}&'
full_url.replace(' ', '+')
import webbrowser
webbrowser.open(full_url)

View File

@ -0,0 +1,3 @@

View File

@ -1,7 +1,6 @@
from __future__ import annotations from __future__ import annotations
import os import os
import pathlib
import re import re
from pathlib import Path from pathlib import Path
@ -9,18 +8,24 @@ from numpy import geomspace, linspace
from pyqtgraph import ViewBox from pyqtgraph import ViewBox
from nmreval.configs import * from nmreval.configs import *
from nmreval.lib.logger import logger
from nmreval.io.sessionwriter import NMRWriter
from .management import UpperManagement from .management import UpperManagement
from ..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 QApodDialog, QBaselineDialog, QPhasedialog from ..data.signaledit import QPreviewDialog, QBaselineDialog
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 import get_icon, make_action_icons from ..lib.iconloading import make_action_icons, get_icon
from ..lib.pg_objects import RegionItem from ..lib.starter import make_starter
from ..math.binning import BinningWindow
from ..math.evaluation import QEvalDialog from ..math.evaluation import QEvalDialog
from ..math.interpol import InterpolDialog from ..math.interpol import InterpolDialog
from ..math.mean_dialog import QMeanTimes from ..math.mean_dialog import QMeanTimes
@ -28,7 +33,7 @@ from ..math.smooth import QSmooth
from ..nmr.coupling_calc import QCoupCalcDialog from ..nmr.coupling_calc import QCoupCalcDialog
from ..nmr.t1_from_tau import QRelaxCalc from ..nmr.t1_from_tau import QRelaxCalc
from .._py.basewindow import Ui_BaseWindow from .._py.basewindow import Ui_BaseWindow
from ..lib.utils import UpdateDialog, open_bug_report, Updater from ..lib.update import UpdateDialog
class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
@ -37,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:
@ -53,8 +58,17 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.fitpreview = [] self.fitpreview = []
self._fit_plot_id = None self._fit_plot_id = None
self.savefitdialog = None self.savefitdialog = None
self._tg_dialog = None
self.fitresult_dialog = None
self.eval = None self.eval = None
self.editor = None self.editor = None
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)
@ -63,14 +77,20 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self._block_window_change = False self._block_window_change = False
self.fname = None self.fname = None
self.tim = QtCore.QTimer()
self.settings = QtCore.QSettings('NMREVal', 'settings') self.settings = QtCore.QSettings('NMREVal', 'settings')
self._init_gui() self._init_gui()
self._init_signals() self._init_signals()
if os.getenv('APPIMAGE') is not None: self.fit_timer = QtCore.QTimer()
if Updater.get_update_information(os.getenv('APPIMAGE'))[0]: self.fit_timer.setInterval(500)
self.fit_timer.timeout.connect(
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() self.look_for_update()
def _init_gui(self): def _init_gui(self):
@ -84,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)
@ -103,29 +115,32 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.mousepos = QtWidgets.QLabel('') self.mousepos = QtWidgets.QLabel('')
self.status = QtWidgets.QLabel('') self.status = QtWidgets.QLabel('')
# noinspection PyUnresolvedReferences
self.statusBar.addWidget(self.status) self.statusBar.addWidget(self.status)
# noinspection PyUnresolvedReferences
self.statusBar.addWidget(self.mousepos) self.statusBar.addWidget(self.mousepos)
self.fitregion = RegionItem()
self._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")
@ -139,20 +154,24 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.actionUndo.setIcon(icon) self.actionUndo.setIcon(icon)
self.menuData.insertAction(self.actionRedo, self.actionUndo) self.menuData.insertAction(self.actionRedo, self.actionUndo)
# # self.actionSave.triggered.connect(lambda: self.management.save('/autohome/dominik/nmreval/testdata/test.nmr', ''))
# self.actionSave.triggered.connect(self.save)
self.action_save_fit_parameter.triggered.connect(self.save_fit_parameter) self.action_save_fit_parameter.triggered.connect(self.save_fit_parameter)
self.ac_group2.triggered.connect(self.change_fit_limits) # 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(lambda: self._show_tab('signal')) self.action_edit.triggered.connect(self.do_preview)
self.actionPick_position.triggered.connect(lambda: self._show_tab('pick')) self.actionPick_position.triggered.connect(lambda: self._show_tab('pick'))
self.actionIntegration.triggered.connect(lambda: self._show_tab('integrate')) self.actionIntegration.triggered.connect(lambda: self._show_tab('integrate'))
self.action_FitWidget.triggered.connect(lambda: self._show_tab('fit')) self.action_FitWidget.triggered.connect(lambda: self._show_tab('fit'))
self.action_draw_object.triggered.connect(lambda: self._show_tab('drawing'))
self.action_new_set.triggered.connect(self.management.create_empty) self.action_new_set.triggered.connect(self.management.create_empty)
self.actionDelete_window.triggered.connect(self.management.delete_sets)
self.actionCascade_windows.triggered.connect(self.area.cascadeSubWindows)
self.actionTile.triggered.connect(self.area.tileSubWindows)
self.actionTileHorizontal.triggered.connect(self.area.tileSubWindowsHorizontally)
self.actionTileVertical.triggered.connect(self.area.tileSubWindowsVertically)
self.datawidget.keyChanged.connect(self.management.change_keys) self.datawidget.keyChanged.connect(self.management.change_keys)
self.datawidget.tree.deleteItem.connect(self.management.delete_sets) self.datawidget.tree.deleteItem.connect(self.management.delete_sets)
self.datawidget.tree.moveItem.connect(self.management.move_sets) self.datawidget.tree.moveItem.connect(self.management.move_sets)
@ -166,7 +185,8 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.datawidget.tree.saveFits.connect(self.save_fit_parameter) self.datawidget.tree.saveFits.connect(self.save_fit_parameter)
self.datawidget.tree.extendFits.connect(self.extend_fit) self.datawidget.tree.extendFits.connect(self.extend_fit)
self.management.newData.connect(self.show_new_data) self.management.newData[list, str].connect(self.show_new_data)
self.management.newData[list, str, bool].connect(self.show_new_data)
self.management.newGraph.connect(self.new_graph) self.management.newGraph.connect(self.new_graph)
self.management.dataChanged.connect(self.update_data) self.management.dataChanged.connect(self.update_data)
self.management.deleteData.connect(self.delete_data) self.management.deleteData.connect(self.delete_data)
@ -176,10 +196,12 @@ 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)
self.fit_dialog.abortFit.connect(lambda : self.management.stopFit.emit()) self.fit_dialog.abortFit.connect(lambda: self.management.stopFit.emit())
self.movedialog.moveData.connect(self.move_sets) self.movedialog.moveData.connect(self.move_sets)
self.movedialog.copyData.connect(self.management.copy_sets) self.movedialog.copyData.connect(self.management.copy_sets)
@ -189,7 +211,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.t1tauwidget.newData.connect(self.management.add_new_data) self.t1tauwidget.newData.connect(self.management.add_new_data)
self.editsignalwidget.do_something.connect(self.management.apply) self.editsignalwidget.do_something.connect(self.management.apply)
self.editsignalwidget.preview_triggered.connect(self.do_preview) # self.editsignalwidget.preview_triggered.connect(self.do_preview)
self.action_sort_pts.triggered.connect(lambda: self.management.apply('sort', ())) self.action_sort_pts.triggered.connect(lambda: self.management.apply('sort', ()))
self.action_calc_eps_derivative.triggered.connect(self.management.bds_deriv) self.action_calc_eps_derivative.triggered.connect(self.management.bds_deriv)
@ -208,16 +230,17 @@ 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())
self.management.graphs.valueChanged.connect(self.update_graph_list)
@QtCore.pyqtSlot(name='on_action_open_triggered') @QtCore.pyqtSlot(name='on_action_open_triggered')
def open(self): def open(self):
@ -232,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
@ -241,11 +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.data_read.connect(self.management.add_new_data) self.fc_reader = QFCReader(path=self.path, parent=self)
reader.exec() self.fc_reader.data_read.connect(self.management.add_new_data)
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):
@ -262,10 +292,11 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
selected_filter = save_dialog.selectedNameFilter() selected_filter = save_dialog.selectedNameFilter()
if savefile is not None: if savefile is not None:
self.path = savefile.parent
use_underscore = save_dialog.checkBox.isChecked() use_underscore = save_dialog.checkBox.isChecked()
self.management.save(savefile, selected_filter, strip_spaces=use_underscore) self.management.save(savefile, selected_filter, strip_spaces=use_underscore)
param_outfile = re.sub('[_\s-]?<label>[_\s-]?', '', savefile.stem) param_outfile = re.sub(r'[_\s-]?<label>[_\s-]?', '', savefile.stem)
bad_character = r'/*<>\|:"' bad_character = r'/*<>\|:"'
for c in bad_character: for c in bad_character:
@ -307,10 +338,15 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
return w.id return w.id
@QtCore.pyqtSlot(list, str) @QtCore.pyqtSlot(list, str)
def show_new_data(self, sets: list, graph: str): @QtCore.pyqtSlot(list, str, bool)
def show_new_data(self, sets: list, graph: str, skip_change: bool = False):
if len(sets) == 0: if len(sets) == 0:
return return
prev_graph = ''
if skip_change:
prev_graph = self.management.current_graph
if graph == '': if graph == '':
graph = self.new_graph() graph = self.new_graph()
@ -319,39 +355,47 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
for idd in sets: for idd in sets:
new_item = self.management[idd] new_item = self.management[idd]
self.datawidget.blockSignals(True) self.datawidget.blockSignals(True)
self.datawidget.add_item(new_item.id, new_item.name, graph) self.datawidget.add_item(new_item.id, new_item.name, new_item.value, graph, update=False)
self.datawidget.blockSignals(False) self.datawidget.blockSignals(False)
self.datawidget.tree.update_indexes()
if graph == self.fit_dialog.connected_figure: # if graph == self.fit_dialog.connected_figure:
self.fit_dialog.load(self.management.graphs.active(graph)) # self.fit_dialog.load(self.management.graphs.active(graph))
if skip_change:
self.area.setActiveSubWidget(prev_graph)
if self.valuewidget.isVisible(): if self.valuewidget.isVisible():
self.valuewidget(self.management.graphs.tree()) self.valuewidget(self.management.graphs.tree())
@QtCore.pyqtSlot(name='on_actionDelete_window_triggered')
def delete_windows(self):
self.management.delete_sets()
@QtCore.pyqtSlot(str) @QtCore.pyqtSlot(str)
def remove_graph(self, gid: str): def remove_graph(self, gid: str):
self.datawidget.remove_item(gid) self.datawidget.remove_item([gid])
val_figure = self.valuewidget.connected_figure val_figure = self.valuewidget.connected_figure
self.valuewidget.remove_graph() self.valuewidget.remove_graph()
_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
self.tabWidget.removeTab(self.tabWidget.indexOf(self.ptsselectwidget)) for line in self.ptsselectwidget.pts_lines:
self.current_graph_widget.remove_external(line)
_remove_pts = True
if self.t1tauwidget.connected_figure == gid: if self.t1tauwidget.connected_figure == gid:
self.t1tauwidget.connected_figure = None self.t1tauwidget.connected_figure = None
self.tabWidget.removeTab(self.tabWidget.indexOf(self.t1tauwidget)) self.current_graph_widget.remove_external(self.t1tauwidget.min_pos)
self.current_graph_widget.remove_external(self.t1tauwidget.parabola)
_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
@ -359,12 +403,15 @@ 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.tabWidget.setCurrentIndex(0) self.valuewidget.connected_figure = None
self.current_graph_widget.remove_external(self.valuewidget.selection_real)
self.current_graph_widget.remove_external(self.valuewidget.selection_imag)
_move_to_data_tab = True
self.current_graph_widget.enable_picking(False) self.current_graph_widget.enable_picking(False)
self.current_graph_widget = None self.current_graph_widget = None
self.management.current_graph = '' self.management.current_graph = None
self.current_plotitem = None self.current_plotitem = None
wdgt.setParent(None) wdgt.setParent(None)
@ -375,6 +422,12 @@ 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)
@ -388,6 +441,12 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
if self.area.subWindowList(): if self.area.subWindowList():
self.area.activateNextSubWindow() self.area.activateNextSubWindow()
@QtCore.pyqtSlot()
def update_graph_list(self):
graph_list = self.management.graphs.list()
self.t1tauwidget.set_graphs(graph_list)
self.ptsselectwidget.set_graphs(graph_list)
@QtCore.pyqtSlot(str) @QtCore.pyqtSlot(str)
def set_graph(self, key: str): def set_graph(self, key: str):
w = self.management.graphs[key] w = self.management.graphs[key]
@ -405,8 +464,9 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.datawidget.blockSignals(False) self.datawidget.blockSignals(False)
w.mousePositionChanged.connect(self.mousemoved) w.mousePositionChanged.connect(self.mousemoved)
w.aboutToClose.connect(self.delete_windows) w.aboutToClose.connect(self.management.delete_sets)
w.positionClicked.connect(self.point_selected) w.positionClicked.connect(self.point_selected)
w.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()
@ -416,7 +476,9 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
@QtCore.pyqtSlot(QtWidgets.QMdiSubWindow, name='on_area_subWindowActivated') @QtCore.pyqtSlot(QtWidgets.QMdiSubWindow, name='on_area_subWindowActivated')
def change_window(self, wd): def change_window(self, wd):
""" Called every time focus moves from or to a subwindow. Returns None if current focus is not on a subwindow""" """ Called every time focus moves from or to a subwindow. Returns None if current focus is not on a subwindow"""
if wd is not None: if wd is None:
return
if self.current_graph_widget is not None: if self.current_graph_widget is not None:
self.current_graph_widget.closable = True self.current_graph_widget.closable = True
@ -445,14 +507,6 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.datawidget.tree.highlight(self.management.current_graph) self.datawidget.tree.highlight(self.management.current_graph)
self.datawidget.tree.blockSignals(False) self.datawidget.tree.blockSignals(False)
@QtCore.pyqtSlot(name='on_actionCascade_windows_triggered')
@QtCore.pyqtSlot(name='on_actionTile_triggered')
def change_window_size(self):
if self.sender() == self.actionCascade_windows:
self.area.cascadeSubWindows()
elif self.sender() == self.actionTile:
self.area.tileSubWindows()
@QtCore.pyqtSlot(name='on_actionChange_datatypes_triggered') @QtCore.pyqtSlot(name='on_actionChange_datatypes_triggered')
def type_change_dialog(self): def type_change_dialog(self):
from ..data.conversion import ConversionDialog from ..data.conversion import ConversionDialog
@ -475,7 +529,6 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
'signal': (self.editsignalwidget, 'Signals'), 'signal': (self.editsignalwidget, 'Signals'),
'pick': (self.ptsselectwidget, 'Pick points'), 'pick': (self.ptsselectwidget, 'Pick points'),
'fit': (self.fit_dialog, 'Fit'), 'fit': (self.fit_dialog, 'Fit'),
'drawing': (self.drawingswidget, 'Draw'),
'integrate': (self.integralwidget, 'Integrate'), 'integrate': (self.integralwidget, 'Integrate'),
}[mode] }[mode]
@ -511,7 +564,6 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self._select_valuewidget(widget == self.valuewidget) self._select_valuewidget(widget == self.valuewidget)
pick_required, block_window = self._select_t1tauwidget(widget == self.t1tauwidget, pick_required, block_window) pick_required, block_window = self._select_t1tauwidget(widget == self.t1tauwidget, pick_required, block_window)
block_window = self._select_fitwidget(widget == self.fit_dialog, block_window) block_window = self._select_fitwidget(widget == self.fit_dialog, block_window)
self._select_drawingswidget(widget == self.drawingswidget)
pick_required = self._select_integralwidget(widget == self.integralwidget, pick_required, block_window) pick_required = self._select_integralwidget(widget == self.integralwidget, pick_required, block_window)
self._set_pick_block(pick_required, block_window) self._set_pick_block(pick_required, block_window)
@ -527,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
@ -543,9 +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:
self.management.graphs[self.valuewidget.connected_figure].remove_external(self.valuewidget.selection_real) conn_fig = self.valuewidget.connected_figure
self.management.graphs[self.valuewidget.connected_figure].remove_external(self.valuewidget.selection_imag) self.management.graphs[conn_fig].remove_external(self.valuewidget.selection_real)
self.management.graphs[conn_fig].remove_external(self.valuewidget.selection_imag)
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:
@ -559,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
@ -592,23 +651,18 @@ 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
def _select_drawingswidget(self, onoff):
if onoff:
if self.drawingswidget.graphs is None:
self.drawingswidget.graphs = self.management.graphs
self.drawingswidget.update_tree()
else:
self.drawingswidget.clear()
@QtCore.pyqtSlot(str) @QtCore.pyqtSlot(str)
def get_data(self, key: str): def get_data(self, key: str):
if hasattr(self.sender(), 'set_data'):
self.sender().set_data(self.management[key]) self.sender().set_data(self.management[key])
@QtCore.pyqtSlot(name='on_actionCalculateT1_triggered') @QtCore.pyqtSlot(name='on_actionCalculateT1_triggered')
@ -623,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')
@ -651,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):
@ -683,6 +741,14 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.eval.exec() self.eval.exec()
@QtCore.pyqtSlot(name='on_actionBinning_triggered')
def open_binning(self):
dialog = BinningWindow(self)
res = dialog.exec()
if res:
digits = float(dialog.spinbox.text())
self.management.binning(digits)
@QtCore.pyqtSlot(name='on_actionDerivation_triggered') @QtCore.pyqtSlot(name='on_actionDerivation_triggered')
# @QtCore.pyqtSlot(name='on_actionIntegration_triggered') # @QtCore.pyqtSlot(name='on_actionIntegration_triggered')
@QtCore.pyqtSlot(name='on_actionFilon_triggered') @QtCore.pyqtSlot(name='on_actionFilon_triggered')
@ -719,9 +785,10 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.datawidget.set_name(sid, self.management[sid].name) self.datawidget.set_name(sid, self.management[sid].name)
@QtCore.pyqtSlot(str) @QtCore.pyqtSlot(list)
def delete_data(self, sid): def delete_data(self, sid: list[str]):
if self.valuewidget.shown_set == sid: for key in sid:
if self.valuewidget.shown_set == key:
self.tabWidget.setCurrentIndex(0) self.tabWidget.setCurrentIndex(0)
self.datawidget.remove_item(sid) self.datawidget.remove_item(sid)
@ -744,29 +811,22 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
editor.finished.connect(self.management.apply) editor.finished.connect(self.management.apply)
editor.exec() editor.exec()
@QtCore.pyqtSlot(str) @QtCore.pyqtSlot()
def do_preview(self, mode): def do_preview(self):
dialog = QPreviewDialog(self)
if mode == 'ap': success = True
dialog = QApodDialog(parent=self)
elif mode == 'ph':
dialog = QPhasedialog(parent=self)
else:
raise ValueError('Unknown preview mode %s' % str(mode))
dialog.setRange(*self.current_graph_widget.ranges, self.current_graph_widget.log)
for sid in self.current_graph_widget.active: for sid in self.current_graph_widget.active:
data_mode = self.management[sid].mode data_mode = self.management[sid].mode
tobeadded = False if data_mode in ('fid', 'spectrum'):
if (data_mode == 'fid') or (data_mode == 'spectrum' and mode == 'ph'): success = dialog.add_data(self.management[sid].data)
tobeadded = True
if tobeadded: if not success:
dialog.add_data(*self.management.get_data(sid, xy_only=True)) break
if dialog.exec() == QtWidgets.QDialog.Accepted: if success and dialog.exec() == QtWidgets.QDialog.Accepted:
self.management.apply(mode, dialog.get_value()) self.management.edit_signals(dialog.get_value())
@QtCore.pyqtSlot(name='on_actionMove_between_plots_triggered') @QtCore.pyqtSlot(name='on_actionMove_between_plots_triggered')
def move_sets_dialog(self): def move_sets_dialog(self):
@ -803,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)
@ -817,6 +873,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
except KeyError: except KeyError:
ret_val = None ret_val = None
# noinspection PyUnresolvedReferences
self.sender().receive_data(ret_val) self.sender().receive_data(ret_val)
return ret_val return ret_val
@ -845,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',
@ -876,12 +932,18 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.action_odr_fit: 'odr' self.action_odr_fit: 'odr'
}[self.ac_group.checkedAction()] }[self.ac_group.checkedAction()]
fit_is_ready = self.management.prepare_fit(parameter, links, fit_options)
if fit_is_ready:
self.management.start_fit()
self.fit_dialog.fit_button.setEnabled(False) self.fit_dialog.fit_button.setEnabled(False)
self.management.start_fit(parameter, links, fit_options) self.status.setText('Fit running...'.format(self.management.fitter.step))
self.fit_timer.start(500)
@QtCore.pyqtSlot(dict, int, bool) @QtCore.pyqtSlot(dict, int, bool)
def show_fit_preview(self, funcs: dict, num: int, show: bool): def show_fit_preview(self, funcs: dict, num: int, show: bool):
if self.fit_dialog.connected_figure is None: if not self.fit_dialog.connected_figure:
logger.warning(f'Fit dialog is not connected graph: Fit {self.fit_dialog.connected_figure}, '
f'current graph: {self.management.current_graph} ({self.management.current_graph in self.management.graphs})')
return return
g = self.management.graphs[self.fit_dialog.connected_figure] g = self.management.graphs[self.fit_dialog.connected_figure]
@ -898,40 +960,59 @@ 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.status.setText('')
if results: if results:
res_dialog = QFitResult(results, self.management, parent=self) if self.fitresult_dialog is None:
res_dialog.add_graphs(self.management.graphs.list()) self.fitresult_dialog = QFitResult(results, sub_colors, self.management, parent=self)
res_dialog.closed.connect(self.accepts_fit) self.fitresult_dialog.add_graphs(self.management.graphs.list())
res_dialog.redoFit.connect(self.management.redo_fits) self.fitresult_dialog.closed.connect(self.accepts_fit)
res_dialog.show() self.fitresult_dialog.redoFit.connect(self.management.redo_fits)
else:
self.fitresult_dialog(results, sub_colors)
self.fitresult_dialog.add_graphs(self.management.graphs.list())
self.fitresult_dialog.show()
@QtCore.pyqtSlot(dict, list, str, bool, bool, list) @QtCore.pyqtSlot(dict, list, str, bool, bool, list)
def accepts_fit(self, res: dict, opts: list, param_graph: str, show_fit: bool, parts: bool, extrapolate: list) -> None: def accepts_fit(self, res: dict, opts: list, param_graph: str,
show_fit: bool, parts: bool, extrapolate: list) -> None:
self.fit_dialog.set_parameter(res) self.fit_dialog.set_parameter(res)
self.management.make_fits(res, opts, param_graph, show_fit, parts, extrapolate) self.management.make_fits(res, opts, param_graph, show_fit, parts, extrapolate)
@QtCore.pyqtSlot(name='on_actionFunction_editor_triggered') @QtCore.pyqtSlot(name='on_actionFunction_editor_triggered')
def edit_models(self): def edit_models(self):
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()
print(res)
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):
@ -955,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():
@ -986,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:
@ -1043,3 +1125,32 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
QLog(parent=self).show() QLog(parent=self).show()
def autosave(self) -> bool:
self.status.setText('Autosave...')
success = NMRWriter(self.management.graphs, self.management.data).export(self.__backup_path.with_suffix('.nmr.0'))
if success:
self.__backup_path.with_suffix('.nmr.0').rename(self.__backup_path)
self.status.setText('')
return success
@QtCore.pyqtSlot(name='on_actionCreate_starter_triggered')
def create_starter(self):
make_starter(os.getenv('APPIMAGE'))
@QtCore.pyqtSlot(name='on_actionAbout_triggered')
def show_version(self):
from nmreval.version import __version__
QtWidgets.QMessageBox.about(self, 'Version', f'Build date of AppImage: {__version__}')
@QtCore.pyqtSlot(name='on_actionTNMH_model_triggered')
@QtCore.pyqtSlot(name='on_actionTNMH_triggered')
def show_tg_dialog(self):
if self._tg_dialog is None:
self._tg_dialog = TgCalculator(self.management, parent=self)
self._tg_dialog.newData.connect(self.management.addTg)
else:
self._tg_dialog()
self._tg_dialog.show()

View File

@ -18,6 +18,7 @@ from nmreval.math.smooth import smooth
from nmreval.nmr.relaxation import Relaxation from nmreval.nmr.relaxation import Relaxation
from ..Qt import QtCore, QtWidgets from ..Qt import QtCore, QtWidgets
from ..lib import Relations
from ..lib.undos import * from ..lib.undos import *
from ..data.container import * from ..data.container import *
from ..io.filereaders import QFileReader from ..io.filereaders import QFileReader
@ -57,11 +58,18 @@ class GraphDict(OrderedDict):
def list(self): def list(self):
return [(k, v.title) for k, v in self.items()] return [(k, v.title) for k, v in self.items()]
def active(self, key: str): def active(self, key: str, return_val: str = 'both'):
if key: if not key:
return [(self._data[i].id, self._data[i].name) for i in self[key]]
else:
return [] return []
else:
if return_val == 'both':
return [(self._data[i].id, self._data[i].name) for i in self[key]]
elif return_val == 'id':
return [self._data[i].id for i in self[key]]
elif return_val == 'name':
return [self._data[i].name for i in self[key]]
else:
raise ValueError(f'return_val got wrong value {return_val!r}')
def current_sets(self, key: str): def current_sets(self, key: str):
if key: if key:
@ -74,10 +82,10 @@ class UpperManagement(QtCore.QObject):
newGraph = QtCore.pyqtSignal() newGraph = QtCore.pyqtSignal()
restoreGraph = QtCore.pyqtSignal(str) restoreGraph = QtCore.pyqtSignal(str)
deleteGraph = QtCore.pyqtSignal(str) deleteGraph = QtCore.pyqtSignal(str)
newData = QtCore.pyqtSignal(list, str) newData = QtCore.pyqtSignal([list, str], [list, str, bool])
deleteData = QtCore.pyqtSignal(str) deleteData = QtCore.pyqtSignal(list)
dataChanged = QtCore.pyqtSignal(str) dataChanged = QtCore.pyqtSignal(str)
fitFinished = QtCore.pyqtSignal(list) fitFinished = QtCore.pyqtSignal(list, 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)
@ -110,7 +118,7 @@ class UpperManagement(QtCore.QObject):
self.counter = 0 self.counter = 0
self.data = OrderedDict() self.data = OrderedDict()
self.window = window self.window = window
self.current_graph = '' self.current_graph = None
self.graphs = GraphDict(self.data) self.graphs = GraphDict(self.data)
self.namespace = None self.namespace = None
self.undostack = QtWidgets.QUndoStack() self.undostack = QtWidgets.QUndoStack()
@ -147,6 +155,10 @@ class UpperManagement(QtCore.QObject):
def active_sets(self): def active_sets(self):
return self.graphs.active(self.current_graph) return self.graphs.active(self.current_graph)
@property
def active_id(self):
return self.graphs.active(self.current_graph, return_val='id')
def get_attributes(self, graph_id: str, attr: str) -> dict[str, Any]: def get_attributes(self, graph_id: str, attr: str) -> dict[str, Any]:
return {self.data[i].id: getattr(self.data[i], attr) for i in self.graphs[graph_id].sets} return {self.data[i].id: getattr(self.data[i], attr) for i in self.graphs[graph_id].sets}
@ -180,7 +192,10 @@ class UpperManagement(QtCore.QObject):
graph.active = active graph.active = active
graph.listWidget.blockSignals(True) graph.listWidget.blockSignals(True)
for i, l in enumerate(g['in_legend']): for i, l in enumerate(g['in_legend']):
graph.listWidget.item(i).setCheckState(l) try:
graph.listWidget.item(i).setCheckState(QtCore.Qt.Checked if l else QtCore.Qt.Unchecked)
except AttributeError:
pass
graph.listWidget.blockSignals(False) graph.listWidget.blockSignals(False)
# set unchecked in tree and hide/show in plot # set unchecked in tree and hide/show in plot
@ -231,9 +246,17 @@ class UpperManagement(QtCore.QObject):
for k in plotkeys: for k in plotkeys:
self.data[k].graph = gid self.data[k].graph = gid
@QtCore.pyqtSlot(str) @QtCore.pyqtSlot(list)
def plot_from_graph(self, key: str): def plot_from_graph(self, key: list[str]):
self.graphs[self.data[key].graph].remove(key) sort_graph = {}
for sid in key:
v = self.data[sid].graph
if v not in sort_graph:
sort_graph[v] = []
sort_graph[v].append(sid)
for gid, sets in sort_graph.items():
self.graphs[gid].remove(sets)
@QtCore.pyqtSlot(list, str, str) @QtCore.pyqtSlot(list, str, str)
def move_sets(self, sets: list, dest: str, src: (str|list), pos: int = -1): def move_sets(self, sets: list, dest: str, src: (str|list), pos: int = -1):
@ -256,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)
@ -270,12 +296,26 @@ 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()
def delete_sets(self, rm_sets: list = None): def delete_sets(self, rm_sets: list = None):
rm_graphs = [] rm_graphs = []
@ -284,12 +324,29 @@ class UpperManagement(QtCore.QObject):
self.undostack.beginMacro('Delete') self.undostack.beginMacro('Delete')
rm_set_by_graph = {}
for k in rm_sets[::-1]: for k in rm_sets[::-1]:
if k in self.data: if k in self.data:
cmd = DeleteCommand(self.data, k, self.newData, self.deleteData) parent_graph = self.data[k].graph
self.undostack.push(cmd) if parent_graph not in rm_set_by_graph:
else: rm_set_by_graph[parent_graph] = set()
rm_set_by_graph[parent_graph].add(k)
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:
logger.warning(f'delete_sets: {k} is not in data or graph found')
for gid, sid_list in rm_set_by_graph.items():
cmd = DeleteCommand(self.data, list(sid_list), self.graphs, gid, self.newData, self.deleteData)
self.undostack.push(cmd)
for k in rm_graphs: for k in rm_graphs:
cmd = DeleteGraphCommand(self.graphs, k, self.restoreGraph, self.deleteGraph) cmd = DeleteGraphCommand(self.graphs, k, self.restoreGraph, self.deleteGraph)
@ -306,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:
@ -318,11 +376,12 @@ class UpperManagement(QtCore.QObject):
if joined is None: if joined is None:
joined = data_i.copy() joined = data_i.copy()
else: else:
joined.append(data_i.x, data_i.y, data_i.y_err) joined.append(data_i.data.x, data_i.data.y, y_err=data_i.data.y_err, mask=data_i.data.mask)
name_set.add(data_i.name) name_set.add(data_i.name)
group_set.add(data_i.group) group_set.add(data_i.group)
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)
@ -333,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):
""" """
@ -348,12 +409,16 @@ class UpperManagement(QtCore.QObject):
def change_visibility(self, selected: list, deselected: list): def change_visibility(self, selected: list, deselected: list):
"""Change status of list of ids after status change in datawidget""" """Change status of list of ids after status change in datawidget"""
for item_list, func in [(selected, 'show_item'), (deselected, 'hide_item')]:
grouping = {}
for s in item_list:
g = self.data[s].graph
if g not in grouping:
grouping[g] = []
grouping[g].append(s)
for s in selected: for k, v in grouping.items():
self.graphs[self.data[s].graph].show_item([s]) getattr(self.graphs[k], func)(v)
for d in deselected:
self.graphs[self.data[d].graph].hide_item([d])
@QtCore.pyqtSlot(str, str) @QtCore.pyqtSlot(str, str)
def change_keys(self, identifier: str, name: str): def change_keys(self, identifier: str, name: str):
@ -363,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))
@ -377,78 +443,140 @@ class UpperManagement(QtCore.QObject):
self.undostack.push(single_undo) self.undostack.push(single_undo)
self.undostack.endMacro() self.undostack.endMacro()
def cut(self): def edit_signals(self: UpperManagement, args: list[tuple]) -> None:
self.undostack.beginMacro('Edit signals')
for sid in self.graphs[self.current_graph]:
single_undo = EditCommand(self.data[sid], *args)
self.undostack.push(single_undo)
self.undostack.endMacro()
def cut(self, 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):
for d in self.data.values(): for d in self.data.values():
d.mask = np.ones_like(d.mask, dtype=bool) d.mask = np.ones_like(d.mask, dtype=bool)
def start_fit(self, parameter: dict, links: list, fit_options: dict): def prepare_fit(self, parameter: dict, links: list, fit_options: dict) -> bool:
if self._fit_active: if self._fit_active:
return return False
self.__fit_options = (parameter, links, fit_options) self.__fit_options = (parameter, links, fit_options)
fitter = FitRoutine() self.fitter = FitRoutine()
models = {} models = {}
fit_limits = fit_options['limits'] fit_limits = fit_options['limits']
fit_mode = fit_options['fit_mode'] fit_mode = fit_options['fit_mode']
we = fit_options['we'] we_option = fit_options['we']
self.fitter.fitmethod = fit_mode
# 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
try:
for model_id, model_p in parameter.items(): for model_id, model_p in parameter.items():
m = Model(model_p['func']) m = model_p['func']
models[model_id] = m models[model_id] = m
m_complex = model_p['complex'] m_complex = model_p['complex']
for set_id, set_params in model_p['parameter'].items(): for set_id in list_ids:
if set_id not in model_p['data_parameter']:
continue
try:
data_i = self.data[set_id] data_i = self.data[set_id]
if we.lower() == 'deltay': except KeyError as e:
raise KeyError(f'{set_id} not found') from e
set_params = model_p['data_parameter'][set_id]
if we_option.lower() == 'deltay':
we = data_i.y_err**2 we = data_i.y_err**2
else:
we = we_option
if m_complex is None or m_complex == 1: if m_complex is None or m_complex == 1:
# 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:
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:
raise Exception(f'Setting data failed for {data_i.name}') from e
d.set_model(m) d.set_model(m)
d.set_parameter(set_params[0], var=model_p['var'], try:
lb=model_p['lb'], ub=model_p['ub'], d.set_parameter(set_params[0], fun_kwargs=set_params[1])
fun_kwargs=set_params[1]) except Exception as e:
raise Exception('Setting parameter failed') from e
fitter.add_data(d) self.fitter.add_data(d)
model_globs = model_p['glob']
if model_globs:
m.set_global_parameter(**model_p['glob'])
for links_i in links: for links_i in links:
fitter.set_link_parameter((models[links_i[0]], links_i[1]), self.fitter.set_link_parameter((models[links_i[0]], links_i[1]),
(models[links_i[2]], links_i[3])) (models[links_i[2]], links_i[3]))
return True
except Exception as e:
logger.error(f'Fit preparation failed with error: {e.args}')
QtWidgets.QMessageBox.warning(QtWidgets.QWidget(),
'Fit prep failed',
f'Fit preparation failed:\n'
'Message:\n'
f'{e.args}:\n')
return False
def start_fit(self):
with busy_cursor(): with busy_cursor():
self.fit_worker = FitWorker(fitter, fit_mode) self.fit_worker = FitWorker(self.fitter)
self.fit_thread = QtCore.QThread() self.fit_thread = QtCore.QThread()
self.fit_worker.moveToThread(self.fit_thread) self.fit_worker.moveToThread(self.fit_thread)
@ -464,13 +592,25 @@ class UpperManagement(QtCore.QObject):
@QtCore.pyqtSlot(list, bool) @QtCore.pyqtSlot(list, bool)
def end_fit(self, result: list, success: bool): def end_fit(self, result: list, success: bool):
print('FIT FINISHED')
if success: if success:
self.fitFinished.emit(result) logger.info('Successful fit')
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:
QtWidgets.QMessageBox.warning(QtWidgets.QWidget(), 'Fit failed', e = result[0]
'Fit kaput with exception: \n' + "\n".join(result[0])) logger.exception(e, exc_info=True)
self.fitFinished.emit([]) QtWidgets.QMessageBox.warning(
QtWidgets.QWidget(),
'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)
@ -482,7 +622,8 @@ class UpperManagement(QtCore.QObject):
for set_id, set_parameter in parameter.items(): for set_id, set_parameter in parameter.items():
new_values = [v.value for v in res[set_id].parameter.values()] new_values = [v.value for v in res[set_id].parameter.values()]
parameter[set_id] = (new_values, set_parameter[1]) parameter[set_id] = (new_values, set_parameter[1])
self.start_fit(*self.__fit_options) if self.prepare_fit(*self.__fit_options):
self.start_fit()
def make_fits(self, res: dict, opts: list, param_graph: str, show_fit: bool, parts: bool, extrapolate: list) -> None: def make_fits(self, res: dict, opts: list, param_graph: str, show_fit: bool, parts: bool, extrapolate: list) -> None:
""" """
@ -507,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()
@ -558,6 +699,9 @@ class UpperManagement(QtCore.QObject):
subfunc.name += data_name subfunc.name += data_name
sub_f_id = self.add(subfunc, color=col, linestyle=LineStyle.Dashed, symbol=SymbolStyle.No) sub_f_id = self.add(subfunc, color=col, linestyle=LineStyle.Dashed, symbol=SymbolStyle.No)
self[sub_f_id].add_relation(Relations.isFitPartOf, f_id)
self[f_id].add_relation(Relations.hasFitPart, sub_f_id)
f_id_list.append(sub_f_id) f_id_list.append(sub_f_id)
gid = data_k.graph gid = data_k.graph
self.delete_sets(tobedeleted) self.delete_sets(tobedeleted)
@ -567,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']
@ -600,11 +748,11 @@ class UpperManagement(QtCore.QObject):
if not graph_id: if not graph_id:
graph_id = '' graph_id = ''
self.newData.emit(p_id_list, graph_id) self.newData[list, str, bool].emit(p_id_list, graph_id, True)
def save_fit_parameter(self, fname: str | pathlib.Path, fit_sets: list[str] = None): def save_fit_parameter(self, fname: str | pathlib.Path, fit_sets: list[str] = None):
if fit_sets is None: if fit_sets is None:
fit_sets = [s for (s, _) in self.active_sets] fit_sets = [s for s in self.active_id]
for set_id in fit_sets: for set_id in fit_sets:
data = self.data[set_id] data = self.data[set_id]
@ -626,12 +774,10 @@ class UpperManagement(QtCore.QObject):
else: else:
continue continue
for key, pvalue in data.parameter.items(): for fit_key, pvalue in data.parameter.items():
name = pvalue.full_name
fit_key = key + data.model_name
if fit_key not in fit_dict: if fit_key not in fit_dict:
fit_dict[fit_key] = [[], name] fit_dict[fit_key] = [[], fit_key]
err = 0 if pvalue.error is None else pvalue.error err = 0 if pvalue.error is None else pvalue.error
@ -645,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():
@ -668,7 +827,7 @@ class UpperManagement(QtCore.QObject):
key = self.add(Points(x=new_x_axis, y=_temp[:, i, 1], y_err=_temp[:, i, 2], name=label)) key = self.add(Points(x=new_x_axis, y=_temp[:, i, 1], y_err=_temp[:, i, 2], name=label))
key_list.append(key) key_list.append(key)
self.newData.emit(key_list, gid) self.newData[list, str, bool].emit(key_list, gid, True)
@QtCore.pyqtSlot(list) @QtCore.pyqtSlot(list)
def get_properties(self, sid: list) -> dict: def get_properties(self, sid: list) -> dict:
@ -733,12 +892,67 @@ 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)
def binning(self, digits: float):
_active = self.graphs[self.current_graph].active
new_data = []
for sid in _active:
key = self.add(self.data[sid].binning(digits=digits))
new_data.append(key)
self.newData.emit(new_data, self.current_graph)
@QtCore.pyqtSlot(dict, str)
def addTg(self, dic: dict, dtype: str):
graph_id = self.current_graph if dtype == 'tg' else dic.pop('graph')
set_id_list = []
if dtype == 'hodge':
for v in dic.values():
set_id_list.append(self.add(v))
else:
for k, (data, lines) in dic.items():
p: ExperimentContainer = self[k]
col = p.plot_real.linecolor
if data is not None:
set_id_list.append(self.add(data, color=col))
if dtype == 'tnmh':
if lines is not None:
lines = [lines]
else:
lines = []
for line in lines:
set_id = self.add(line, color=col)
self[set_id].setLine(style=LineStyle.Dashed)
self[set_id].setSymbol(symbol=SymbolStyle.No)
set_id_list.append(set_id)
self.newData.emit(set_id_list, graph_id)
@QtCore.pyqtSlot(int, dict) @QtCore.pyqtSlot(int, dict)
def smooth_data(self, npoints, param_kwargs): def smooth_data(self, npoints, param_kwargs):
_active = self.graphs[self.current_graph].active _active = self.graphs[self.current_graph].active
@ -771,13 +985,10 @@ class UpperManagement(QtCore.QObject):
d_k = self.data[k] d_k = self.data[k]
if copy_data is None: if copy_data is None:
d_k.x = d_k.x*v[1][0] + v[0][0] d_k.shift_scale(v[0], v[1])
d_k.y = d_k.y*v[1][1] + v[0][1]
else: else:
new_data = d_k.copy(full=True) new_data = d_k.copy(full=True)
new_data.update({'shift': v[0], 'scale': v[1]}) new_data.shift_scale(v[0], v[1])
new_data.data.x = new_data.x*v[1][0] + v[0][0]
new_data.y = new_data.y*v[1][1] + v[0][1]
sid = self.add(new_data) sid = self.add(new_data)
sid_list.append(sid) sid_list.append(sid)
@ -833,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))
@ -867,17 +1078,24 @@ 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:
self.undostack.beginMacro('Evaluate expression') self.undostack.beginMacro('Evaluate expression')
failures = [] failures = []
for sid in set_ids: for i, g in enumerate(self.graphs.values()):
for j, sid in enumerate(g.sets):
if sid not in set_ids:
continue
data_i = self.data[sid] data_i = self.data[sid]
try: try:
# use a copy of original namespace # use a copy of original namespace
new_data = data_i.eval_expression(cmds, dict(ns)) new_data = data_i.eval_expression(cmds, dict(ns), i=i, j=j)
if overwrite: if overwrite:
cmd = EvalCommand(self.data, sid, new_data, 'Evaluate expression') cmd = EvalCommand(self.data, sid, new_data, 'Evaluate expression')
self.undostack.push(cmd) self.undostack.push(cmd)
@ -886,7 +1104,7 @@ class UpperManagement(QtCore.QObject):
self.data[new_id[0]].data = new_data self.data[new_id[0]].data = new_data
except Exception as e: except Exception as e:
failures.append((data_i, e)) failures.append((data_i, e))
print(str(data_i) + ' failed with Exception: ' + ''.join(e.args)) logger.warning(str(data_i) + ' failed with Exception: ' + ''.join(e.args))
continue continue
if overwrite: if overwrite:
@ -894,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())
@ -921,9 +1154,9 @@ class UpperManagement(QtCore.QObject):
self.newData.emit([s_id], graph) self.newData.emit([s_id], graph)
except Exception as err: except Exception as err:
print('Creation failed with error: ' + ', '.join(err.args)) logger.exception('Creation failed with error: ' + ', '.join(err.args))
err_msg = QtWidgets.QMessageBox(parent=self.sender()) err_msg = QtWidgets.QMessageBox(parent=self.sender())
err_msg.setText('One or more errors occured during evaluation.') err_msg.setText('One or more errors occurred during evaluation.')
err_msg.setDetailedText('Creation failed with error: ' + ', '.join(err.args)) err_msg.setDetailedText('Creation failed with error: ' + ', '.join(err.args))
err_msg.exec() err_msg.exec()
@ -932,7 +1165,7 @@ class UpperManagement(QtCore.QObject):
def show_statistics(self, mode): def show_statistics(self, mode):
x, y, = [], [] x, y, = [], []
for i, _ in self.active_sets: for i in self.active_id:
_temp = self.data[i] _temp = self.data[i]
try: try:
x.append(float(_temp.name)) x.append(float(_temp.name))
@ -943,7 +1176,7 @@ class UpperManagement(QtCore.QObject):
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def calc_magn(self): def calc_magn(self):
new_id = [] new_id = []
for k, _ in self.active_sets: for k in self.active_id:
dataset = self.data[k] dataset = self.data[k]
if isinstance(dataset, SignalContainer): if isinstance(dataset, SignalContainer):
new_value = dataset.copy(full=True) new_value = dataset.copy(full=True)
@ -955,7 +1188,7 @@ class UpperManagement(QtCore.QObject):
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def center(self): def center(self):
new_id = [] new_id = []
for k, _ in self.active_sets: for k in self.active_id:
new_value = self.data[k].copy(full=True) new_value = self.data[k].copy(full=True)
new_value.x -= new_value.x[np.argmax(new_value.y.real)] new_value.x -= new_value.x[np.argmax(new_value.y.real)]
new_id.append(self.add(new_value)) new_id.append(self.add(new_value))
@ -994,7 +1227,7 @@ class UpperManagement(QtCore.QObject):
def bds_deriv(self): def bds_deriv(self):
new_sets = [] new_sets = []
for (set_id, _) in self.active_sets: for set_id in self.active_id:
data_i = self.data[set_id] data_i = self.data[set_id]
diff = data_i.data.diff(log=True) diff = data_i.data.diff(log=True)
new_data = Points(x=diff.x, y=-np.pi/2*diff.y.real) new_data = Points(x=diff.x, y=-np.pi/2*diff.y.real)
@ -1007,13 +1240,14 @@ class UpperManagement(QtCore.QObject):
def logft(self, **kwargs): def logft(self, **kwargs):
new_sets = [] new_sets = []
ft_mode = kwargs['ft_mode'] ft_mode = kwargs['ft_mode']
return_f = kwargs['return_f']
for set_id in kwargs['sets']: for set_id in kwargs['sets']:
data_i = self.data[set_id] data_i = self.data[set_id]
if ft_mode in ['cos', 'sin']: if ft_mode in ['cos', 'sin']:
new_data = Points(*logft(data_i.x, data_i.y, mode=ft_mode)) new_data = Points(*logft(data_i.x, data_i.y, mode=ft_mode, return_f=return_f))
else: else:
new_data = Signal(*logft(data_i.x, data_i.y, mode=ft_mode)) new_data = Signal(*logft(data_i.x, data_i.y, mode=ft_mode, return_f=return_f))
new_sets.append(self.add(new_data, color=data_i['color'], symbol=data_i['symbol'], line=data_i['line'])) new_sets.append(self.add(new_data, color=data_i['color'], symbol=data_i['symbol'], line=data_i['line']))
self.data[new_sets[-1]].update(data_i.data.meta) self.data[new_sets[-1]].update(data_i.data.meta)
@ -1021,7 +1255,7 @@ class UpperManagement(QtCore.QObject):
self.newData.emit(new_sets, kwargs['graph']) self.newData.emit(new_sets, kwargs['graph'])
def skip_points(self, offset: int, step: int, invert: bool = False, copy: bool = False): def skip_points(self, offset: int, step: int, invert: bool = False, copy: bool = False):
for k, _ in self.active_sets: for k in self.active_id:
src = self.data[k] src = self.data[k]
if invert: if invert:
mask = np.mod(np.arange(offset, src.x.size+offset), step) != 0 mask = np.mod(np.arange(offset, src.x.size+offset), step) != 0
@ -1047,9 +1281,11 @@ class UpperManagement(QtCore.QObject):
params = opts['pts'] params = opts['pts']
if len(params) == 4: if len(params) == 4:
if params[3]: if params[3]:
_x = x1 = np.geomspace(params[0], params[1], num=params[2]) _x = np.geomspace(params[0], params[1], num=params[2])
x1 = np.geomspace(params[0], params[1], num=params[2])
else: else:
_x = x1 = np.linspace(params[0], params[1], num=params[2]) _x = np.linspace(params[0], params[1], num=params[2])
x1 = np.linspace(params[0], params[1], num=params[2])
if opts['axis1'] in ['t', 'invt1000']: if opts['axis1'] in ['t', 'invt1000']:
t_p = opts['t_param'] t_p = opts['t_param']
@ -1068,21 +1304,23 @@ class UpperManagement(QtCore.QObject):
_x = x1 = self.data[params[0]].x _x = x1 = self.data[params[0]].x
x2 = opts['val2'] x2 = opts['val2']
sd = opts['spec_dens'] sd = opts['spec_dens']
sd_param = [self.data[p].y.real if isinstance(p, str) else p for p in opts['sd_param'][0]] sd_param = list(zip(*[self.data[p].y.real if isinstance(p, str) else [p]*len(_x) for p in opts['sd_param'][0]]))
sd.convert(_x, *sd_param, from_=opts['tau_type'], to_='raw')
relax = Relaxation() relax = Relaxation()
relax.set_distribution(sd, parameter=sd_param, keywords=opts['sd_param'][1]) relax.set_distribution(sd, keywords=opts['sd_param'][1])
cp_param = [self.data[p].y.real if isinstance(p, str) else p for p in opts['cp_param'][0]] cp_param = list(zip(*[self.data[p].y.real if isinstance(p, str) else [p]*len(_x) for p in opts['cp_param'][0]]))
relax.set_coupling(opts['coup'], parameter=cp_param, keywords=opts['cp_param'][1]) # relax.set_coupling(opts['coup'], parameter=cp_param, keywords=opts['cp_param'][1])
if opts['out'] == 't1': relax_func = relax.t1 if opts['out'] == 't1' else relax.t2
y = relax.t1(x2, _x) y = np.zeros(_x.size)
else:
y = relax.t2(x2, _x) for i in range(_x.size):
_x_i = sd.convert(_x[i], *sd_param[i], from_=opts['tau_type'], to_='raw')
relax.dist_parameter = sd_param[i]
relax.set_coupling(opts['coup'], parameter=cp_param[i], keywords=opts['cp_param'][1])
y[i] = relax_func(x2, _x_i)
pts = Points(x1, y, name=sd.name) pts = Points(x1, y, name=sd.name)
pts.meta.update(opts) pts.meta.update(opts)
@ -1176,18 +1414,17 @@ class UpperManagement(QtCore.QObject):
class FitWorker(QtCore.QObject): class FitWorker(QtCore.QObject):
finished = QtCore.pyqtSignal(list, bool) finished = QtCore.pyqtSignal(list, bool)
def __init__(self, fitter, mode): def __init__(self, fitter):
super().__init__() super().__init__()
self.fitter = fitter self.fitter = fitter
self.mode = mode
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def run(self): def run(self):
try: try:
res = self.fitter.run(mode=self.mode) res = self.fitter.run()
success = True success = True
except Exception as e: except Exception as e:
res = [e.args] res = [e]
success = False success = False
self.finished.emit(res, success) self.finished.emit(res, success)

View File

@ -0,0 +1,21 @@
from ..Qt import QtWidgets, QtGui
class BinningWindow(QtWidgets.QDialog):
def __init__(self, parent=None):
super().__init__(parent=parent)
layout = QtWidgets.QFormLayout()
self.label = QtWidgets.QLabel('Digits (negative values position left of decimal point)')
self.spinbox = QtWidgets.QLineEdit()
self.spinbox.setValidator(QtGui.QDoubleValidator())
self.spinbox.setText('1')
layout.addRow(self.label, self.spinbox)
self.dialogbox = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
self.dialogbox.accepted.connect(self.accept)
self.dialogbox.rejected.connect(self.reject)
layout.addWidget(self.dialogbox)
self.setLayout(layout)

View File

@ -31,14 +31,36 @@ class QEvalDialog(QtWidgets.QDialog, Ui_CalcDialog):
self.namespace_widget.set_namespace(self.namespace) self.namespace_widget.set_namespace(self.namespace)
def add_data(self, data): def add_data(self, data):
self.listWidget.clear() # self.listWidget.clear()
tmp = []
while self.listWidget.count():
tmp.append(self.listWidget.takeItem(0))
for set_id, name in data: for set_id, name in data:
# search if set was used before
new_one = True
for i in range(len(tmp)):
w = tmp[i]
if w.data(QtCore.Qt.UserRole) == set_id:
w.setText(name)
self.listWidget.addItem(w)
tmp.pop(i)
new_one = False
break
# new set, create item
if new_one:
item = QtWidgets.QListWidgetItem(name) item = QtWidgets.QListWidgetItem(name)
item.setData(QtCore.Qt.UserRole, set_id) item.setData(QtCore.Qt.UserRole, set_id)
item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable) item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable)
item.setCheckState(QtCore.Qt.Checked) item.setCheckState(QtCore.Qt.Checked)
self.listWidget.addItem(item) self.listWidget.addItem(item)
while len(tmp):
# delete remaining ListWidgetItems
w = tmp.pop()
del w
def set_graphs(self, graphs: list): def set_graphs(self, graphs: list):
self.graph_comboBox.clear() self.graph_comboBox.clear()
self.graph_comboBox.addItem('New graph', userData='') self.graph_comboBox.addItem('New graph', userData='')

View File

@ -16,16 +16,19 @@ class QDeriveIntegrate(QtWidgets.QDialog, Ui_Dialog):
self.start_lineedit.setValidator(QtGui.QDoubleValidator()) self.start_lineedit.setValidator(QtGui.QDoubleValidator())
self.stop_lineedit.setValidator(QtGui.QDoubleValidator()) self.stop_lineedit.setValidator(QtGui.QDoubleValidator())
self.ft_comboBox.hide() self.ft_comboBox.hide()
self.freq_box.hide()
elif self.mode == 'd': elif self.mode == 'd':
self.setWindowTitle('Differentiation dialog') self.setWindowTitle('Differentiation dialog')
self.widget.hide() self.widget.hide()
self.ft_comboBox.hide() self.ft_comboBox.hide()
self.freq_box.hide()
elif self.mode == 'l': elif self.mode == 'l':
self.setWindowTitle('Logarithmic FT dialog') self.setWindowTitle('Logarithmic FT dialog')
self.log_checkbox.hide() self.log_checkbox.hide()
self.widget.hide() self.widget.hide()
self.freq_box.show()
else: else:
raise ValueError(f'Unknown mode {mode}, use "d", "i", or "l".') raise ValueError(f'Unknown mode {mode}, use "d", "i", or "l".')
@ -54,8 +57,10 @@ class QDeriveIntegrate(QtWidgets.QDialog, Ui_Dialog):
self.stop_lineedit.setEnabled(full_range != QtCore.Qt.Checked) self.stop_lineedit.setEnabled(full_range != QtCore.Qt.Checked)
def get_options(self): def get_options(self):
opts = {'graph': '' if self.newgraph_checkbox.isChecked() else self.graph_combobox.currentData(), opts = {
'mode': self.mode, 'sets': []} 'graph': '' if self.newgraph_checkbox.isChecked() else self.graph_combobox.currentData(),
'mode': self.mode, 'sets': []
}
if self.mode == 'i': if self.mode == 'i':
start = None start = None
@ -75,6 +80,7 @@ class QDeriveIntegrate(QtWidgets.QDialog, Ui_Dialog):
if self.mode == 'l': if self.mode == 'l':
opts['ft_mode'] = ['cos', 'sin', 'complex'][self.ft_comboBox.currentIndex()] opts['ft_mode'] = ['cos', 'sin', 'complex'][self.ft_comboBox.currentIndex()]
opts['return_f'] = self.freq_box.isChecked()
else: else:
opts['log'] = self.log_checkbox.isChecked() opts['log'] = self.log_checkbox.isChecked()

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] self.specdens = [ColeCole, ColeDavidson, HavriliakNegami, KWW, LogGaussian, GGAlpha]
self.coupling = [Quadrupolar, HomoDipolar, Czjzek] self.coupling = [Quadrupolar, HomoDipolar, Czjzek]
self.tau_parameter = [] self.tau_parameter = []
@ -131,7 +131,7 @@ class QRelaxCalc(QtWidgets.QDialog, Ui_Dialog):
self.verticalLayout_3.addWidget(_temp) self.verticalLayout_3.addWidget(_temp)
def get_taus(self, dic: dict): def get_taus(self, dic: dict):
dic['tau_type'] = {0: 'raw', 1: 'mean', 2: 'peak', 3: 'logmean'}[self.xtype_combobox.currentIndex()] dic['tau_type'] = {0: 'raw', 1: 'peak', 2: 'mean', 3: 'logmean'}[self.xtype_combobox.currentIndex()]
dic['axis1'] = {self.radioButton: 'tau', self.radioButton_2: 'omega', dic['axis1'] = {self.radioButton: 'tau', self.radioButton_2: 'omega',
self.radioButton_3: 't', self.radioButton_4: 'invt1000'}[self.buttonGroup.checkedButton()] self.radioButton_3: 't', self.radioButton_4: 'invt1000'}[self.buttonGroup.checkedButton()]
@ -199,3 +199,9 @@ class QRelaxCalc(QtWidgets.QDialog, Ui_Dialog):
def accept(self): def accept(self):
self.calc_relaxation() self.calc_relaxation()
super().accept() super().accept()
@QtCore.pyqtSlot(QtWidgets.QAbstractButton)
def on_buttonBox_clicked(self, button: QtWidgets.QAbstractButton):
role = self.buttonBox.buttonRole(button)
if role == self.buttonBox.ApplyRole:
self.calc_relaxation()

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())
@ -61,6 +67,8 @@ class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
self.freq_combox.currentIndexChanged.connect(lambda x: self.update_model()) self.freq_combox.currentIndexChanged.connect(lambda x: self.update_model())
self.freq_spinbox.valueChanged.connect(lambda x: self.update_model()) self.freq_spinbox.valueChanged.connect(lambda x: self.update_model())
self.checkBox_interpol.setVisible(False)
self.update_specdens(0) self.update_specdens(0)
self.update_coupling(0) self.update_coupling(0)
@ -81,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)
@ -108,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)
@ -131,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
@ -217,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()
@ -227,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)
@ -271,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')
@ -290,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])
@ -330,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

@ -0,0 +1,175 @@
/* integrands used in quadrature integration with scipy's LowLevelCallables */
#include <math.h>
const double KB = 8.617333262145179e-05;
/* FFHS functions */
double ffhsSD(double x, void *user_data) {
double *c = (double *)user_data;
double omega = c[0];
double tau = c[1];
double u = x*x;
double res = u*u * tau;
res /= 81. + 9.*u - 2.*u*u + u*u*u;
res /= u*u + omega*omega * tau*tau;
return res;
}
/* log-gaussian functions */
double logNormalDist(double tau, double tau0, double sigma) {
return exp(- pow((log(tau/tau0) / sigma), 2) / 2.) / sqrt(2*M_PI)/sigma;
}
double logGaussian_imag_high(double u, void *user_data) {
double *c = (double *)user_data;
double omega = c[0];
double tau = c[1];
double sigma = c[2];
double uu = exp(-u);
double dist = logNormalDist(1./uu, tau, sigma);
return dist * omega * uu / (pow(uu, 2) + pow(omega, 2));
}
double logGaussian_imag_low(double u, void *user_data) {
double *c = (double *)user_data;
double omega = c[0];
double tau = c[1];
double sigma = c[2];
double uu = exp(u);
double dist = logNormalDist(uu, tau, sigma);
return dist * omega * uu / (1. + pow(omega*uu, 2));
}
double logGaussian_real_high(double u, void *user_data) {
double *c = (double *)user_data;
double omega = c[0];
double tau = c[1];
double sigma = c[2];
double uu = exp(-2.*u);
double dist = logNormalDist(exp(uu), tau, sigma);
return dist * uu / (uu + pow(omega, 2));
}
double logGaussian_real_low(double u, void *user_data) {
double *c = (double *)user_data;
double omega = c[0];
double tau = c[1];
double sigma = c[2];
double uu = exp(u);
double dist = logNormalDist(uu, tau, sigma);
return dist / (1. + pow(omega*uu, 2));
}
double logGaussianCorrelation(double x, void *user_data) {
double *c = (double *)user_data;
double t = c[0];
double tau = c[1];
double sigma = c[2];
double uu = exp(x);
double dist = logNormalDist(uu, tau, sigma);
return dist * exp(-t/uu);
}
// functions for distribution of energy
double normalDist(double x, double x0, double sigma) {
return exp(- pow((x-x0) / sigma, 2) / 2.) / sqrt(2 * M_PI) / sigma;
}
double rate(double tau0, double ea, double t) {
return exp(-ea / t / KB) / tau0;
}
double energyDist_SD(double x, void *user_data) {
double *c = (double *)user_data;
double omega = c[0];
double tau0 = c[1];
double e_m = c[2];
double e_b = c[3];
double temp = c[4];
double r = rate(tau0, x, temp);
return r/(pow(r, 2) + pow(omega, 2)) * normalDist(x, e_m, e_b);
}
double energyDistSuscReal(double x, void *user_data) {
double *c = (double *)user_data;
double omega = c[0];
double tau0 = c[1];
double e_m = c[2];
double e_b = c[3];
double temp = c[4];
double r = rate(tau0, x, temp);
return 1 / (pow(r, 2) + pow(omega, 2)) * normalDist(x, e_m, e_b);
}
double energyDistSuscImag(double x, void *user_data) {
double *c = (double *)user_data;
double omega = c[0];
double tau0 = c[1];
double e_m = c[2];
double e_b = c[3];
double temp = c[4];
double r = rate(tau0, x, temp);
return omega * r / (pow(r, 2) + pow(omega, 2)) * normalDist(x, e_m, e_b);
}
double energyDistCorrelation(double x, void *user_data) {
double *c = (double *)user_data;
double t = c[0];
double tau0 = c[1];
double e_m = c[2];
double e_b = c[3];
double temp = c[4];
double r = rate(tau0, x, temp);
return normalDist(x, e_m, e_b) * exp(-t * r);
}
// 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);
}

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