finalcif 170__tar.gz → 172__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-170 → finalcif-172}/.gitignore +2 -0
- {finalcif-170 → finalcif-172}/PKG-INFO +2 -2
- finalcif-172/finalcif/__init__.py +3 -0
- {finalcif-170 → finalcif-172}/finalcif/appwindow.py +107 -27
- {finalcif-170 → finalcif-172}/finalcif/cif/checkcif/checkcif.py +188 -4
- {finalcif-170 → finalcif-172}/finalcif/gui/dialogs.py +23 -1
- {finalcif-170 → finalcif-172}/finalcif/gui/import_selector.py +34 -21
- {finalcif-170 → finalcif-172}/finalcif/gui/import_selector_ui.py +4 -11
- {finalcif-170 → finalcif-172}/finalcif/gui/import_selector_ui.ui +2 -9
- {finalcif-170 → finalcif-172}/finalcif/report/templated_report.py +76 -1
- finalcif-172/finalcif/template/miew/LICENSES +23 -0
- finalcif-172/finalcif/template/miew/Miew.css +2 -0
- finalcif-172/finalcif/template/miew/Miew.min.js +12 -0
- finalcif-172/finalcif/template/miew/lodash.min.js +140 -0
- finalcif-172/finalcif/template/miew/three.js +7 -0
- finalcif-172/finalcif/template/report_fastmolwidget.tmpl +621 -0
- finalcif-172/finalcif/tools/mol_file_writer.py +164 -0
- {finalcif-170 → finalcif-172}/pyproject.toml +1 -1
- {finalcif-170 → finalcif-172}/uv.lock +86 -86
- finalcif-170/finalcif/__init__.py +0 -3
- finalcif-170/finalcif/displaymol/vtk_molecule.py +0 -116
- finalcif-170/finalcif/tools/__init__.py +0 -0
- {finalcif-170 → finalcif-172}/.gitattributes +0 -0
- {finalcif-170 → finalcif-172}/.githooks/pre-push +0 -0
- {finalcif-170 → finalcif-172}/.python-version +0 -0
- {finalcif-170 → finalcif-172}/.ruff.toml +0 -0
- {finalcif-170 → finalcif-172}/.run/Make Documentation MAC.run.xml +0 -0
- {finalcif-170 → finalcif-172}/.run/compile_ui_files.run.xml +0 -0
- {finalcif-170 → finalcif-172}/.run/make documentation Windows.run.xml +0 -0
- {finalcif-170 → finalcif-172}/AGENTS.md +0 -0
- {finalcif-170 → finalcif-172}/CODE_OF_CONDUCT.md +0 -0
- {finalcif-170 → finalcif-172}/CONTRIBUTING.md +0 -0
- {finalcif-170 → finalcif-172}/Finalcif_installer_win.spec +0 -0
- {finalcif-170 → finalcif-172}/Finalcif_linux_onefile.spec +0 -0
- {finalcif-170 → finalcif-172}/Finalcif_mac.spec +0 -0
- {finalcif-170 → finalcif-172}/LICENSE +0 -0
- {finalcif-170 → finalcif-172}/README.md +0 -0
- {finalcif-170 → finalcif-172}/finalcif/app_path.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/__init__.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/all_cif_dicts.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/atoms.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/checkcif/__init__.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/cif_dict_foo.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/cif_file_io.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/cif_keyword_mapping.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/cif_order.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/cod/__init__.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/cod/deposit.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/cod/deposit_check.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/cod/deposition_list.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/cod/doi.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/cod/upload.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/cod/website_parser.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/core_dict.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/hkl.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/modulation_dict.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/powder_dict.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/reference.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/restraints_dict.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/text.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/twin_dict.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/cif/vrf_entry.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/ciforder/__init__.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/ciforder/order.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/ciforder/order_ui.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/ciforder/order_ui.ui +0 -0
- {finalcif-170 → finalcif-172}/finalcif/datafiles/__init__.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/datafiles/bruker_data.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/datafiles/bruker_frame.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/datafiles/ccdc_mail.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/datafiles/data.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/datafiles/p4p_reader.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/datafiles/sadabs.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/datafiles/saint.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/datafiles/shelx_lst.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/datafiles/utils.py +0 -0
- {finalcif-170/finalcif/displaymol → finalcif-172/finalcif/equip_property}/__init__.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/equip_property/author_loop_templates.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/equip_property/equipment.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/equip_property/properties.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/equip_property/tools.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/finalcif_start.py +0 -0
- {finalcif-170/finalcif/equip_property → finalcif-172/finalcif/gui}/__init__.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/block_combobox.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/checkcif_browser.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/cif_table_model.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/cif_table_view.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/combobox.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/custom_classes.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/edit_button.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/equipmenttable.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/file_editor.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/finalcif_gui_ui.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/finalcif_gui_ui.ui +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/loop_creator.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/loop_creator_ui.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/loop_creator_ui.ui +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/loops.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/mainstackwidget.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/mixins.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/new_key_dialog.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/new_key_dialog_ui.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/new_key_dialog_ui.ui +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/plaintextedit.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/playground.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/propertytable.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/shelx_navigation.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/single_line_textedit.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/spell_check_edit.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/squeeze_dialog.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/syntax_highlighter.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/text_templates_ui.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/text_templates_ui.ui +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/text_value_editor.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/timers.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/validators.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/vrf_classes.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/gui/vzs_viewer.py +0 -0
- {finalcif-170/finalcif/gui → finalcif-172/finalcif/icon}/__init__.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/icon/finalcif.png +0 -0
- {finalcif-170 → finalcif-172}/finalcif/icon/finalcif2.ico +0 -0
- {finalcif-170 → finalcif-172}/finalcif/icon/finalcif2.png +0 -0
- {finalcif-170 → finalcif-172}/finalcif/icon/multitable.ico +0 -0
- {finalcif-170 → finalcif-172}/finalcif/icon/multitable.png +0 -0
- {finalcif-170 → finalcif-172}/finalcif/icon/multitable.xcf +0 -0
- {finalcif-170 → finalcif-172}/finalcif/report/__init__.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/report/archive_report.py +0 -0
- {finalcif-170/finalcif/icon → finalcif-172/finalcif/report/gui}/__init__.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/report/gui/mainwindow.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/report/gui/mainwindow.ui +0 -0
- {finalcif-170 → finalcif-172}/finalcif/report/mtools.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/report/references.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/report/report_text.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/report/symm.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/report/tables.py +0 -0
- {finalcif-170/finalcif/report/gui → finalcif-172/finalcif/template}/__init__.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/bootstrap/bootstrap.min.css +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/machines/D8_VENTURE.cif +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/mathml2omml.xsl +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/report.tex +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/report.tmpl +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/report2.tex +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/report_default.docx +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/template1.docx +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/template_for_multitable.docx +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/template_text.docx +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/template_without_text.docx +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/templates.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/unicode2latex/FakePlasTeX/FakeContext.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/unicode2latex/FakePlasTeX/FakeTokenizer.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/unicode2latex/FakePlasTeX/README.md +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/unicode2latex/FakePlasTeX/TokenizerPassThru.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/unicode2latex/LICENSE +0 -0
- {finalcif-170/finalcif/template → finalcif-172/finalcif/template/unicode2latex}/__init__.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/unicode2latex/u2l.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/unicode2latex/unicode-math-table.tex +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/unicode2latex/unicode-math-xetex.sty +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/xsl/convert.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/xsl/xmltohtml.xsl +0 -0
- {finalcif-170 → finalcif-172}/finalcif/template/xsl/xmltolatex.xsl +0 -0
- {finalcif-170/finalcif/template/unicode2latex → finalcif-172/finalcif/tools}/__init__.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/tools/chemparse.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/tools/download.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/tools/dsrmath.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/tools/formal_charge.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/tools/misc.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/tools/options.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/tools/platon.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/tools/pupdate.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/tools/selfupdate.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/tools/settings.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/tools/shred.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/tools/space_groups.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/tools/spgr_format.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/tools/squeeze.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/tools/statusbar.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/tools/sumformula.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif/tools/z_from_packing.py +0 -0
- {finalcif-170 → finalcif-172}/finalcif.exe +0 -0
- {finalcif-170 → finalcif-172}/platon/README.txt +0 -0
- {finalcif-170 → finalcif-172}/platon/compile_platon.bat +0 -0
- {finalcif-170 → finalcif-172}/platon/platon_special.exe +0 -0
- {finalcif-170 → finalcif-172}/platon/platon_special.f +0 -0
- {finalcif-170 → finalcif-172}/pylock.toml +0 -0
- {finalcif-170 → finalcif-172}/run_and_capture.py +0 -0
- {finalcif-170 → finalcif-172}/run_finalcif +0 -0
- {finalcif-170 → finalcif-172}/run_finalcif.bat +0 -0
- {finalcif-170 → finalcif-172}/screenshots/finalcif_checkcif.png +0 -0
- {finalcif-170 → finalcif-172}/screenshots/finalcif_details.png +0 -0
- {finalcif-170 → finalcif-172}/screenshots/finalcif_loops.png +0 -0
- {finalcif-170 → finalcif-172}/screenshots/finalcif_loops2.png +0 -0
- {finalcif-170 → finalcif-172}/screenshots/finalcif_main.png +0 -0
- {finalcif-170 → finalcif-172}/screenshots/finalcif_report.png +0 -0
- {finalcif-170 → finalcif-172}/screenshots/finalcif_responses.png +0 -0
- {finalcif-170 → finalcif-172}/screenshots/loops_column_buttons.png +0 -0
- {finalcif-170 → finalcif-172}/work/compare_z_platon.py +0 -0
- {finalcif-170 → finalcif-172}/work/diagnose_1519506.py +0 -0
- {finalcif-170 → finalcif-172}/work/diagnose_z.py +0 -0
- {finalcif-170 → finalcif-172}/work/investigate_failures.py +0 -0
- {finalcif-170 → finalcif-172}/work/validate_cod.py +0 -0
- {finalcif-170 → finalcif-172}/work/validate_zprime.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: finalcif
|
|
3
|
-
Version:
|
|
3
|
+
Version: 172
|
|
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,7 +17,7 @@ Requires-Dist: chardet
|
|
|
17
17
|
Requires-Dist: charset-normalizer
|
|
18
18
|
Requires-Dist: docxcompose==2.2.0
|
|
19
19
|
Requires-Dist: docxtpl[subdoc]>=0.20.2
|
|
20
|
-
Requires-Dist: fastmolwidget>=0.
|
|
20
|
+
Requires-Dist: fastmolwidget>=1.0.0
|
|
21
21
|
Requires-Dist: gemmi>=0.7.5
|
|
22
22
|
Requires-Dist: html2text
|
|
23
23
|
Requires-Dist: lxml>=6.1.1
|
|
@@ -14,18 +14,19 @@ import sys
|
|
|
14
14
|
import threading
|
|
15
15
|
import time
|
|
16
16
|
import warnings
|
|
17
|
+
from collections.abc import Callable
|
|
17
18
|
from contextlib import suppress
|
|
18
19
|
from datetime import datetime
|
|
19
20
|
from math import sin, radians
|
|
20
21
|
from pathlib import Path, WindowsPath
|
|
21
22
|
from typing import cast, TYPE_CHECKING
|
|
22
23
|
|
|
24
|
+
|
|
23
25
|
if TYPE_CHECKING:
|
|
24
26
|
from finalcif.cif.checkcif.checkcif import MyHTMLParser
|
|
25
27
|
|
|
26
28
|
from fastmolwidget import Atomtuple
|
|
27
29
|
from fastmolwidget.part_combo import PartFilterWidget
|
|
28
|
-
from fastmolwidget.sdm import SDM
|
|
29
30
|
|
|
30
31
|
from finalcif.gui.shelx_navigation import find_shelx_line_for_atom, scroll_and_highlight_shelx_atom
|
|
31
32
|
from finalcif.gui.syntax_highlighter import ShelxSyntaxHighlighter
|
|
@@ -60,7 +61,7 @@ from finalcif.gui.custom_classes import Column, light_green, yellow, light_blue,
|
|
|
60
61
|
white, light_red, MyTableWidgetItem, blue
|
|
61
62
|
from finalcif.gui.cif_table_model import CifRowData
|
|
62
63
|
from finalcif.gui.dialogs import show_update_warning, unable_to_open_message, show_general_warning, \
|
|
63
|
-
cif_file_open_dialog, \
|
|
64
|
+
cif_file_open_dialog, fcf_file_open_dialog, \
|
|
64
65
|
bad_z_message, show_res_checksum_warning, show_hkl_checksum_warning, cif_file_save_dialog, show_yes_now_question, \
|
|
65
66
|
video_file_open_dialog
|
|
66
67
|
from finalcif.gui.finalcif_gui_ui import Ui_FinalCifWindow
|
|
@@ -76,6 +77,7 @@ from finalcif.gui.vrf_classes import MyVRFContainer
|
|
|
76
77
|
from finalcif.template.templates import ReportTemplates
|
|
77
78
|
from finalcif.tools.download import MyDownloader
|
|
78
79
|
from finalcif.tools.dsrmath import my_isnumeric
|
|
80
|
+
from finalcif.tools.mol_file_writer import grown_atoms
|
|
79
81
|
from finalcif.tools.misc import (next_path, celltxt, to_float, is_database_number,
|
|
80
82
|
open_file, strip_finalcif_of_name, file_age_in_days, open_in_text_editor,
|
|
81
83
|
running_inside_unit_test as is_unit_test)
|
|
@@ -163,8 +165,10 @@ class AppWindow(QMainWindow):
|
|
|
163
165
|
super().__init__()
|
|
164
166
|
self.thread_download = None
|
|
165
167
|
self.ckf = None
|
|
168
|
+
self.htmlfile: Path | None = None
|
|
169
|
+
self.pdf_htmlfile: Path | None = None
|
|
166
170
|
self.thread_version = None
|
|
167
|
-
self.
|
|
171
|
+
self._downloaders: set[MyDownloader] = set()
|
|
168
172
|
self._z_thread: _ZEstimatorThread | None = None
|
|
169
173
|
self.structure_factor_thread: _StructureFactorReportThread | None = None
|
|
170
174
|
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_DeleteOnClose)
|
|
@@ -989,11 +993,36 @@ class AppWindow(QMainWindow):
|
|
|
989
993
|
print('Skipping version.txt download because NO_NETWORK variable is set.')
|
|
990
994
|
return
|
|
991
995
|
mainurl = "https://dkratzert.de/files/finalcif/version.txt"
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
996
|
+
self.thread_version = self._start_download(mainurl, self.is_update_necessary)
|
|
997
|
+
|
|
998
|
+
def _start_download(self, url: str, on_loaded: Callable[[bytes], None]) -> threading.Thread:
|
|
999
|
+
"""Download `url` in a daemon thread and hand the content to `on_loaded`.
|
|
1000
|
+
|
|
1001
|
+
The worker must not have a parent, otherwise it could not be moved to a thread, so it
|
|
1002
|
+
is kept in `_downloaders` until it is done. Without that reference it would be
|
|
1003
|
+
garbage collected as soon as the next download starts, and its queued signals would
|
|
1004
|
+
never reach the main thread.
|
|
1005
|
+
"""
|
|
1006
|
+
worker = MyDownloader(url, parent=None)
|
|
1007
|
+
self._downloaders.add(worker)
|
|
1008
|
+
worker.loaded.connect(on_loaded)
|
|
1009
|
+
worker.failed.connect(self._download_failed)
|
|
1010
|
+
worker.finished.connect(self._download_finished)
|
|
1011
|
+
thread = threading.Thread(target=worker.download, daemon=True)
|
|
1012
|
+
thread.start()
|
|
1013
|
+
return thread
|
|
1014
|
+
|
|
1015
|
+
def _download_finished(self) -> None:
|
|
1016
|
+
# These slots run in the main thread, so 'loaded' was already delivered here:
|
|
1017
|
+
self._forget_downloader(cast(MyDownloader, self.sender()))
|
|
1018
|
+
|
|
1019
|
+
def _download_failed(self, status_code: int) -> None:
|
|
1020
|
+
worker = cast(MyDownloader, self.sender())
|
|
1021
|
+
print(f'Download of {worker.url} failed with status code {status_code}.')
|
|
1022
|
+
self._forget_downloader(worker)
|
|
1023
|
+
|
|
1024
|
+
def _forget_downloader(self, worker: MyDownloader) -> None:
|
|
1025
|
+
self._downloaders = {each for each in self._downloaders if each is not worker}
|
|
997
1026
|
|
|
998
1027
|
def is_update_necessary(self, content: bytes) -> None:
|
|
999
1028
|
"""
|
|
@@ -1060,10 +1089,7 @@ class AppWindow(QMainWindow):
|
|
|
1060
1089
|
print('Skipping check.def download because NO_NETWORK variable is set.')
|
|
1061
1090
|
return
|
|
1062
1091
|
url = 'http://www.platonsoft.nl/xraysoft/unix/platon/check.def'
|
|
1063
|
-
self.
|
|
1064
|
-
self.worker.loaded.connect(self._save_checkdef)
|
|
1065
|
-
self.thread_download = threading.Thread(target=self.worker.download, daemon=True)
|
|
1066
|
-
self.thread_download.start()
|
|
1092
|
+
self.thread_download = self._start_download(url, self._save_checkdef)
|
|
1067
1093
|
|
|
1068
1094
|
def _save_checkdef(self, reply: bytes) -> None:
|
|
1069
1095
|
"""
|
|
@@ -1150,6 +1176,53 @@ class AppWindow(QMainWindow):
|
|
|
1150
1176
|
def _checkcif_failed(self, txt: str) -> None:
|
|
1151
1177
|
self.ui.CheckCifLogPlainTextEdit.appendHtml(f'<b>{txt}</b>')
|
|
1152
1178
|
|
|
1179
|
+
def _log_checkcif(self, txt: str) -> None:
|
|
1180
|
+
self.ui.CheckCifLogPlainTextEdit.appendPlainText(txt)
|
|
1181
|
+
app.processEvents()
|
|
1182
|
+
|
|
1183
|
+
def _resolve_structure_factor_upload(self, html_file: Path) -> None:
|
|
1184
|
+
"""
|
|
1185
|
+
Answers the CheckCIF page that asks for an explicit upload of the structure factor file.
|
|
1186
|
+
|
|
1187
|
+
The structure factor file belonging to the current CIF is searched next to it. If no
|
|
1188
|
+
matching file is found, the user is asked for one. When the user cancels the dialog, the
|
|
1189
|
+
check is continued without the remaining structure factors.
|
|
1190
|
+
"""
|
|
1191
|
+
from finalcif.cif.checkcif.checkcif import (fix_iucr_urls, needs_structure_factor_upload,
|
|
1192
|
+
submit_without_structure_factors, upload_structure_factors)
|
|
1193
|
+
try:
|
|
1194
|
+
html = html_file.read_text('utf-8', 'ignore')
|
|
1195
|
+
except OSError:
|
|
1196
|
+
return
|
|
1197
|
+
if not needs_structure_factor_upload(html):
|
|
1198
|
+
return
|
|
1199
|
+
self.ui.CheckCifLogPlainTextEdit.appendHtml(
|
|
1200
|
+
'<b>CheckCIF requests the structure factor file for this CIF.</b>')
|
|
1201
|
+
app.processEvents()
|
|
1202
|
+
fcf_file = self._get_structure_factor_file()
|
|
1203
|
+
if fcf_file:
|
|
1204
|
+
new_html = upload_structure_factors(response_html=html, fcf_file=fcf_file,
|
|
1205
|
+
url=self.options.checkcif_url, progress=self._log_checkcif)
|
|
1206
|
+
else:
|
|
1207
|
+
self._log_checkcif('No structure factor file selected. '
|
|
1208
|
+
'Continuing without the remaining structure factors.')
|
|
1209
|
+
new_html = submit_without_structure_factors(response_html=html, url=self.options.checkcif_url,
|
|
1210
|
+
progress=self._log_checkcif)
|
|
1211
|
+
if new_html == html:
|
|
1212
|
+
return
|
|
1213
|
+
with suppress(OSError):
|
|
1214
|
+
html_file.write_text(fix_iucr_urls(new_html), encoding='utf-8')
|
|
1215
|
+
|
|
1216
|
+
def _get_structure_factor_file(self) -> Path | None:
|
|
1217
|
+
from finalcif.cif.checkcif.checkcif import find_matching_fcf_file
|
|
1218
|
+
fcf_file = find_matching_fcf_file(self.cif.finalcif_file)
|
|
1219
|
+
if fcf_file:
|
|
1220
|
+
self._log_checkcif(f'Found structure factor file {fcf_file.name}.')
|
|
1221
|
+
return fcf_file
|
|
1222
|
+
self._log_checkcif('No structure factor file found for the current CIF. Please select one.')
|
|
1223
|
+
filename = fcf_file_open_dialog(parent=self, last_dir=str(self.cif.finalcif_file.parent))
|
|
1224
|
+
return Path(filename) if filename else None
|
|
1225
|
+
|
|
1153
1226
|
def _ckf_progress(self, txt: str) -> None:
|
|
1154
1227
|
self.ui.CheckCifLogPlainTextEdit.appendPlainText(txt)
|
|
1155
1228
|
|
|
@@ -1160,6 +1233,9 @@ class AppWindow(QMainWindow):
|
|
|
1160
1233
|
from finalcif.cif.checkcif.checkcif import MyHTMLParser, AlertHelp
|
|
1161
1234
|
self.ui.CheckcifHTMLOnlineButton.setEnabled(True)
|
|
1162
1235
|
self.ui.CheckcifPDFOnlineButton.setEnabled(True)
|
|
1236
|
+
|
|
1237
|
+
self._resolve_structure_factor_upload(self.htmlfile)
|
|
1238
|
+
|
|
1163
1239
|
try:
|
|
1164
1240
|
parser = MyHTMLParser(self.htmlfile.read_text())
|
|
1165
1241
|
except FileNotFoundError:
|
|
@@ -1167,22 +1243,27 @@ class AppWindow(QMainWindow):
|
|
|
1167
1243
|
self.ui.CheckCifLogPlainTextEdit.appendHtml('<b>CheckCIF failed to finish. '
|
|
1168
1244
|
'Please try it at https://checkcif.iucr.org/ instead.</b>')
|
|
1169
1245
|
return
|
|
1246
|
+
|
|
1170
1247
|
self.checkcif_browser = CheckCifBrowser(parent=self, checkdef=self.checkdef)
|
|
1171
1248
|
self.ui.htmlCHeckCifGridLayout.addWidget(self.checkcif_browser)
|
|
1172
1249
|
self.ui.MainStackedWidget.go_to_checkcif_page()
|
|
1173
1250
|
self.ui.CheckCIFResultsTabWidget.setCurrentIndex(1) # Index 1 is html page
|
|
1251
|
+
|
|
1174
1252
|
# Save the structure image locally and point the HTML to it. The remote image URL
|
|
1175
1253
|
# is session-specific and expires, so the locally saved copy is used for display.
|
|
1176
1254
|
gif_file = self.cif.finalcif_file.with_suffix('.gif')
|
|
1177
1255
|
parser.save_image(gif_file)
|
|
1256
|
+
|
|
1178
1257
|
try:
|
|
1179
1258
|
html = self.htmlfile.resolve().read_text('utf-8', 'ignore')
|
|
1180
1259
|
local_images = {parser.imageurl: gif_file} if parser.imageurl else {}
|
|
1181
1260
|
self.checkcif_browser.set_checkcif_html(html, local_images)
|
|
1182
1261
|
except FileNotFoundError:
|
|
1183
1262
|
print(f'{self.htmlfile} not found')
|
|
1263
|
+
|
|
1184
1264
|
self._display_structure_factor_report(parser)
|
|
1185
1265
|
self.ui.CheckCifLogPlainTextEdit.appendPlainText('CheckCIF Report finished.')
|
|
1266
|
+
|
|
1186
1267
|
html_entries = parser.response_forms
|
|
1187
1268
|
# Merge HTML-parsed entries into the main table:
|
|
1188
1269
|
# - Entries already shown in the main table (source='cif') get their source badge
|
|
@@ -1192,6 +1273,7 @@ class AppWindow(QMainWindow):
|
|
|
1192
1273
|
# the main table, marked 'checkcif'.
|
|
1193
1274
|
a = AlertHelp(self.checkdef)
|
|
1194
1275
|
table = self.ui.cif_main_table
|
|
1276
|
+
|
|
1195
1277
|
# Build a lookup of already-saved CIF VRF entries once, outside the loop.
|
|
1196
1278
|
existing_by_key = {e.key: e for e in self.cif.get_vrf_entries()}
|
|
1197
1279
|
for entry in html_entries:
|
|
@@ -1216,6 +1298,7 @@ class AppWindow(QMainWindow):
|
|
|
1216
1298
|
vrf.template_requested.connect(self._on_vrf_template_requested)
|
|
1217
1299
|
self.validation_response_forms_list.append(vrf)
|
|
1218
1300
|
table.place_vrf_widget(insert_pos, vrf)
|
|
1301
|
+
|
|
1219
1302
|
if html_entries:
|
|
1220
1303
|
n = len(html_entries)
|
|
1221
1304
|
form_noun = 'form' if n == 1 else 'forms'
|
|
@@ -1309,6 +1392,14 @@ class AppWindow(QMainWindow):
|
|
|
1309
1392
|
def _pdf_checkcif_finished(self) -> None:
|
|
1310
1393
|
self.ui.CheckcifPDFOnlineButton.setEnabled(True)
|
|
1311
1394
|
self.ui.CheckcifHTMLOnlineButton.setEnabled(True)
|
|
1395
|
+
if self.pdf_htmlfile is None:
|
|
1396
|
+
return
|
|
1397
|
+
self._resolve_structure_factor_upload(self.pdf_htmlfile)
|
|
1398
|
+
self.ckf.show_pdf_report()
|
|
1399
|
+
self.ui.CheckCifLogPlainTextEdit.appendPlainText('PDF Checkcif report finished.')
|
|
1400
|
+
with suppress(OSError):
|
|
1401
|
+
self.pdf_htmlfile.unlink(missing_ok=True)
|
|
1402
|
+
self.pdf_htmlfile = None
|
|
1312
1403
|
|
|
1313
1404
|
def _on_vrf_deleted(self, vrf_widget) -> None:
|
|
1314
1405
|
"""
|
|
@@ -1360,14 +1451,14 @@ class AppWindow(QMainWindow):
|
|
|
1360
1451
|
self.ui.CheckCifLogPlainTextEdit.appendHtml('<b>Unable to save CIF file. Aborting action...</b>')
|
|
1361
1452
|
return None
|
|
1362
1453
|
self.load_cif_file(self.cif.finalcif_file, current_block, load_changes=False)
|
|
1363
|
-
|
|
1454
|
+
self.pdf_htmlfile = self.cif.finalcif_file_prefixed(prefix='checkpdf-', suffix='.html')
|
|
1364
1455
|
try:
|
|
1365
|
-
|
|
1456
|
+
self.pdf_htmlfile.unlink()
|
|
1366
1457
|
except (FileNotFoundError, PermissionError):
|
|
1367
1458
|
pass
|
|
1368
1459
|
self.ui.CheckCifLogPlainTextEdit.appendPlainText(
|
|
1369
1460
|
f'Sending pdf report request to {self.options.checkcif_url} ...')
|
|
1370
|
-
self.ckf = CheckCif(parent=self, cif=self.cif, outfile=
|
|
1461
|
+
self.ckf = CheckCif(parent=self, cif=self.cif, outfile=self.pdf_htmlfile,
|
|
1371
1462
|
hkl_upload=(not self.ui.structfactCheckBox.isChecked()),
|
|
1372
1463
|
pdf=True, url=self.options.checkcif_url,
|
|
1373
1464
|
full_iucr=self.ui.fullIucrCheckBox.isChecked(),
|
|
@@ -1379,11 +1470,6 @@ class AppWindow(QMainWindow):
|
|
|
1379
1470
|
self.ui.CheckcifPDFOnlineButton.setDisabled(True)
|
|
1380
1471
|
self.ui.CheckcifHTMLOnlineButton.setDisabled(True)
|
|
1381
1472
|
self.ckf.start()
|
|
1382
|
-
self.ui.CheckCifLogPlainTextEdit.appendPlainText('PDF Checkcif report finished.')
|
|
1383
|
-
try:
|
|
1384
|
-
htmlfile.unlink()
|
|
1385
|
-
except (FileNotFoundError, PermissionError):
|
|
1386
|
-
pass
|
|
1387
1473
|
|
|
1388
1474
|
def do_offline_checkcif(self) -> None:
|
|
1389
1475
|
self.ui.CheckcifButton.setDisabled(True)
|
|
@@ -2342,13 +2428,7 @@ class AppWindow(QMainWindow):
|
|
|
2342
2428
|
|
|
2343
2429
|
def _calc_grown_atoms(self):
|
|
2344
2430
|
"""Helper to generate the symmetry expanded atoms without drawing them."""
|
|
2345
|
-
|
|
2346
|
-
if not atoms_fract:
|
|
2347
|
-
return []
|
|
2348
|
-
|
|
2349
|
-
sdm = SDM(atoms_fract, self.cif.symmops, self.cif.cell[:6], centric=self.cif.is_centrosymm)
|
|
2350
|
-
needsymm = sdm.calc_sdm()
|
|
2351
|
-
return sdm.packer(sdm, needsymm)
|
|
2431
|
+
return grown_atoms(self.cif)
|
|
2352
2432
|
|
|
2353
2433
|
def get_adps(self) -> dict:
|
|
2354
2434
|
adp_dict = {}
|
|
@@ -5,13 +5,17 @@
|
|
|
5
5
|
# and you think this stuff is worth it, you can buy me a beer in return.
|
|
6
6
|
# Dr. Daniel Kratzert
|
|
7
7
|
# ----------------------------------------------------------------------------
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
8
10
|
import re
|
|
9
11
|
import subprocess
|
|
10
12
|
import sys
|
|
11
13
|
import time
|
|
14
|
+
from collections.abc import Callable
|
|
12
15
|
from contextlib import suppress
|
|
13
16
|
from html.parser import HTMLParser
|
|
14
17
|
from pathlib import Path
|
|
18
|
+
from urllib.parse import urljoin
|
|
15
19
|
|
|
16
20
|
import requests
|
|
17
21
|
from qtpy.QtCore import QThread, Signal
|
|
@@ -21,6 +25,189 @@ from requests.exceptions import MissingSchema
|
|
|
21
25
|
from finalcif.cif.cif_file_io import CifContainer
|
|
22
26
|
from finalcif.cif.vrf_entry import VRFEntry
|
|
23
27
|
from finalcif.gui.dialogs import show_general_warning
|
|
28
|
+
from finalcif.tools.misc import strip_finalcif_of_name
|
|
29
|
+
|
|
30
|
+
#: Text of the intermediate CheckCIF page that asks for an explicit upload of the
|
|
31
|
+
#: structure factor file.
|
|
32
|
+
STRUCTURE_FACTOR_REQUEST_TEXT = 'File name of structure factor file'
|
|
33
|
+
#: Name of the submit button of the form that continues without structure factors.
|
|
34
|
+
SUBMIT_WITHOUT_HKL_NAME = 'Qsubmitnow'
|
|
35
|
+
#: Maximum difference in seconds between the modification time of a CIF file and a
|
|
36
|
+
#: structure factor file in order to regard them as belonging together.
|
|
37
|
+
MAX_FCF_TIME_DIFFERENCE = 3600.0
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class HtmlForm:
|
|
41
|
+
"""A single HTML form of the CheckCIF intermediate page."""
|
|
42
|
+
|
|
43
|
+
def __init__(self, action: str = '') -> None:
|
|
44
|
+
self.action = action
|
|
45
|
+
self.hidden_data: dict[str, str] = {}
|
|
46
|
+
self.submit_data: dict[str, str] = {}
|
|
47
|
+
self.file_input_name: str = ''
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def payload(self) -> dict[str, str]:
|
|
51
|
+
return {**self.hidden_data, **self.submit_data}
|
|
52
|
+
|
|
53
|
+
def url(self, base_url: str) -> str:
|
|
54
|
+
return urljoin(base_url, self.action) if self.action else base_url
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class CheckCifFormParser(HTMLParser):
|
|
58
|
+
"""
|
|
59
|
+
Extracts the forms of the CheckCIF page that asks for a structure factor upload.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(self, html: str = '') -> None:
|
|
63
|
+
super().__init__()
|
|
64
|
+
self.forms: list[HtmlForm] = []
|
|
65
|
+
self._current_form: HtmlForm | None = None
|
|
66
|
+
if html:
|
|
67
|
+
self.feed(html)
|
|
68
|
+
|
|
69
|
+
def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
|
|
70
|
+
attrs_dict = {key: value or '' for key, value in attrs}
|
|
71
|
+
if tag == 'form':
|
|
72
|
+
self._current_form = HtmlForm(action=attrs_dict.get('action', ''))
|
|
73
|
+
self.forms.append(self._current_form)
|
|
74
|
+
elif tag == 'input' and self._current_form is not None:
|
|
75
|
+
self._add_input(attrs_dict)
|
|
76
|
+
|
|
77
|
+
def _add_input(self, attrs_dict: dict[str, str]) -> None:
|
|
78
|
+
name = attrs_dict.get('name')
|
|
79
|
+
if not name or self._current_form is None:
|
|
80
|
+
return
|
|
81
|
+
input_type = attrs_dict.get('type', '').lower()
|
|
82
|
+
value = attrs_dict.get('value', '')
|
|
83
|
+
if input_type == 'hidden':
|
|
84
|
+
self._current_form.hidden_data[name] = value
|
|
85
|
+
elif input_type == 'submit':
|
|
86
|
+
self._current_form.submit_data[name] = value
|
|
87
|
+
elif input_type == 'file':
|
|
88
|
+
self._current_form.file_input_name = name
|
|
89
|
+
|
|
90
|
+
def handle_endtag(self, tag: str) -> None:
|
|
91
|
+
if tag == 'form':
|
|
92
|
+
self._current_form = None
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def fcf_upload_form(self) -> HtmlForm | None:
|
|
96
|
+
"""The form with a file input field for the structure factor file."""
|
|
97
|
+
for form in self.forms:
|
|
98
|
+
if form.file_input_name:
|
|
99
|
+
return form
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def no_structure_factors_form(self) -> HtmlForm | None:
|
|
104
|
+
"""The form that continues the check without the remaining structure factors."""
|
|
105
|
+
for form in self.forms:
|
|
106
|
+
if SUBMIT_WITHOUT_HKL_NAME in form.submit_data:
|
|
107
|
+
return form
|
|
108
|
+
for form in self.forms:
|
|
109
|
+
if not form.file_input_name:
|
|
110
|
+
return form
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def needs_structure_factor_upload(html: str) -> bool:
|
|
115
|
+
"""Whether the CheckCIF server answered with the structure factor upload form."""
|
|
116
|
+
return STRUCTURE_FACTOR_REQUEST_TEXT in html
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _fcf_candidates(cif_file: Path) -> list[Path]:
|
|
120
|
+
"""Possible structure factor files belonging to *cif_file*, without duplicates."""
|
|
121
|
+
stem_without_finalcif = Path(strip_finalcif_of_name(cif_file.stem, till_name_ends=True)).name
|
|
122
|
+
names = [f'{cif_file.stem}.fcf', f'{stem_without_finalcif}.fcf']
|
|
123
|
+
candidates = []
|
|
124
|
+
for name in names:
|
|
125
|
+
candidate = cif_file.parent / name
|
|
126
|
+
if candidate.exists() and candidate not in candidates:
|
|
127
|
+
candidates.append(candidate)
|
|
128
|
+
return candidates
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _reference_cif_file(cif_file: Path) -> Path:
|
|
132
|
+
"""
|
|
133
|
+
The CIF file the structure factors should be compared against. This is the file
|
|
134
|
+
without the '-finalcif' suffix, because that is the file the current CIF was made from.
|
|
135
|
+
"""
|
|
136
|
+
stem_without_finalcif = Path(strip_finalcif_of_name(cif_file.stem, till_name_ends=True)).name
|
|
137
|
+
source_cif = cif_file.parent / f'{stem_without_finalcif}.cif'
|
|
138
|
+
return source_cif if source_cif.exists() else cif_file
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def find_matching_fcf_file(cif_file: Path,
|
|
142
|
+
max_time_difference: float = MAX_FCF_TIME_DIFFERENCE) -> Path | None:
|
|
143
|
+
"""
|
|
144
|
+
Returns the structure factor file that was created together with *cif_file*.
|
|
145
|
+
|
|
146
|
+
Files named like the CIF with and without the '-finalcif' suffix are considered.
|
|
147
|
+
A file is only accepted if its modification time differs by less than
|
|
148
|
+
*max_time_difference* seconds from the modification time of the CIF file the
|
|
149
|
+
current CIF was made from.
|
|
150
|
+
"""
|
|
151
|
+
reference_file = _reference_cif_file(cif_file)
|
|
152
|
+
with suppress(OSError):
|
|
153
|
+
reference_time = reference_file.stat().st_mtime
|
|
154
|
+
matches = []
|
|
155
|
+
for candidate in _fcf_candidates(cif_file):
|
|
156
|
+
difference = abs(candidate.stat().st_mtime - reference_time)
|
|
157
|
+
if difference < max_time_difference:
|
|
158
|
+
matches.append((difference, candidate))
|
|
159
|
+
if matches:
|
|
160
|
+
return min(matches)[1]
|
|
161
|
+
return None
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _post_form(form: HtmlForm, base_url: str, files: dict | None = None) -> str:
|
|
165
|
+
response = requests.post(form.url(base_url), data=form.payload, files=files, timeout=900)
|
|
166
|
+
response.raise_for_status()
|
|
167
|
+
return response.text
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def upload_structure_factors(response_html: str, fcf_file: Path, url: str,
|
|
171
|
+
progress: Callable[[str], None] = print) -> str:
|
|
172
|
+
"""
|
|
173
|
+
Answers the structure factor upload form of CheckCIF with *fcf_file*.
|
|
174
|
+
|
|
175
|
+
Returns the final report or the unchanged *response_html* if the upload failed.
|
|
176
|
+
"""
|
|
177
|
+
form = CheckCifFormParser(response_html).fcf_upload_form
|
|
178
|
+
if form is None:
|
|
179
|
+
progress('Could not find the structure factor upload form of CheckCIF.')
|
|
180
|
+
return response_html
|
|
181
|
+
if not fcf_file.exists():
|
|
182
|
+
progress(f'Structure factor file {fcf_file.name} not found.')
|
|
183
|
+
return response_html
|
|
184
|
+
try:
|
|
185
|
+
with fcf_file.open('rb') as file_handle:
|
|
186
|
+
files = {form.file_input_name: (fcf_file.name, file_handle, 'application/octet-stream')}
|
|
187
|
+
progress(f'Uploading structure factors from {fcf_file.name} ...')
|
|
188
|
+
html = _post_form(form, url, files=files)
|
|
189
|
+
progress('Structure factor upload finished.')
|
|
190
|
+
return html
|
|
191
|
+
except (OSError, requests.exceptions.RequestException) as e:
|
|
192
|
+
progress(f'Structure factor upload failed: {e}')
|
|
193
|
+
return response_html
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def submit_without_structure_factors(response_html: str, url: str,
|
|
197
|
+
progress: Callable[[str], None] = print) -> str:
|
|
198
|
+
"""
|
|
199
|
+
Continues a CheckCIF run without the remaining structure factors.
|
|
200
|
+
"""
|
|
201
|
+
form = CheckCifFormParser(response_html).no_structure_factors_form
|
|
202
|
+
if form is None:
|
|
203
|
+
progress('Could not find the CheckCIF form to continue without structure factors.')
|
|
204
|
+
return response_html
|
|
205
|
+
try:
|
|
206
|
+
progress('Requesting the report without the remaining structure factors ...')
|
|
207
|
+
return _post_form(form, url)
|
|
208
|
+
except requests.exceptions.RequestException as e:
|
|
209
|
+
progress(f'Request without structure factors failed: {e}')
|
|
210
|
+
return response_html
|
|
24
211
|
|
|
25
212
|
|
|
26
213
|
class CheckCif(QThread):
|
|
@@ -92,10 +279,7 @@ class CheckCif(QThread):
|
|
|
92
279
|
print('html checkcif result could not be written.')
|
|
93
280
|
return
|
|
94
281
|
with suppress(Exception):
|
|
95
|
-
|
|
96
|
-
Path('finalcif_checkcif_tmp.cif').unlink()
|
|
97
|
-
if self.pdf:
|
|
98
|
-
self.finished.connect(self._open_pdf_result)
|
|
282
|
+
Path('finalcif_checkcif_tmp.cif').unlink(missing_ok=True)
|
|
99
283
|
|
|
100
284
|
def _do_the_server_request(self, headers: dict, temp_cif: bytes) -> Response | None:
|
|
101
285
|
req = None
|
|
@@ -19,6 +19,8 @@ from finalcif.tools.selfupdate import ElevationRefused, UpdateCancelled, UpdateE
|
|
|
19
19
|
|
|
20
20
|
# Keeps the running downloads alive, a local variable would be garbage collected:
|
|
21
21
|
_running_updates: set = set()
|
|
22
|
+
# The same for a message box without a parent:
|
|
23
|
+
_open_message_boxes: set = set()
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
class InstallerDownload:
|
|
@@ -334,7 +336,7 @@ def show_update_warning(parent: QWidget | None, remote_version: int = 0) -> None
|
|
|
334
336
|
"https://dkratzert.de/finalcif.html</a>"
|
|
335
337
|
box = QMessageBox(parent)
|
|
336
338
|
box.setTextFormat(QtCore.Qt.TextFormat.AutoText)
|
|
337
|
-
box.setWindowTitle(" ")
|
|
339
|
+
box.setWindowTitle("FinalCif update")
|
|
338
340
|
box.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.TextBrowserInteraction)
|
|
339
341
|
if sys.platform.startswith("win"):
|
|
340
342
|
warn_text += r"<br><br>Updating now will end all running FinalCIF programs!"
|
|
@@ -342,7 +344,12 @@ def show_update_warning(parent: QWidget | None, remote_version: int = 0) -> None
|
|
|
342
344
|
update_button.clicked.connect(lambda: do_update_program(str(remote_version), parent))
|
|
343
345
|
box.setText(warn_text.format(remote_version))
|
|
344
346
|
box.setModal(True)
|
|
347
|
+
_open_message_boxes.add(box)
|
|
348
|
+
box.finished.connect(lambda _: _open_message_boxes.discard(box))
|
|
345
349
|
box.show()
|
|
350
|
+
# The main window is shown right after this box during startup and covers it otherwise:
|
|
351
|
+
box.raise_()
|
|
352
|
+
box.activateWindow()
|
|
346
353
|
|
|
347
354
|
|
|
348
355
|
def bad_z_message(parent: QWidget | None, z: float) -> None:
|
|
@@ -409,6 +416,21 @@ def cif_file_save_dialog(filename: str, parent: QWidget | None = None) -> str:
|
|
|
409
416
|
return filename
|
|
410
417
|
|
|
411
418
|
|
|
419
|
+
def fcf_file_open_dialog(parent: QWidget | None = None, last_dir: str = '', options=None) -> str:
|
|
420
|
+
"""
|
|
421
|
+
Returns the name of a structure factor file from a file dialog.
|
|
422
|
+
"""
|
|
423
|
+
file_filter = "Structure factor file (*.fcf)"
|
|
424
|
+
filename, _ = compat.getopenfilename(parent=parent,
|
|
425
|
+
caption='Select the structure factor (.fcf) file for this CIF',
|
|
426
|
+
basedir=last_dir,
|
|
427
|
+
filters=file_filter,
|
|
428
|
+
selectedfilter=file_filter,
|
|
429
|
+
options=options
|
|
430
|
+
)
|
|
431
|
+
return filename
|
|
432
|
+
|
|
433
|
+
|
|
412
434
|
def video_file_open_dialog(parent: QWidget | None = None, filter: str = "Video file (*.vzs; *.jpg)", last_dir='',
|
|
413
435
|
options=None) -> str:
|
|
414
436
|
filename, _ = compat.getopenfilename(parent=parent,
|