finalcif 161__tar.gz → 162__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.
- {finalcif-161 → finalcif-162}/AGENTS.md +7 -5
- {finalcif-161 → finalcif-162}/PKG-INFO +6 -6
- finalcif-162/finalcif/__init__.py +3 -0
- {finalcif-161 → finalcif-162}/finalcif/appwindow.py +112 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/cif_file_io.py +6 -2
- {finalcif-161 → finalcif-162}/finalcif/gui/finalcif_gui_ui.py +1040 -1024
- {finalcif-161 → finalcif-162}/finalcif/gui/finalcif_gui_ui.ui +924 -909
- finalcif-162/finalcif/tools/z_from_packing.py +631 -0
- {finalcif-161 → finalcif-162}/pyproject.toml +5 -5
- {finalcif-161 → finalcif-162}/uv.lock +72 -81
- finalcif-162/work/diagnose_1519506.py +109 -0
- finalcif-162/work/diagnose_z.py +26 -0
- finalcif-162/work/investigate_failures.py +73 -0
- finalcif-162/work/validate_cod.py +202 -0
- finalcif-162/work/validate_zprime.py +110 -0
- finalcif-161/finalcif/__init__.py +0 -3
- {finalcif-161 → finalcif-162}/.gitattributes +0 -0
- {finalcif-161 → finalcif-162}/.githooks/pre-push +0 -0
- {finalcif-161 → finalcif-162}/.gitignore +0 -0
- {finalcif-161 → finalcif-162}/.python-version +0 -0
- {finalcif-161 → finalcif-162}/.ruff.toml +0 -0
- {finalcif-161 → finalcif-162}/.run/Make Documentation MAC.run.xml +0 -0
- {finalcif-161 → finalcif-162}/.run/compile_ui_files.run.xml +0 -0
- {finalcif-161 → finalcif-162}/.run/make documentation Windows.run.xml +0 -0
- {finalcif-161 → finalcif-162}/CODE_OF_CONDUCT.md +0 -0
- {finalcif-161 → finalcif-162}/CONTRIBUTING.md +0 -0
- {finalcif-161 → finalcif-162}/Finalcif_installer_win.spec +0 -0
- {finalcif-161 → finalcif-162}/Finalcif_linux_onefile.spec +0 -0
- {finalcif-161 → finalcif-162}/Finalcif_mac.spec +0 -0
- {finalcif-161 → finalcif-162}/LICENSE +0 -0
- {finalcif-161 → finalcif-162}/README.md +0 -0
- {finalcif-161 → finalcif-162}/finalcif/app_path.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/__init__.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/all_cif_dicts.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/atoms.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/checkcif/__init__.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/checkcif/checkcif.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/cif_dict_foo.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/cif_order.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/cod/__init__.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/cod/deposit.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/cod/deposit_check.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/cod/deposition_list.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/cod/doi.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/cod/upload.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/cod/website_parser.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/core_dict.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/hkl.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/modulation_dict.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/powder_dict.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/reference.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/restraints_dict.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/text.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/twin_dict.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/cif/vrf_entry.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/ciforder/__init__.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/ciforder/order.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/ciforder/order_ui.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/ciforder/order_ui.ui +0 -0
- {finalcif-161 → finalcif-162}/finalcif/datafiles/__init__.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/datafiles/bruker_data.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/datafiles/bruker_frame.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/datafiles/ccdc_mail.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/datafiles/data.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/datafiles/p4p_reader.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/datafiles/sadabs.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/datafiles/saint.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/datafiles/shelx_lst.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/datafiles/utils.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/displaymol/__init__.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/displaymol/vtk_molecule.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/equip_property/__init__.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/equip_property/author_loop_templates.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/equip_property/equipment.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/equip_property/properties.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/equip_property/tools.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/finalcif_start.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/__init__.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/block_combobox.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/cif_table_model.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/cif_table_view.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/combobox.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/custom_classes.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/dialogs.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/edit_button.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/equipmenttable.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/file_editor.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/import_selector.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/import_selector_ui.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/import_selector_ui.ui +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/loop_creator.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/loop_creator_ui.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/loop_creator_ui.ui +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/loops.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/mainstackwidget.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/mixins.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/new_key_dialog.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/new_key_dialog_ui.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/new_key_dialog_ui.ui +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/plaintextedit.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/playground.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/propertytable.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/spell_check_edit.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/syntax_highlighter.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/text_templates_ui.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/text_templates_ui.ui +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/text_value_editor.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/validators.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/vrf_classes.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/gui/vzs_viewer.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/icon/__init__.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/icon/finalcif.png +0 -0
- {finalcif-161 → finalcif-162}/finalcif/icon/finalcif2.ico +0 -0
- {finalcif-161 → finalcif-162}/finalcif/icon/finalcif2.png +0 -0
- {finalcif-161 → finalcif-162}/finalcif/icon/multitable.ico +0 -0
- {finalcif-161 → finalcif-162}/finalcif/icon/multitable.png +0 -0
- {finalcif-161 → finalcif-162}/finalcif/icon/multitable.xcf +0 -0
- {finalcif-161 → finalcif-162}/finalcif/report/__init__.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/report/archive_report.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/report/gui/__init__.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/report/gui/mainwindow.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/report/gui/mainwindow.ui +0 -0
- {finalcif-161 → finalcif-162}/finalcif/report/mtools.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/report/references.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/report/report_text.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/report/symm.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/report/tables.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/report/templated_report.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/__init__.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/bootstrap/bootstrap.min.css +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/machines/D8_VENTURE.cif +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/mathml2omml.xsl +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/report.tex +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/report.tmpl +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/report2.tex +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/report_default.docx +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/template1.docx +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/template_for_multitable.docx +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/template_text.docx +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/template_without_text.docx +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/templates.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/unicode2latex/FakePlasTeX/FakeContext.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/unicode2latex/FakePlasTeX/FakeTokenizer.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/unicode2latex/FakePlasTeX/README.md +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/unicode2latex/FakePlasTeX/TokenizerPassThru.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/unicode2latex/LICENSE +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/unicode2latex/__init__.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/unicode2latex/u2l.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/unicode2latex/unicode-math-table.tex +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/unicode2latex/unicode-math-xetex.sty +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/xsl/convert.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/xsl/xmltohtml.xsl +0 -0
- {finalcif-161 → finalcif-162}/finalcif/template/xsl/xmltolatex.xsl +0 -0
- {finalcif-161 → finalcif-162}/finalcif/tools/__init__.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/tools/chemparse.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/tools/download.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/tools/dsrmath.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/tools/misc.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/tools/options.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/tools/platon.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/tools/pupdate.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/tools/settings.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/tools/shred.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/tools/space_groups.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/tools/spgr_format.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/tools/statusbar.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif/tools/sumformula.py +0 -0
- {finalcif-161 → finalcif-162}/finalcif.exe +0 -0
- {finalcif-161 → finalcif-162}/platon/README.txt +0 -0
- {finalcif-161 → finalcif-162}/platon/compile_platon.bat +0 -0
- {finalcif-161 → finalcif-162}/platon/platon_special.exe +0 -0
- {finalcif-161 → finalcif-162}/platon/platon_special.f +0 -0
- {finalcif-161 → finalcif-162}/pylock.toml +0 -0
- {finalcif-161 → finalcif-162}/run_finalcif +0 -0
- {finalcif-161 → finalcif-162}/run_finalcif.bat +0 -0
- {finalcif-161 → finalcif-162}/screenshots/finalcif_checkcif.png +0 -0
- {finalcif-161 → finalcif-162}/screenshots/finalcif_details.png +0 -0
- {finalcif-161 → finalcif-162}/screenshots/finalcif_loops.png +0 -0
- {finalcif-161 → finalcif-162}/screenshots/finalcif_loops2.png +0 -0
- {finalcif-161 → finalcif-162}/screenshots/finalcif_main.png +0 -0
- {finalcif-161 → finalcif-162}/screenshots/finalcif_report.png +0 -0
- {finalcif-161 → finalcif-162}/screenshots/finalcif_responses.png +0 -0
- {finalcif-161 → finalcif-162}/screenshots/loops_column_buttons.png +0 -0
- {finalcif-161 → finalcif-162}/update.exe +0 -0
|
@@ -16,15 +16,15 @@
|
|
|
16
16
|
- **Entry Point:** The application starts from `finalcif/finalcif_start.py` (registered as `finalcif` in `pyproject.toml`).
|
|
17
17
|
- **Main Window:** `AppWindow` (QMainWindow) in `finalcif/appwindow.py` is the orchestrator wiring all subsystems; UI defined in `finalcif/gui/finalcif_gui_ui.ui`.
|
|
18
18
|
- **Subpackage Map:**
|
|
19
|
-
- `finalcif/cif/` — CIF I/O via `gemmi.cif` wrapped in `CifContainer` (`cif_file_io.py`); dict definitions (core/powder/modulation/twin/restraints); subpackages `checkcif/` (IUCr CheckCIF HTML/PDF parsing) and `cod/` (COD/CCDC deposition: `deposit.py`, `upload.py`, `doi.py`, `website_parser.py`).
|
|
19
|
+
- `finalcif/cif/` — CIF I/O via `gemmi.cif` wrapped in `CifContainer` (`cif_file_io.py`); dict definitions (core/powder/modulation/twin/restraints); subpackages `checkcif/` (IUCr CheckCIF HTML/PDF parsing) and `cod/` (COD/CCDC deposition: `deposit.py`, `deposit_check.py`, `deposition_list.py`, `upload.py`, `doi.py`, `website_parser.py`).
|
|
20
20
|
- `finalcif/ciforder/` — CIF key ordering dialog.
|
|
21
21
|
- `finalcif/datafiles/` — raw instrument data importers: Bruker (`bruker_data.py`, `bruker_frame.py`), SAINT, SADABS, SHELX `.lst` (`shelx_lst.py`), `.p4p`, CCDC deposition mail parser (`ccdc_mail.py`).
|
|
22
22
|
- `finalcif/displaymol/` — 3D molecular display (`vtk_molecule.py`) is not used; 2D rendering is provided by the external `fastmolwidget` package (custom widget in `finalcif_gui_ui.ui`).
|
|
23
23
|
- `finalcif/equip_property/` — Equipment/Properties/AuthorLoops template tables (`equipment.py`, `properties.py`, `author_loop_templates.py`).
|
|
24
24
|
- `finalcif/gui/` — Qt widgets; `.ui` files compiled to sibling `*_ui.py` modules.
|
|
25
|
-
- `finalcif/report/` — DOCX report generation via `docxtpl` (`DocxTemplate`, `RichText`, `InlineImage`) in `templated_report.py`; also archive reports, tables, references, symmetry.
|
|
25
|
+
- `finalcif/report/` — DOCX report generation via `docxtpl` (`DocxTemplate`, `RichText`, `InlineImage`) in `templated_report.py`; also archive reports (`archive_report.py`), tables, references, symmetry. The `report/gui/` subpackage hosts the standalone report-options window (`mainwindow.py` + `mainwindow.ui`).
|
|
26
26
|
- `finalcif/template/` — Report template infrastructure (`ReportTemplates`).
|
|
27
|
-
- `finalcif/tools/` — cross-cutting helpers: `platon.py` (`PlatonRunner`), `shred.py`, `space_groups.py`, `sumformula.py`, `settings.py` (`FinalCifSettings` QSettings wrapper), `download.py`, `pupdate.py` (auto-updater), `statusbar.py`, `chemparse.py`, `dsrmath.py`.
|
|
27
|
+
- `finalcif/tools/` — cross-cutting helpers: `platon.py` (`PlatonRunner`), `shred.py`, `space_groups.py` / `spgr_format.py`, `sumformula.py`, `settings.py` (`FinalCifSettings` QSettings wrapper), `options.py`, `download.py`, `pupdate.py` (auto-updater), `statusbar.py`, `chemparse.py`, `dsrmath.py`, `z_from_packing.py`, `misc.py`.
|
|
28
28
|
- **External Tool Integration:**
|
|
29
29
|
- Offline CheckCIF uses PLATON via `finalcif/tools/platon.py`; executable resolution checks bundled `platon/platon_special.exe`, then `C:\pwt\platon.exe` (Windows), then `platon` on `PATH`.
|
|
30
30
|
- Online CheckCIF (IUCr) via HTTP in `finalcif/cif/checkcif/checkcif.py`.
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
- DOI / CrossRef lookups via `crossrefapi` (see `finalcif/cif/cod/doi.py`).
|
|
33
33
|
- SHELX parsing via the external `shelxfile` package (used in `report/templated_report.py`); in-tree `.lst` parser in `datafiles/shelx_lst.py`.
|
|
34
34
|
- **CIF Parsing:** Standardized on `gemmi` (>=0.7.5); `gemmi.set_leak_warnings(False)` is set at startup.
|
|
35
|
+
- **Key Third-Party Runtime Deps:** `qtpy` + `pyside6` / `pyside6-addons`, `gemmi`, `docxtpl[subdoc]`, `python-docx`, `shelxfile`, `fastmolwidget`, `crossrefapi`, `QtAwesome` (icon fonts), `pyenchant` (spell-check), `html2text`, `lxml`, `numpy<2.4`, `chardet` / `charset-normalizer`, `requests`/`urllib3`, `packaging`, `pefile` (see `pyproject.toml`).
|
|
35
36
|
|
|
36
37
|
## Conventions
|
|
37
38
|
- **UI Files:** Edit `.ui` files in Qt Designer (`scripts/designer.py`), then regenerate via `python scripts/compile_ui_files.py`. Never hand-edit generated `*_ui.py`.
|
|
@@ -48,8 +49,9 @@
|
|
|
48
49
|
- *Network-Gated Tests:* set `NO_NETWORK=1` to skip network-dependent checks (CheckCIF/DOI/deposit/PLATON; see `tests/test_checkcif.py`, `tests/test_doi.py`).
|
|
49
50
|
- *Helpers:* shared base class `AppWindowTestCase` and `processevents()` in `tests/helpers.py`; `get_platon_exe()` resolves `C:\pwt\platon.exe` on Windows or `which('platon')`. Fixtures live in `tests/examples/`, `tests/statics/`, `tests/checkcif_results/`, and top-level `test-data/`.
|
|
50
51
|
- **Linting:**
|
|
51
|
-
- *Command (local):* `ruff check .` — configured via `.ruff.toml` (line-length 120, target py312).
|
|
52
|
-
- *CI Lint:* GitHub Actions runs `flake8` in `.github/workflows/python_tests.yml` (ruff is not invoked in CI).
|
|
52
|
+
- *Command (local):* `ruff check .` — configured via `.ruff.toml` (line-length 120, target py312, rule set `E,F,B,UP,ANN,A,C4,DTZ,NPY,PL,FURB,RUF`).
|
|
53
|
+
- *CI Lint:* GitHub Actions runs `flake8` in `.github/workflows/python_tests.yml` (installed on the fly via `uv add flake8 pytest`; ruff is not invoked in CI).
|
|
54
|
+
- **Type Checking:** The dev dependency group includes `ty` and `pyside6-stubs`; `[tool.pyrefly]` in `pyproject.toml` configures Pyrefly project includes/excludes. There is no CI gate for type checks.
|
|
53
55
|
- **UI Compilation:**
|
|
54
56
|
- *Command:* `python scripts/compile_ui_files.py` (discovers `.ui` files, compiles them, and hot-fixes imports to `qtpy`).
|
|
55
57
|
- **Build/Releases:**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: finalcif
|
|
3
|
-
Version:
|
|
3
|
+
Version: 162
|
|
4
4
|
Summary: CIF file editor
|
|
5
5
|
Project-URL: Homepage, https://dkratzert.de/finalcif.html
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/dkratzert/FinalCif/issues
|
|
@@ -17,15 +17,15 @@ Requires-Dist: chardet
|
|
|
17
17
|
Requires-Dist: charset-normalizer
|
|
18
18
|
Requires-Dist: crossrefapi
|
|
19
19
|
Requires-Dist: docxtpl[subdoc]>=0.20.2
|
|
20
|
-
Requires-Dist: fastmolwidget>=0.9.
|
|
20
|
+
Requires-Dist: fastmolwidget>=0.9.7
|
|
21
21
|
Requires-Dist: gemmi>=0.7.5
|
|
22
22
|
Requires-Dist: hatchling
|
|
23
23
|
Requires-Dist: html2text
|
|
24
24
|
Requires-Dist: lxml==6.1.0
|
|
25
25
|
Requires-Dist: numpy<2.4,>=2.3.5
|
|
26
|
-
Requires-Dist: packaging
|
|
26
|
+
Requires-Dist: packaging==26.2
|
|
27
27
|
Requires-Dist: pefile
|
|
28
|
-
Requires-Dist: pybind11
|
|
28
|
+
Requires-Dist: pybind11==3.0.4
|
|
29
29
|
Requires-Dist: pyenchant
|
|
30
30
|
Requires-Dist: pyside6
|
|
31
31
|
Requires-Dist: pyside6-addons
|
|
@@ -34,8 +34,8 @@ Requires-Dist: qtawesome
|
|
|
34
34
|
Requires-Dist: qtpy
|
|
35
35
|
Requires-Dist: requests
|
|
36
36
|
Requires-Dist: shelxfile
|
|
37
|
-
Requires-Dist: urllib3
|
|
38
|
-
Requires-Dist: wheel
|
|
37
|
+
Requires-Dist: urllib3==2.7.0
|
|
38
|
+
Requires-Dist: wheel==0.47.0
|
|
39
39
|
Description-Content-Type: text/markdown
|
|
40
40
|
|
|
41
41
|

|
|
@@ -75,6 +75,7 @@ from finalcif.tools.platon import PlatonRunner
|
|
|
75
75
|
from finalcif.tools.settings import FinalCifSettings
|
|
76
76
|
from finalcif.tools.shred import ShredCIF
|
|
77
77
|
from finalcif.tools.space_groups import SpaceGroups
|
|
78
|
+
from finalcif.tools.z_from_packing import count_z_and_zprime, ZResult
|
|
78
79
|
from finalcif.tools.spgr_format import spgrps
|
|
79
80
|
from finalcif.tools.statusbar import StatusBar
|
|
80
81
|
from finalcif.tools.sumformula import formula_str_to_dict, sum_formula_to_html
|
|
@@ -88,6 +89,42 @@ with suppress(ImportError):
|
|
|
88
89
|
import qtawesome as qta
|
|
89
90
|
|
|
90
91
|
|
|
92
|
+
class _ZEstimatorThread(QtCore.QThread):
|
|
93
|
+
"""Background thread that computes Z and Z′ without blocking the UI.
|
|
94
|
+
|
|
95
|
+
Emits ``result`` (a :class:`ZResult`) on success, or ``failed`` (with an
|
|
96
|
+
empty :class:`ZResult`) on any exception so the caller can always clear
|
|
97
|
+
the label consistently.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
result = QtCore.Signal(object) # emits a ZResult
|
|
101
|
+
|
|
102
|
+
def __init__(
|
|
103
|
+
self,
|
|
104
|
+
atoms_fract: list,
|
|
105
|
+
symmops: list[str],
|
|
106
|
+
cell: tuple[float, ...],
|
|
107
|
+
formula_sum: str | None = None,
|
|
108
|
+
parent: QtCore.QObject | None = None,
|
|
109
|
+
) -> None:
|
|
110
|
+
super().__init__(parent)
|
|
111
|
+
# Take immutable snapshots so the background thread never touches CIF state.
|
|
112
|
+
self._atoms_fract = list(atoms_fract)
|
|
113
|
+
self._symmops = list(symmops)
|
|
114
|
+
self._cell = tuple(cell)
|
|
115
|
+
self._formula_sum = formula_sum
|
|
116
|
+
|
|
117
|
+
def run(self) -> None:
|
|
118
|
+
try:
|
|
119
|
+
res = count_z_and_zprime(
|
|
120
|
+
self._atoms_fract, self._symmops, self._cell,
|
|
121
|
+
formula_sum=self._formula_sum,
|
|
122
|
+
)
|
|
123
|
+
except Exception:
|
|
124
|
+
res = None
|
|
125
|
+
self.result.emit(res)
|
|
126
|
+
|
|
127
|
+
|
|
91
128
|
class AppWindow(QMainWindow):
|
|
92
129
|
|
|
93
130
|
def __init__(self, file: Path | None = None):
|
|
@@ -96,6 +133,7 @@ class AppWindow(QMainWindow):
|
|
|
96
133
|
self.ckf = None
|
|
97
134
|
self.thread_version = None
|
|
98
135
|
self.worker = None
|
|
136
|
+
self._z_thread: _ZEstimatorThread | None = None
|
|
99
137
|
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_DeleteOnClose)
|
|
100
138
|
# This prevents some things to happen during unit tests:
|
|
101
139
|
# Open of target dir of shred cif,
|
|
@@ -1766,6 +1804,7 @@ class AppWindow(QMainWindow):
|
|
|
1766
1804
|
if self.ui.MainStackedWidget.on_info_page():
|
|
1767
1805
|
self.show_residuals()
|
|
1768
1806
|
self.redraw_molecule()
|
|
1807
|
+
self._update_z_label()
|
|
1769
1808
|
t = QtCore.QTimer(self)
|
|
1770
1809
|
t.singleShot(1000, self.check_cecksums)
|
|
1771
1810
|
|
|
@@ -1987,6 +2026,79 @@ class AppWindow(QMainWindow):
|
|
|
1987
2026
|
self.ui.render_widget.grow_molecule(atom_tuples,
|
|
1988
2027
|
cell=self.cif.cell[:6])
|
|
1989
2028
|
|
|
2029
|
+
def _update_z_label(self) -> None:
|
|
2030
|
+
"""Start a background computation of Z and Z′ and show a placeholder.
|
|
2031
|
+
|
|
2032
|
+
Cancels any in-flight worker before starting a new one so that opening
|
|
2033
|
+
files in rapid succession never delivers a stale result to the label.
|
|
2034
|
+
|
|
2035
|
+
Uses the full symmetry expansion of the asymmetric unit to fill the unit
|
|
2036
|
+
cell, then counts discrete connected molecular graphs. Disorder is handled
|
|
2037
|
+
by retaining only the primary component of each disordered site so that
|
|
2038
|
+
each atomic position is populated exactly once.
|
|
2039
|
+
|
|
2040
|
+
Z′ = Z / Z_sg indicates the number of formula units per asymmetric unit:
|
|
2041
|
+
* Z′ = 1 → one molecule per ASU (most common, high confidence).
|
|
2042
|
+
* Z′ = ½ → molecule on 2-fold special position (medium confidence).
|
|
2043
|
+
* Z′ = ⅓ → molecule on 3-fold axis, trigonal/hexagonal (medium confidence).
|
|
2044
|
+
* Z′ = ¼ → molecule on 4-fold axis, tetragonal (medium confidence).
|
|
2045
|
+
* Z′ = ⅙ → molecule on 6-fold axis, hexagonal (medium confidence).
|
|
2046
|
+
* Z′ not a multiple of 1/n (n∈{1,2,3,4,6}) → estimate likely unreliable (low).
|
|
2047
|
+
"""
|
|
2048
|
+
# Abort any previous worker still running.
|
|
2049
|
+
if self._z_thread is not None and self._z_thread.isRunning():
|
|
2050
|
+
self._z_thread.result.disconnect()
|
|
2051
|
+
self._z_thread.quit()
|
|
2052
|
+
self._z_thread.wait(50) # give it 50 ms to stop cleanly
|
|
2053
|
+
self._z_thread = None
|
|
2054
|
+
|
|
2055
|
+
try:
|
|
2056
|
+
atoms = list(self.cif.atoms_fract)
|
|
2057
|
+
symmops = list(self.cif.symmops)
|
|
2058
|
+
cell = tuple(self.cif.cell[:6])
|
|
2059
|
+
formula_sum = self.cif['_chemical_formula_sum'] or None
|
|
2060
|
+
except Exception:
|
|
2061
|
+
self.ui.zEstimateLabel.setText('?')
|
|
2062
|
+
self.ui.zEstimateLabel2.setText('?')
|
|
2063
|
+
return
|
|
2064
|
+
|
|
2065
|
+
self.ui.zEstimateLabel.setText('Z = ')
|
|
2066
|
+
self.ui.zEstimateLabel2.setText('Z = ')
|
|
2067
|
+
self.ui.zEstimateLabel.setToolTip('')
|
|
2068
|
+
self.ui.zEstimateLabel2.setToolTip('')
|
|
2069
|
+
|
|
2070
|
+
self._z_thread = _ZEstimatorThread(atoms, symmops, cell, formula_sum, parent=self)
|
|
2071
|
+
self._z_thread.result.connect(self._on_z_result)
|
|
2072
|
+
self._z_thread.start()
|
|
2073
|
+
|
|
2074
|
+
def _on_z_result(self, result: ZResult | None) -> None:
|
|
2075
|
+
"""Receive the computed :class:`ZResult` from the background thread and update the label."""
|
|
2076
|
+
if result is None:
|
|
2077
|
+
self.ui.zEstimateLabel.setText('?')
|
|
2078
|
+
self.ui.zEstimateLabel2.setText('?')
|
|
2079
|
+
return
|
|
2080
|
+
confidence_icon = {'high': '✓', 'medium': '~', 'low': '?'}[result.confidence]
|
|
2081
|
+
self.ui.zEstimateLabel.setText(f"Z = {result.z} Z′ = {result.z_prime:.3g} {confidence_icon}")
|
|
2082
|
+
self.ui.zEstimateLabel2.setText(f"Z = {result.z} Z′ = {result.z_prime:.3g} {confidence_icon}")
|
|
2083
|
+
tip_lines = [
|
|
2084
|
+
f"Estimated Z = {result.z} formula units per unit cell",
|
|
2085
|
+
f"Z′ = {result.z_prime:.4f} (formula units per asymmetric unit)",
|
|
2086
|
+
f"Z_sg = {result.z_sg} (general positions in space group)",
|
|
2087
|
+
"",
|
|
2088
|
+
]
|
|
2089
|
+
if result.confidence == 'high':
|
|
2090
|
+
tip_lines.append("Confidence: HIGH — Z′ is a positive integer.")
|
|
2091
|
+
elif result.confidence == 'medium':
|
|
2092
|
+
tip_lines.append("Confidence: MEDIUM — Z′ is a non-integer crystallographic")
|
|
2093
|
+
tip_lines.append("fraction (½, ⅓, ¼, or ⅙), suggesting the molecule sits")
|
|
2094
|
+
tip_lines.append("on a special position; the true Z may be higher.")
|
|
2095
|
+
else:
|
|
2096
|
+
tip_lines.append("Confidence: LOW — Z′ is not a multiple of ½.")
|
|
2097
|
+
tip_lines.append("The bond-graph estimate is likely incorrect")
|
|
2098
|
+
tip_lines.append("(common for polymers, frameworks, or salts).")
|
|
2099
|
+
self.ui.zEstimateLabel.setToolTip('\n'.join(tip_lines))
|
|
2100
|
+
self.ui.zEstimateLabel2.setToolTip('\n'.join(tip_lines))
|
|
2101
|
+
|
|
1990
2102
|
def _calc_grown_atoms(self):
|
|
1991
2103
|
"""Helper to generate the symmetry expanded atoms without drawing them."""
|
|
1992
2104
|
atoms_fract = tuple(self.cif.atoms_fract)
|
|
@@ -70,8 +70,12 @@ class CifContainer:
|
|
|
70
70
|
raise GemmiError(e)
|
|
71
71
|
# Starting with first block, but can use others with subsequent self._onload():
|
|
72
72
|
self.block: gemmi.cif.Block = self.doc[0]
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
try:
|
|
74
|
+
self.shx = Shelxfile(verbose=True)
|
|
75
|
+
self.shx.read_string(self.res_file_data[1:-1])
|
|
76
|
+
except Exception:
|
|
77
|
+
print('Shelx file parsing failed.')
|
|
78
|
+
self.shx = None
|
|
75
79
|
self._on_load()
|
|
76
80
|
self.order = []
|
|
77
81
|
self.essential_keys = []
|