quantui 0.5.1__tar.gz → 0.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {quantui-0.5.1 → quantui-0.6.0}/CHANGELOG.md +82 -1
- {quantui-0.5.1/quantui.egg-info → quantui-0.6.0}/PKG-INFO +2 -2
- {quantui-0.5.1 → quantui-0.6.0}/README.md +1 -1
- {quantui-0.5.1 → quantui-0.6.0}/pyproject.toml +1 -1
- {quantui-0.5.1 → quantui-0.6.0}/quantui/__init__.py +1 -1
- {quantui-0.5.1 → quantui-0.6.0}/quantui/app.py +120 -40
- {quantui-0.5.1 → quantui-0.6.0}/quantui/app_builders.py +209 -14
- {quantui-0.5.1 → quantui-0.6.0}/quantui/app_exports.py +124 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/app_formatters.py +3 -1
- {quantui-0.5.1 → quantui-0.6.0}/quantui/app_visualization.py +412 -122
- {quantui-0.5.1 → quantui-0.6.0}/quantui/live_log.py +3 -1
- {quantui-0.5.1 → quantui-0.6.0}/quantui/orbital_visualization.py +456 -47
- quantui-0.6.0/quantui/theme.py +125 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/user_settings.py +20 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/visualization_py3dmol.py +6 -1
- {quantui-0.5.1 → quantui-0.6.0}/quantui/viz_assets.py +21 -6
- {quantui-0.5.1 → quantui-0.6.0}/quantui/viz_backend_router.py +34 -8
- {quantui-0.5.1 → quantui-0.6.0/quantui.egg-info}/PKG-INFO +2 -2
- {quantui-0.5.1 → quantui-0.6.0}/quantui.egg-info/SOURCES.txt +1 -0
- {quantui-0.5.1 → quantui-0.6.0}/LICENSE +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/MANIFEST.in +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/SECURITY.md +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/analytics.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/app_analysis.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/app_history.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/app_runflow.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/ase_bridge.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/benchmarks.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/c_stderr.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/cactus.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/calc_log.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/calculator.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/cancellation.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/cli.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/comparison.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/config.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/data/js/3Dmol-min.js +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/data/js/3Dmol-min.js.LICENSE.txt +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/data/library/library.sqlite +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/data/manifests/bulk_qm9.json +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/data/manifests/curated.json +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/data/manifests/presets.json +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/descriptor_cards.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/freq_calc.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/freq_ir_workers.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/gpu_offload.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/help_content.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/ir_plot.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/issue_tracker.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/log_utils.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/molecule.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/molecule_library.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/nmr_calc.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/optimizer.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/pes_scan.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/preopt.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/progress.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/pubchem.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/reorganization_energy.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/results_storage.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/security.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/session_calc.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/structure_providers.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/tddft_calc.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/utils.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui/vib_cache.py +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui.egg-info/dependency_links.txt +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui.egg-info/entry_points.txt +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui.egg-info/requires.txt +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/quantui.egg-info/top_level.txt +0 -0
- {quantui-0.5.1 → quantui-0.6.0}/setup.cfg +0 -0
|
@@ -7,6 +7,85 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.6.0] - 2026-08-04
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Save orbital isosurfaces as images.** A **Save PNG** button sits under the
|
|
15
|
+
viewer and captures exactly what you are looking at, camera and all. Options
|
|
16
|
+
for the filename, the DPI written into the file (72–600; this sets the
|
|
17
|
+
*print* size, not the pixel count), and a transparent background for dropping
|
|
18
|
+
a figure onto a slide — the transparency applies only to the saved file, so
|
|
19
|
+
the viewer on screen is unchanged.
|
|
20
|
+
- **Tunable isosurface resolution** — Coarse/Medium/Fine/Very fine grids,
|
|
21
|
+
remembered between launches.
|
|
22
|
+
- **Live isosurface controls**: isovalue, opacity and a colour-scheme dropdown
|
|
23
|
+
(GaussView orange/blue, Avogadro/Jmol red/blue, journal yellow/blue, and
|
|
24
|
+
more). All three update the existing viewer without recomputing anything, so
|
|
25
|
+
the orientation you rotated into is preserved.
|
|
26
|
+
- **The isovalue is explained in chemical terms.** Beside the slider, QuantUI
|
|
27
|
+
reports what fraction of the orbital's density the surface actually encloses
|
|
28
|
+
— "encloses 98.4% of the density" — because an amplitude threshold is rarely
|
|
29
|
+
the question anyone has.
|
|
30
|
+
- **A Cancel button** for isosurface generation. The underlying PySCF call
|
|
31
|
+
cannot be interrupted, so the computation finishes in the background, but its
|
|
32
|
+
result is discarded and the controls come back immediately.
|
|
33
|
+
- **The molecule is shown before you generate anything**, so the panel is never
|
|
34
|
+
empty — and you can orient the structure first, since that view carries into
|
|
35
|
+
the isosurface.
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- **Orbital isosurfaces are py3Dmol only.** The Plotly renderer is no longer
|
|
40
|
+
selectable for them: it downsamples the volume by construction, and it cannot
|
|
41
|
+
carry the new image export. Plotly remains available for the molecule viewers.
|
|
42
|
+
- **Exported animations now match what you saw.** Vibrational modes and
|
|
43
|
+
optimization trajectories both export from py3Dmol — the renderer that drew
|
|
44
|
+
them on screen — instead of being rebuilt in Plotly. Exported files are also
|
|
45
|
+
now complete HTML documents declaring UTF-8, so labels no longer risk
|
|
46
|
+
mojibake when opened from disk.
|
|
47
|
+
- **For geometry optimizations, the Orbital Isosurface panel opens by default**
|
|
48
|
+
on the Analysis tab.
|
|
49
|
+
- **Isosurface viewers are framed** like the other 3-D viewers, and generating a
|
|
50
|
+
new one dims the current one in place rather than collapsing the panel.
|
|
51
|
+
|
|
52
|
+
### Fixed
|
|
53
|
+
|
|
54
|
+
- **3-D backgrounds follow the theme.** The isosurface, vibrational and
|
|
55
|
+
trajectory viewers, and the Analysis-tab molecule viewer, kept their old
|
|
56
|
+
background when switching Light/Dark until something happened to redraw them.
|
|
57
|
+
- **The page no longer jumps** when generating an isosurface.
|
|
58
|
+
- **Isosurface changes now apply correctly.** Adjusting the isovalue, opacity or
|
|
59
|
+
colours previously layered a new surface on top of the old one, so raising the
|
|
60
|
+
isovalue appeared to do nothing, lowering opacity did nothing, and switching
|
|
61
|
+
palettes made the surface progressively brighter.
|
|
62
|
+
- Very large orbital grids are dramatically lighter in the browser — the cube
|
|
63
|
+
data was being embedded three times per render.
|
|
64
|
+
|
|
65
|
+
## [0.5.2] - 2026-08-03
|
|
66
|
+
|
|
67
|
+
### Fixed
|
|
68
|
+
|
|
69
|
+
- **Dark mode now has visible panel edges.** Borders, cards, and dividers were
|
|
70
|
+
effectively invisible in Dark mode — measured at 1.14:1 against the panel
|
|
71
|
+
background, well under the 3:1 that WCAG asks of a UI component. Dark mode is
|
|
72
|
+
a whole-page colour inversion rather than a separate palette, so a light grey
|
|
73
|
+
border inverts to a near-black one on a near-black page and disappears. The
|
|
74
|
+
borders are now a mid-tone that stays visible in *both* modes (3.20:1 light,
|
|
75
|
+
4.30:1 dark). Text contrast was measured too and was never the problem — it
|
|
76
|
+
is 7.24:1 in light mode and slightly *better* inverted — so text colours are
|
|
77
|
+
deliberately unchanged.
|
|
78
|
+
|
|
79
|
+
### Added
|
|
80
|
+
|
|
81
|
+
- **Every 3-D viewer now has a border around it**, so it is clear where the
|
|
82
|
+
view ends and where the page can be scrolled without dragging the structure.
|
|
83
|
+
This covers the molecule preview, the results and analysis structure views,
|
|
84
|
+
the geometry-optimization trajectory, the vibrational-mode animation, and the
|
|
85
|
+
classical pre-optimization preview. The frame hugs the plot itself rather
|
|
86
|
+
than spanning the window width, and looks the same whichever rendering
|
|
87
|
+
backend (py3Dmol or plotlymol3d) is active.
|
|
88
|
+
|
|
10
89
|
## [0.5.1] - 2026-07-31
|
|
11
90
|
|
|
12
91
|
### Fixed
|
|
@@ -484,7 +563,9 @@ Initial public scaffolding of the QuantUI package: `quantui` package with
|
|
|
484
563
|
`calculator.py`, basic notebook launcher, Apptainer container definition,
|
|
485
564
|
MIT license, and project metadata.
|
|
486
565
|
|
|
487
|
-
[Unreleased]: https://github.com/The-Schultz-Lab/QuantUI/compare/v0.
|
|
566
|
+
[Unreleased]: https://github.com/The-Schultz-Lab/QuantUI/compare/v0.6.0...HEAD
|
|
567
|
+
[0.6.0]: https://github.com/The-Schultz-Lab/QuantUI/compare/v0.5.2...v0.6.0
|
|
568
|
+
[0.5.2]: https://github.com/The-Schultz-Lab/QuantUI/compare/v0.5.1...v0.5.2
|
|
488
569
|
[0.5.1]: https://github.com/The-Schultz-Lab/QuantUI/compare/v0.5.0...v0.5.1
|
|
489
570
|
[0.5.0]: https://github.com/The-Schultz-Lab/QuantUI/compare/v0.4.1...v0.5.0
|
|
490
571
|
[0.4.1]: https://github.com/The-Schultz-Lab/QuantUI/compare/v0.4.0...v0.4.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quantui
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: An open-source frontend for DFT and post-HF quantum chemistry with PySCF
|
|
5
5
|
Author-email: Jonathan Schultz <nccu-schultz-lab@users.noreply.github.com>
|
|
6
6
|
License: MIT License
|
|
@@ -91,7 +91,7 @@ Dynamic: license-file
|
|
|
91
91
|
|
|
92
92
|
# QuantUI
|
|
93
93
|
|
|
94
|
-
[](https://pypi.org/project/quantui/)
|
|
94
|
+
[](https://pypi.org/project/quantui/)
|
|
95
95
|
[](https://github.com/The-Schultz-Lab/QuantUI/actions/workflows/ci.yml)
|
|
96
96
|
[](https://the-schultz-lab.github.io/QuantUI/)
|
|
97
97
|
[](https://github.com/The-Schultz-Lab/QuantUI/blob/main/LICENSE)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# QuantUI
|
|
2
2
|
|
|
3
|
-
[](https://pypi.org/project/quantui/)
|
|
3
|
+
[](https://pypi.org/project/quantui/)
|
|
4
4
|
[](https://github.com/The-Schultz-Lab/QuantUI/actions/workflows/ci.yml)
|
|
5
5
|
[](https://the-schultz-lab.github.io/QuantUI/)
|
|
6
6
|
[](https://github.com/The-Schultz-Lab/QuantUI/blob/main/LICENSE)
|
|
@@ -7,7 +7,7 @@ Calculations run locally in the Jupyter session — no cluster or SLURM required
|
|
|
7
7
|
PySCF requires Linux/macOS/WSL. Windows users should use the Apptainer container.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
__version__ = "0.
|
|
10
|
+
__version__ = "0.6.0"
|
|
11
11
|
|
|
12
12
|
import logging
|
|
13
13
|
from typing import Any
|
|
@@ -32,6 +32,7 @@ import quantui
|
|
|
32
32
|
import quantui.calc_log as _calc_log
|
|
33
33
|
import quantui.issue_tracker as _issue_tracker
|
|
34
34
|
from quantui import molecule_library as _ml
|
|
35
|
+
from quantui import theme as _theme
|
|
35
36
|
from quantui.app_analysis import (
|
|
36
37
|
activate_ana_panel as _ana_activate_ana_panel,
|
|
37
38
|
)
|
|
@@ -146,6 +147,9 @@ from quantui.app_exports import (
|
|
|
146
147
|
from quantui.app_exports import (
|
|
147
148
|
on_iso_export_cube as _exp_on_iso_export_cube,
|
|
148
149
|
)
|
|
150
|
+
from quantui.app_exports import (
|
|
151
|
+
on_orb_png_captured as _exp_on_orb_png_captured,
|
|
152
|
+
)
|
|
149
153
|
from quantui.app_formatters import (
|
|
150
154
|
format_freq_result as _fmt_freq_result,
|
|
151
155
|
)
|
|
@@ -332,6 +336,12 @@ from quantui.app_visualization import (
|
|
|
332
336
|
from quantui.app_visualization import (
|
|
333
337
|
on_ir_mode_changed as _viz_on_ir_mode_changed,
|
|
334
338
|
)
|
|
339
|
+
from quantui.app_visualization import (
|
|
340
|
+
on_iso_appearance_changed as _viz_on_iso_appearance_changed,
|
|
341
|
+
)
|
|
342
|
+
from quantui.app_visualization import (
|
|
343
|
+
on_iso_cancel as _viz_on_iso_cancel,
|
|
344
|
+
)
|
|
335
345
|
from quantui.app_visualization import (
|
|
336
346
|
on_iso_generate as _viz_on_iso_generate,
|
|
337
347
|
)
|
|
@@ -359,6 +369,9 @@ from quantui.app_visualization import (
|
|
|
359
369
|
from quantui.app_visualization import (
|
|
360
370
|
render_vib_mode as _viz_render_vib_mode,
|
|
361
371
|
)
|
|
372
|
+
from quantui.app_visualization import (
|
|
373
|
+
rerender_3d_scenes_for_theme as _viz_rerender_3d_scenes_for_theme,
|
|
374
|
+
)
|
|
362
375
|
from quantui.app_visualization import (
|
|
363
376
|
show_ir_spectrum as _viz_show_ir_spectrum,
|
|
364
377
|
)
|
|
@@ -450,9 +463,6 @@ try:
|
|
|
450
463
|
from quantui.visualization_py3dmol import (
|
|
451
464
|
VIZ_STYLE_OPTIONS as _VIZ_STYLE_OPTIONS,
|
|
452
465
|
)
|
|
453
|
-
from quantui.visualization_py3dmol import (
|
|
454
|
-
display_molecule as _display_molecule,
|
|
455
|
-
)
|
|
456
466
|
from quantui.visualization_py3dmol import (
|
|
457
467
|
render_molecule_html as _render_molecule_html,
|
|
458
468
|
)
|
|
@@ -460,7 +470,6 @@ try:
|
|
|
460
470
|
VISUALIZATION_AVAILABLE = True
|
|
461
471
|
except ImportError:
|
|
462
472
|
VISUALIZATION_AVAILABLE = False
|
|
463
|
-
_display_molecule = None # type: ignore[assignment]
|
|
464
473
|
_render_molecule_html = None # type: ignore[assignment]
|
|
465
474
|
_PLOTLYMOL_VIZ = False
|
|
466
475
|
_PY3DMOL_VIZ = False
|
|
@@ -616,7 +625,7 @@ h3 {
|
|
|
616
625
|
color: #64748b !important;
|
|
617
626
|
margin: 24px 0 10px !important;
|
|
618
627
|
padding-bottom: 5px !important;
|
|
619
|
-
border-bottom: 1px solid
|
|
628
|
+
border-bottom: 1px solid __Q_BORDER__ !important;
|
|
620
629
|
}
|
|
621
630
|
|
|
622
631
|
/* Rounded corners on inputs, dropdowns, and buttons -------------------- */
|
|
@@ -632,21 +641,6 @@ h3 {
|
|
|
632
641
|
background: transparent !important;
|
|
633
642
|
}
|
|
634
643
|
|
|
635
|
-
/* 3D molecule-viewer frames — a subtle bounding box so the viewer
|
|
636
|
-
extent reads clearly. The light border inverts to dark automatically under
|
|
637
|
-
the global dark-mode invert filter. */
|
|
638
|
-
.quantui-viewer-frame {
|
|
639
|
-
border: 1px solid #c0ccd8 !important;
|
|
640
|
-
border-radius: 6px !important;
|
|
641
|
-
overflow: hidden !important;
|
|
642
|
-
}
|
|
643
|
-
/* Collapse the frame to nothing when the viewer output is empty (e.g. before
|
|
644
|
-
a calc runs) so no hollow box shows. Degrades to an always-on border where
|
|
645
|
-
:has() is unsupported. */
|
|
646
|
-
.quantui-viewer-frame:not(:has(.jp-OutputArea-child)) {
|
|
647
|
-
border-color: transparent !important;
|
|
648
|
-
}
|
|
649
|
-
|
|
650
644
|
/* Inline "calculating" spinner — shown next to slow on-demand
|
|
651
645
|
controls (e.g. orbital-isosurface generation) while work is in flight. */
|
|
652
646
|
@keyframes quantui-spin { to { transform: rotate(360deg); } }
|
|
@@ -654,13 +648,20 @@ h3 {
|
|
|
654
648
|
display: inline-block;
|
|
655
649
|
width: 14px;
|
|
656
650
|
height: 14px;
|
|
657
|
-
border: 2px solid
|
|
651
|
+
border: 2px solid __Q_BORDER__;
|
|
658
652
|
border-top-color: #2563eb;
|
|
659
653
|
border-radius: 50%;
|
|
660
654
|
animation: quantui-spin 0.7s linear infinite;
|
|
661
655
|
vertical-align: middle;
|
|
662
656
|
}
|
|
663
|
-
</style>"""
|
|
657
|
+
</style>""".replace(
|
|
658
|
+
# Sentinel substitution rather than an f-string: this block is dense with
|
|
659
|
+
# CSS braces, every one of which would need doubling. If a second sentinel
|
|
660
|
+
# is ever added, substitute the LONGER name first — "__Q_BORDER__" matches
|
|
661
|
+
# inside "__Q_BORDER_STRONG__" and would leave a dangling "_STRONG__".
|
|
662
|
+
"__Q_BORDER__",
|
|
663
|
+
_theme.BORDER,
|
|
664
|
+
)
|
|
664
665
|
|
|
665
666
|
_LAYOUT_TRAITS: frozenset[str] = frozenset(widgets.Layout.class_trait_names())
|
|
666
667
|
|
|
@@ -1639,9 +1640,31 @@ class QuantUIApp:
|
|
|
1639
1640
|
("Isosurface", "_pop_isosurface", False),
|
|
1640
1641
|
],
|
|
1641
1642
|
"geometry_opt": [
|
|
1642
|
-
|
|
1643
|
+
# ORDER MATTERS: the FIRST entry whose populator returns True and
|
|
1644
|
+
# carries auto_select=True becomes the default panel (see the rules
|
|
1645
|
+
# above). Isosurface therefore leads (requested 2026-08-04); an
|
|
1646
|
+
# earlier attempt put it last, which did nothing because Trajectory
|
|
1647
|
+
# had already claimed the selection.
|
|
1648
|
+
#
|
|
1649
|
+
# Trajectory keeps auto_select=True as the fallback: when a result
|
|
1650
|
+
# has no orbital data, _pop_isosurface returns False, Isosurface
|
|
1651
|
+
# never activates, and Trajectory becomes the default instead.
|
|
1652
|
+
# ORDER IS LOAD-BEARING TWICE OVER.
|
|
1653
|
+
#
|
|
1654
|
+
# 1. Execution: _pop_energies calls show_orbital_diagram, which is
|
|
1655
|
+
# what populates _last_orb_mo_coeff / _mol_atom / _mol_basis —
|
|
1656
|
+
# the very state _pop_isosurface checks. Energies MUST run
|
|
1657
|
+
# first, or Isosurface reports "required data is missing" on a
|
|
1658
|
+
# result that has it. (Putting Isosurface first did exactly
|
|
1659
|
+
# that, 2026-08-04.)
|
|
1660
|
+
# 2. Selection: the FIRST entry with auto_select=True that returns
|
|
1661
|
+
# True becomes the default panel. Energies is False, so
|
|
1662
|
+
# Isosurface is the first candidate and opens by default —
|
|
1663
|
+
# which is the request — while Trajectory keeps True as the
|
|
1664
|
+
# fallback for results with no orbital data.
|
|
1643
1665
|
("Energies", "_pop_energies", False),
|
|
1644
|
-
("Isosurface", "_pop_isosurface",
|
|
1666
|
+
("Isosurface", "_pop_isosurface", True),
|
|
1667
|
+
("Trajectory", "_pop_geo_trajectory", True),
|
|
1645
1668
|
],
|
|
1646
1669
|
"frequency": [
|
|
1647
1670
|
("Vibrational", "_pop_vibrational", True),
|
|
@@ -1990,6 +2013,27 @@ class QuantUIApp:
|
|
|
1990
2013
|
)
|
|
1991
2014
|
# Cube + bundle exports
|
|
1992
2015
|
self._iso_export_cube_btn.on_click(self._on_iso_export_cube)
|
|
2016
|
+
self._iso_cancel_btn.on_click(self._safe_cb(self._on_iso_cancel))
|
|
2017
|
+
# PNG capture arrives from the browser, so there is no button to bind
|
|
2018
|
+
# here — the viewer's own Save-PNG button posts into this Textarea and
|
|
2019
|
+
# ipywidgets syncs it back, firing this observer (ORBX.1).
|
|
2020
|
+
self._orb_png_inbox.observe(
|
|
2021
|
+
self._safe_cb(self._on_orb_png_captured), names="value"
|
|
2022
|
+
)
|
|
2023
|
+
# Persist the grid choice so it survives a relaunch (ORBX.2).
|
|
2024
|
+
self._iso_resolution_dd.observe(
|
|
2025
|
+
self._safe_cb(self._on_iso_resolution_changed), names="value"
|
|
2026
|
+
)
|
|
2027
|
+
# Appearance controls redraw from the cube on disk — no cubegen — so
|
|
2028
|
+
# they can respond directly rather than behind an Apply button.
|
|
2029
|
+
for _w in (
|
|
2030
|
+
self._iso_isovalue_slider,
|
|
2031
|
+
self._iso_opacity_slider,
|
|
2032
|
+
self._iso_colors_dd,
|
|
2033
|
+
# NOT _iso_png_transparent: it is an export-only option, applied at
|
|
2034
|
+
# capture time. Observing it here would change the live viewer.
|
|
2035
|
+
):
|
|
2036
|
+
_w.observe(self._safe_cb(self._on_iso_appearance_changed), names="value")
|
|
1993
2037
|
self._export_bundle_btn.on_click(self._on_export_bundle)
|
|
1994
2038
|
|
|
1995
2039
|
# ── Files tab ────────────────────────────────────────────────────────
|
|
@@ -2383,7 +2427,7 @@ class QuantUIApp:
|
|
|
2383
2427
|
body = rows[1:]
|
|
2384
2428
|
head_html = "".join(
|
|
2385
2429
|
f'<th style="padding:4px 10px;text-align:left;'
|
|
2386
|
-
f"border-bottom:1px solid
|
|
2430
|
+
f"border-bottom:1px solid {_theme.BORDER};font-size:12px;"
|
|
2387
2431
|
f'color:#1e293b">{_html.escape(str(c))}</th>'
|
|
2388
2432
|
for c in header
|
|
2389
2433
|
)
|
|
@@ -2427,7 +2471,7 @@ class QuantUIApp:
|
|
|
2427
2471
|
# reach the parent app.
|
|
2428
2472
|
iframe_html = (
|
|
2429
2473
|
'<iframe sandbox="allow-scripts" '
|
|
2430
|
-
'style="width:100%;height:400px;border:1px solid
|
|
2474
|
+
f'style="width:100%;height:400px;border:1px solid {_theme.BORDER};'
|
|
2431
2475
|
'border-radius:4px" '
|
|
2432
2476
|
f'srcdoc="{_html.escape(raw, quote=True)}"></iframe>'
|
|
2433
2477
|
)
|
|
@@ -2757,8 +2801,18 @@ class QuantUIApp:
|
|
|
2757
2801
|
_last_pes = getattr(self, "_last_pes_result", None)
|
|
2758
2802
|
if _last_pes is not None:
|
|
2759
2803
|
self._show_pes_scan_result(_last_pes)
|
|
2760
|
-
# Re-render 3D molecule
|
|
2761
|
-
|
|
2804
|
+
# Re-render BOTH 3D molecule viewers so scene_bgcolor updates
|
|
2805
|
+
# immediately. This used to call _refresh_calc_mol_viewer directly,
|
|
2806
|
+
# which covered the Calculate tab only — the Analysis-tab viewer kept
|
|
2807
|
+
# its old background until something else happened to redraw it.
|
|
2808
|
+
# _rerender_3d_views already handled both; it just was not reached from
|
|
2809
|
+
# here. Reported 2026-08-04.
|
|
2810
|
+
self._rerender_3d_views()
|
|
2811
|
+
# ...and the isosurface / vibrational viewers, which bake the same
|
|
2812
|
+
# colour into their generated HTML. Without this they keep the old
|
|
2813
|
+
# background until the user regenerates — reported 2026-08-04. Both
|
|
2814
|
+
# re-render from cached inputs; neither re-runs a calculation.
|
|
2815
|
+
_viz_rerender_3d_scenes_for_theme(self)
|
|
2762
2816
|
|
|
2763
2817
|
def _initialize_viz_state_from_preference(self) -> None:
|
|
2764
2818
|
"""Align _viz_backend and the three preference widgets with the
|
|
@@ -2871,7 +2925,7 @@ class QuantUIApp:
|
|
|
2871
2925
|
def _rerender_3d_views(self) -> None:
|
|
2872
2926
|
"""Re-render visible 3D molecule viewers using the router to pick a
|
|
2873
2927
|
backend per task. Updates the "Rendering with: X" label widgets."""
|
|
2874
|
-
if
|
|
2928
|
+
if _render_molecule_html is None:
|
|
2875
2929
|
return
|
|
2876
2930
|
|
|
2877
2931
|
# Calculate-tab molecule preview (MOLECULE_PREVIEW task).
|
|
@@ -2884,15 +2938,19 @@ class QuantUIApp:
|
|
|
2884
2938
|
if self._analysis_displayed_molecule is not None:
|
|
2885
2939
|
chosen = self._resolve_backend(VizTask.ANALYSIS_STRUCTURE_VIEW)
|
|
2886
2940
|
if chosen is not None:
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2941
|
+
# Same render path as the first draw (app_visualization.
|
|
2942
|
+
# _show_result_3d), not display(): the fragment it returns
|
|
2943
|
+
# carries the viewer's own border. Going through display()
|
|
2944
|
+
# here would silently drop that border the moment a user
|
|
2945
|
+
# toggled backends on the Analysis tab.
|
|
2946
|
+
html = _render_molecule_html(
|
|
2947
|
+
self._analysis_displayed_molecule,
|
|
2948
|
+
backend=str(chosen),
|
|
2949
|
+
style=self._viz_style,
|
|
2950
|
+
lighting=self._viz_lighting,
|
|
2951
|
+
bgcolor=self._plotly_theme_colors()["scene_bgcolor"],
|
|
2952
|
+
)
|
|
2953
|
+
self._set_html_output(self._analysis_mol_output, html)
|
|
2896
2954
|
self._update_analysis_backend_label(chosen)
|
|
2897
2955
|
|
|
2898
2956
|
def _update_analysis_backend_label(self, chosen: VizBackend) -> None:
|
|
@@ -3320,6 +3378,28 @@ class QuantUIApp:
|
|
|
3320
3378
|
def _on_export_pdb(self, btn) -> None:
|
|
3321
3379
|
_exp_on_export_pdb(self, btn)
|
|
3322
3380
|
|
|
3381
|
+
def _on_iso_cancel(self, btn) -> None:
|
|
3382
|
+
_viz_on_iso_cancel(self, btn)
|
|
3383
|
+
|
|
3384
|
+
def _on_iso_appearance_changed(self, change) -> None:
|
|
3385
|
+
_viz_on_iso_appearance_changed(self, change)
|
|
3386
|
+
|
|
3387
|
+
def _on_orb_png_captured(self, change) -> None:
|
|
3388
|
+
_exp_on_orb_png_captured(self, change)
|
|
3389
|
+
|
|
3390
|
+
def _on_iso_resolution_changed(self, change) -> None:
|
|
3391
|
+
"""Persist the isosurface grid choice.
|
|
3392
|
+
|
|
3393
|
+
Saved on change rather than at generate time so the preference sticks
|
|
3394
|
+
even if the user picks a grid and then closes the app without running
|
|
3395
|
+
anything.
|
|
3396
|
+
"""
|
|
3397
|
+
new_val = (change or {}).get("new")
|
|
3398
|
+
if not new_val or new_val == self._user_settings.viz.iso_resolution:
|
|
3399
|
+
return
|
|
3400
|
+
self._user_settings.viz.iso_resolution = str(new_val)
|
|
3401
|
+
self._user_settings.save()
|
|
3402
|
+
|
|
3323
3403
|
def _on_iso_export_cube(self, btn) -> None:
|
|
3324
3404
|
_exp_on_iso_export_cube(self, btn)
|
|
3325
3405
|
|
|
@@ -5514,7 +5594,7 @@ class QuantUIApp:
|
|
|
5514
5594
|
rows.append(f'<div style="{style}">{esc}</div>')
|
|
5515
5595
|
self._log_output_html.value = (
|
|
5516
5596
|
'<div style="font-family:monospace;font-size:12px;line-height:1.4;'
|
|
5517
|
-
"padding:8px 10px;background:#f8fafc;border:1px solid
|
|
5597
|
+
f"padding:8px 10px;background:#f8fafc;border:1px solid {_theme.BORDER};"
|
|
5518
5598
|
'border-radius:4px;overflow-x:auto;max-height:550px;overflow-y:auto">'
|
|
5519
5599
|
+ "".join(rows)
|
|
5520
5600
|
+ "</div>"
|
|
@@ -5530,7 +5610,7 @@ class QuantUIApp:
|
|
|
5530
5610
|
if key and key in HELP_TOPICS:
|
|
5531
5611
|
entry = HELP_TOPICS[key]
|
|
5532
5612
|
self.help_content_html.value = (
|
|
5533
|
-
f'<div style="border:1px solid
|
|
5613
|
+
f'<div style="border:1px solid {_theme.BORDER};border-radius:6px;'
|
|
5534
5614
|
f'padding:14px 18px;margin:8px 0;background:#f8fafc;max-width:700px">'
|
|
5535
5615
|
f'<h4 style="margin:0 0 10px;color:#1e293b;font-size:15px;font-weight:700">'
|
|
5536
5616
|
f'{entry["title"]}</h4>'
|