finalcif 158__tar.gz → 159__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.
Files changed (167) hide show
  1. {finalcif-158 → finalcif-159}/PKG-INFO +3 -3
  2. {finalcif-158 → finalcif-159}/README.md +2 -2
  3. finalcif-159/finalcif/__init__.py +2 -0
  4. {finalcif-158 → finalcif-159}/finalcif/appwindow.py +152 -136
  5. {finalcif-158 → finalcif-159}/finalcif/cif/checkcif/checkcif.py +25 -22
  6. {finalcif-158 → finalcif-159}/finalcif/cif/cif_file_io.py +16 -0
  7. finalcif-159/finalcif/cif/vrf_entry.py +92 -0
  8. {finalcif-158 → finalcif-159}/finalcif/equip_property/author_loop_templates.py +4 -4
  9. {finalcif-158 → finalcif-159}/finalcif/gui/cif_table_view.py +16 -1
  10. {finalcif-158 → finalcif-159}/finalcif/gui/finalcif_gui_ui.py +21 -78
  11. {finalcif-158 → finalcif-159}/finalcif/gui/finalcif_gui_ui.ui +19 -136
  12. {finalcif-158 → finalcif-159}/finalcif/gui/import_selector.py +3 -2
  13. {finalcif-158 → finalcif-159}/finalcif/gui/loop_creator.py +3 -0
  14. finalcif-159/finalcif/gui/loops.py +555 -0
  15. {finalcif-158 → finalcif-159}/finalcif/gui/vrf_classes.py +72 -75
  16. {finalcif-158 → finalcif-159}/finalcif/report/templated_report.py +3 -1
  17. finalcif-159/finalcif/template/report_default.docx +0 -0
  18. finalcif-159/screenshots/loops_column_buttons.png +0 -0
  19. finalcif-158/finalcif/__init__.py +0 -2
  20. finalcif-158/finalcif/gui/loops.py +0 -332
  21. finalcif-158/finalcif/template/report_default.docx +0 -0
  22. {finalcif-158 → finalcif-159}/.gitattributes +0 -0
  23. {finalcif-158 → finalcif-159}/.gitignore +0 -0
  24. {finalcif-158 → finalcif-159}/.python-version +0 -0
  25. {finalcif-158 → finalcif-159}/.ruff.toml +0 -0
  26. {finalcif-158 → finalcif-159}/.run/Make Documentation MAC.run.xml +0 -0
  27. {finalcif-158 → finalcif-159}/.run/compile_ui_files.run.xml +0 -0
  28. {finalcif-158 → finalcif-159}/.run/make documentation Windows.run.xml +0 -0
  29. {finalcif-158 → finalcif-159}/AGENTS.md +0 -0
  30. {finalcif-158 → finalcif-159}/CODE_OF_CONDUCT.md +0 -0
  31. {finalcif-158 → finalcif-159}/CONTRIBUTING.md +0 -0
  32. {finalcif-158 → finalcif-159}/Finalcif_installer_win.spec +0 -0
  33. {finalcif-158 → finalcif-159}/Finalcif_linux_onefile.spec +0 -0
  34. {finalcif-158 → finalcif-159}/Finalcif_mac.spec +0 -0
  35. {finalcif-158 → finalcif-159}/LICENSE +0 -0
  36. {finalcif-158 → finalcif-159}/finalcif/app_path.py +0 -0
  37. {finalcif-158 → finalcif-159}/finalcif/cif/__init__.py +0 -0
  38. {finalcif-158 → finalcif-159}/finalcif/cif/all_cif_dicts.py +0 -0
  39. {finalcif-158 → finalcif-159}/finalcif/cif/atoms.py +0 -0
  40. {finalcif-158 → finalcif-159}/finalcif/cif/checkcif/__init__.py +0 -0
  41. {finalcif-158 → finalcif-159}/finalcif/cif/cif_dict_foo.py +0 -0
  42. {finalcif-158 → finalcif-159}/finalcif/cif/cif_order.py +0 -0
  43. {finalcif-158 → finalcif-159}/finalcif/cif/cod/__init__.py +0 -0
  44. {finalcif-158 → finalcif-159}/finalcif/cif/cod/deposit.py +0 -0
  45. {finalcif-158 → finalcif-159}/finalcif/cif/cod/deposit_check.py +0 -0
  46. {finalcif-158 → finalcif-159}/finalcif/cif/cod/deposition_list.py +0 -0
  47. {finalcif-158 → finalcif-159}/finalcif/cif/cod/doi.py +0 -0
  48. {finalcif-158 → finalcif-159}/finalcif/cif/cod/upload.py +0 -0
  49. {finalcif-158 → finalcif-159}/finalcif/cif/cod/website_parser.py +0 -0
  50. {finalcif-158 → finalcif-159}/finalcif/cif/core_dict.py +0 -0
  51. {finalcif-158 → finalcif-159}/finalcif/cif/hkl.py +0 -0
  52. {finalcif-158 → finalcif-159}/finalcif/cif/modulation_dict.py +0 -0
  53. {finalcif-158 → finalcif-159}/finalcif/cif/powder_dict.py +0 -0
  54. {finalcif-158 → finalcif-159}/finalcif/cif/reference.py +0 -0
  55. {finalcif-158 → finalcif-159}/finalcif/cif/restraints_dict.py +0 -0
  56. {finalcif-158 → finalcif-159}/finalcif/cif/text.py +0 -0
  57. {finalcif-158 → finalcif-159}/finalcif/cif/twin_dict.py +0 -0
  58. {finalcif-158 → finalcif-159}/finalcif/ciforder/__init__.py +0 -0
  59. {finalcif-158 → finalcif-159}/finalcif/ciforder/order.py +0 -0
  60. {finalcif-158 → finalcif-159}/finalcif/ciforder/order_ui.py +0 -0
  61. {finalcif-158 → finalcif-159}/finalcif/ciforder/order_ui.ui +0 -0
  62. {finalcif-158 → finalcif-159}/finalcif/datafiles/__init__.py +0 -0
  63. {finalcif-158 → finalcif-159}/finalcif/datafiles/bruker_data.py +0 -0
  64. {finalcif-158 → finalcif-159}/finalcif/datafiles/bruker_frame.py +0 -0
  65. {finalcif-158 → finalcif-159}/finalcif/datafiles/ccdc_mail.py +0 -0
  66. {finalcif-158 → finalcif-159}/finalcif/datafiles/data.py +0 -0
  67. {finalcif-158 → finalcif-159}/finalcif/datafiles/p4p_reader.py +0 -0
  68. {finalcif-158 → finalcif-159}/finalcif/datafiles/sadabs.py +0 -0
  69. {finalcif-158 → finalcif-159}/finalcif/datafiles/saint.py +0 -0
  70. {finalcif-158 → finalcif-159}/finalcif/datafiles/shelx_lst.py +0 -0
  71. {finalcif-158 → finalcif-159}/finalcif/datafiles/utils.py +0 -0
  72. {finalcif-158 → finalcif-159}/finalcif/displaymol/__init__.py +0 -0
  73. {finalcif-158 → finalcif-159}/finalcif/displaymol/vtk_molecule.py +0 -0
  74. {finalcif-158 → finalcif-159}/finalcif/equip_property/__init__.py +0 -0
  75. {finalcif-158 → finalcif-159}/finalcif/equip_property/equipment.py +0 -0
  76. {finalcif-158 → finalcif-159}/finalcif/equip_property/properties.py +0 -0
  77. {finalcif-158 → finalcif-159}/finalcif/equip_property/tools.py +0 -0
  78. {finalcif-158 → finalcif-159}/finalcif/finalcif_start.py +0 -0
  79. {finalcif-158 → finalcif-159}/finalcif/gui/__init__.py +0 -0
  80. {finalcif-158 → finalcif-159}/finalcif/gui/block_combobox.py +0 -0
  81. {finalcif-158 → finalcif-159}/finalcif/gui/cif_table_model.py +0 -0
  82. {finalcif-158 → finalcif-159}/finalcif/gui/combobox.py +0 -0
  83. {finalcif-158 → finalcif-159}/finalcif/gui/custom_classes.py +0 -0
  84. {finalcif-158 → finalcif-159}/finalcif/gui/dialogs.py +0 -0
  85. {finalcif-158 → finalcif-159}/finalcif/gui/edit_button.py +0 -0
  86. {finalcif-158 → finalcif-159}/finalcif/gui/equipmenttable.py +0 -0
  87. {finalcif-158 → finalcif-159}/finalcif/gui/file_editor.py +0 -0
  88. {finalcif-158 → finalcif-159}/finalcif/gui/import_selector_ui.py +0 -0
  89. {finalcif-158 → finalcif-159}/finalcif/gui/import_selector_ui.ui +0 -0
  90. {finalcif-158 → finalcif-159}/finalcif/gui/loop_creator_ui.py +0 -0
  91. {finalcif-158 → finalcif-159}/finalcif/gui/loop_creator_ui.ui +0 -0
  92. {finalcif-158 → finalcif-159}/finalcif/gui/mainstackwidget.py +0 -0
  93. {finalcif-158 → finalcif-159}/finalcif/gui/mixins.py +0 -0
  94. {finalcif-158 → finalcif-159}/finalcif/gui/new_key_dialog.py +0 -0
  95. {finalcif-158 → finalcif-159}/finalcif/gui/new_key_dialog_ui.py +0 -0
  96. {finalcif-158 → finalcif-159}/finalcif/gui/new_key_dialog_ui.ui +0 -0
  97. {finalcif-158 → finalcif-159}/finalcif/gui/plaintextedit.py +0 -0
  98. {finalcif-158 → finalcif-159}/finalcif/gui/playground.py +0 -0
  99. {finalcif-158 → finalcif-159}/finalcif/gui/propertytable.py +0 -0
  100. {finalcif-158 → finalcif-159}/finalcif/gui/spell_check_edit.py +0 -0
  101. {finalcif-158 → finalcif-159}/finalcif/gui/syntax_highlighter.py +0 -0
  102. {finalcif-158 → finalcif-159}/finalcif/gui/text_templates_ui.py +0 -0
  103. {finalcif-158 → finalcif-159}/finalcif/gui/text_templates_ui.ui +0 -0
  104. {finalcif-158 → finalcif-159}/finalcif/gui/text_value_editor.py +0 -0
  105. {finalcif-158 → finalcif-159}/finalcif/gui/validators.py +0 -0
  106. {finalcif-158 → finalcif-159}/finalcif/gui/vzs_viewer.py +0 -0
  107. {finalcif-158 → finalcif-159}/finalcif/icon/__init__.py +0 -0
  108. {finalcif-158 → finalcif-159}/finalcif/icon/finalcif.png +0 -0
  109. {finalcif-158 → finalcif-159}/finalcif/icon/finalcif2.ico +0 -0
  110. {finalcif-158 → finalcif-159}/finalcif/icon/finalcif2.png +0 -0
  111. {finalcif-158 → finalcif-159}/finalcif/icon/multitable.ico +0 -0
  112. {finalcif-158 → finalcif-159}/finalcif/icon/multitable.png +0 -0
  113. {finalcif-158 → finalcif-159}/finalcif/icon/multitable.xcf +0 -0
  114. {finalcif-158 → finalcif-159}/finalcif/report/__init__.py +0 -0
  115. {finalcif-158 → finalcif-159}/finalcif/report/archive_report.py +0 -0
  116. {finalcif-158 → finalcif-159}/finalcif/report/gui/__init__.py +0 -0
  117. {finalcif-158 → finalcif-159}/finalcif/report/gui/mainwindow.py +0 -0
  118. {finalcif-158 → finalcif-159}/finalcif/report/gui/mainwindow.ui +0 -0
  119. {finalcif-158 → finalcif-159}/finalcif/report/mtools.py +0 -0
  120. {finalcif-158 → finalcif-159}/finalcif/report/references.py +0 -0
  121. {finalcif-158 → finalcif-159}/finalcif/report/report_text.py +0 -0
  122. {finalcif-158 → finalcif-159}/finalcif/report/symm.py +0 -0
  123. {finalcif-158 → finalcif-159}/finalcif/report/tables.py +0 -0
  124. {finalcif-158 → finalcif-159}/finalcif/template/__init__.py +0 -0
  125. {finalcif-158 → finalcif-159}/finalcif/template/bootstrap/bootstrap.min.css +0 -0
  126. {finalcif-158 → finalcif-159}/finalcif/template/machines/D8_VENTURE.cif +0 -0
  127. {finalcif-158 → finalcif-159}/finalcif/template/mathml2omml.xsl +0 -0
  128. {finalcif-158 → finalcif-159}/finalcif/template/report.tmpl +0 -0
  129. {finalcif-158 → finalcif-159}/finalcif/template/template1.docx +0 -0
  130. {finalcif-158 → finalcif-159}/finalcif/template/template_for_multitable.docx +0 -0
  131. {finalcif-158 → finalcif-159}/finalcif/template/template_text.docx +0 -0
  132. {finalcif-158 → finalcif-159}/finalcif/template/template_without_text.docx +0 -0
  133. {finalcif-158 → finalcif-159}/finalcif/template/templates.py +0 -0
  134. {finalcif-158 → finalcif-159}/finalcif/template/xsl/convert.py +0 -0
  135. {finalcif-158 → finalcif-159}/finalcif/template/xsl/xmltohtml.xsl +0 -0
  136. {finalcif-158 → finalcif-159}/finalcif/tools/__init__.py +0 -0
  137. {finalcif-158 → finalcif-159}/finalcif/tools/chemparse.py +0 -0
  138. {finalcif-158 → finalcif-159}/finalcif/tools/download.py +0 -0
  139. {finalcif-158 → finalcif-159}/finalcif/tools/dsrmath.py +0 -0
  140. {finalcif-158 → finalcif-159}/finalcif/tools/misc.py +0 -0
  141. {finalcif-158 → finalcif-159}/finalcif/tools/options.py +0 -0
  142. {finalcif-158 → finalcif-159}/finalcif/tools/platon.py +0 -0
  143. {finalcif-158 → finalcif-159}/finalcif/tools/pupdate.py +0 -0
  144. {finalcif-158 → finalcif-159}/finalcif/tools/settings.py +0 -0
  145. {finalcif-158 → finalcif-159}/finalcif/tools/shred.py +0 -0
  146. {finalcif-158 → finalcif-159}/finalcif/tools/space_groups.py +0 -0
  147. {finalcif-158 → finalcif-159}/finalcif/tools/spgr_format.py +0 -0
  148. {finalcif-158 → finalcif-159}/finalcif/tools/statusbar.py +0 -0
  149. {finalcif-158 → finalcif-159}/finalcif/tools/sumformula.py +0 -0
  150. {finalcif-158 → finalcif-159}/finalcif.exe +0 -0
  151. {finalcif-158 → finalcif-159}/platon/README.txt +0 -0
  152. {finalcif-158 → finalcif-159}/platon/compile_platon.bat +0 -0
  153. {finalcif-158 → finalcif-159}/platon/platon_special.exe +0 -0
  154. {finalcif-158 → finalcif-159}/platon/platon_special.f +0 -0
  155. {finalcif-158 → finalcif-159}/pylock.toml +0 -0
  156. {finalcif-158 → finalcif-159}/pyproject.toml +0 -0
  157. {finalcif-158 → finalcif-159}/run_finalcif +0 -0
  158. {finalcif-158 → finalcif-159}/run_finalcif.bat +0 -0
  159. {finalcif-158 → finalcif-159}/screenshots/finalcif_checkcif.png +0 -0
  160. {finalcif-158 → finalcif-159}/screenshots/finalcif_details.png +0 -0
  161. {finalcif-158 → finalcif-159}/screenshots/finalcif_loops.png +0 -0
  162. {finalcif-158 → finalcif-159}/screenshots/finalcif_loops2.png +0 -0
  163. {finalcif-158 → finalcif-159}/screenshots/finalcif_main.png +0 -0
  164. {finalcif-158 → finalcif-159}/screenshots/finalcif_report.png +0 -0
  165. {finalcif-158 → finalcif-159}/screenshots/finalcif_responses.png +0 -0
  166. {finalcif-158 → finalcif-159}/update.exe +0 -0
  167. {finalcif-158 → finalcif-159}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: finalcif
3
- Version: 158
3
+ Version: 159
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
@@ -38,7 +38,7 @@ Requires-Dist: urllib3
38
38
  Requires-Dist: wheel
39
39
  Description-Content-Type: text/markdown
40
40
 
41
- ![Lates Release](https://img.shields.io/github/v/tag/dkratzert/FinalCif?label=Release)
41
+ ![Latest Release](https://img.shields.io/github/v/tag/dkratzert/FinalCif?label=Release)
42
42
  [![Unit Tests](https://github.com/dkratzert/FinalCif/actions/workflows/python_tests.yml/badge.svg?branch=master)](https://github.com/dkratzert/FinalCif/actions/workflows/python_tests.yml)
43
43
  ![Contributions](https://img.shields.io/badge/contributions-welcome-blue)
44
44
  [![PyPI package](https://repology.org/badge/version-for-repo/pypi/python:finalcif.svg)](https://repology.org/project/python:finalcif/versions)
@@ -49,7 +49,7 @@ Description-Content-Type: text/markdown
49
49
 
50
50
  # FinalCif
51
51
 
52
- FinalCif helps you to get a complete [CIF](https://www.iucr.org/resources/cif) file from a single-xrystal X-ray diffraction experiment. See here [https://dkratzert.de/finalcif.html](https://dkratzert.de/finalcif.html) for details.
52
+ FinalCif helps you to get a complete [CIF](https://www.iucr.org/resources/cif) file from a single-crystal X-ray diffraction experiment. See here [https://dkratzert.de/finalcif.html](https://dkratzert.de/finalcif.html) for details.
53
53
 
54
54
  For tables from multiple CIF files, you may want to use the program [multitable](https://github.com/dkratzert/multitable).
55
55
 
@@ -1,4 +1,4 @@
1
- ![Lates Release](https://img.shields.io/github/v/tag/dkratzert/FinalCif?label=Release)
1
+ ![Latest Release](https://img.shields.io/github/v/tag/dkratzert/FinalCif?label=Release)
2
2
  [![Unit Tests](https://github.com/dkratzert/FinalCif/actions/workflows/python_tests.yml/badge.svg?branch=master)](https://github.com/dkratzert/FinalCif/actions/workflows/python_tests.yml)
3
3
  ![Contributions](https://img.shields.io/badge/contributions-welcome-blue)
4
4
  [![PyPI package](https://repology.org/badge/version-for-repo/pypi/python:finalcif.svg)](https://repology.org/project/python:finalcif/versions)
@@ -9,7 +9,7 @@
9
9
 
10
10
  # FinalCif
11
11
 
12
- FinalCif helps you to get a complete [CIF](https://www.iucr.org/resources/cif) file from a single-xrystal X-ray diffraction experiment. See here [https://dkratzert.de/finalcif.html](https://dkratzert.de/finalcif.html) for details.
12
+ FinalCif helps you to get a complete [CIF](https://www.iucr.org/resources/cif) file from a single-crystal X-ray diffraction experiment. See here [https://dkratzert.de/finalcif.html](https://dkratzert.de/finalcif.html) for details.
13
13
 
14
14
  For tables from multiple CIF files, you may want to use the program [multitable](https://github.com/dkratzert/multitable).
15
15
 
@@ -0,0 +1,2 @@
1
+
2
+ VERSION = 159
@@ -16,9 +16,10 @@ from contextlib import suppress
16
16
  from datetime import datetime
17
17
  from math import sin, radians
18
18
  from pathlib import Path, WindowsPath
19
- from typing import cast, Any
19
+ from typing import cast
20
20
 
21
21
  from fastmolwidget.sdm import SDM
22
+
22
23
  from finalcif.gui.vzs_viewer import VZSImageViewer
23
24
 
24
25
  warnings.filterwarnings("ignore", category=RuntimeWarning, module="shibokensupport.signature.parser")
@@ -31,7 +32,7 @@ if hasattr(gemmi, 'set_leak_warnings'):
31
32
  import requests
32
33
  from qtpy import QtCore, QtGui, QtWebEngineWidgets, QtWidgets, compat
33
34
  from qtpy.QtCore import Qt, QEvent
34
- from qtpy.QtWidgets import (QMainWindow, QCheckBox, QListWidgetItem, QApplication,
35
+ from qtpy.QtWidgets import (QMainWindow, QCheckBox, QApplication,
35
36
  QPlainTextEdit, QMessageBox, QScrollBar)
36
37
  from gemmi import cif
37
38
 
@@ -57,15 +58,15 @@ from finalcif.gui.dialogs import show_update_warning, unable_to_open_message, sh
57
58
  from finalcif.gui.finalcif_gui_ui import Ui_FinalCifWindow
58
59
  from finalcif.gui.import_selector import ImportSelector
59
60
  from finalcif.gui.loop_creator import LoopCreator
60
- from finalcif.gui.loops import Loop, LoopTableModel, MyQTableView
61
61
  from finalcif.gui.plaintextedit import MyQPlainTextEdit
62
62
  from finalcif.gui.text_value_editor import MyTextTemplateEdit, TextEditItem
63
- from finalcif.gui.vrf_classes import MyVRFContainer, VREF
63
+ from finalcif.cif.vrf_entry import VRFEntry
64
+ from finalcif.gui.vrf_classes import MyVRFContainer
64
65
  from finalcif.report.templated_report import ReportFormat
65
66
  from finalcif.template.templates import ReportTemplates
66
67
  from finalcif.tools.download import MyDownloader
67
68
  from finalcif.tools.dsrmath import my_isnumeric
68
- from finalcif.tools.misc import (next_path, celltxt, to_float, cif_to_header_label, grouper, is_database_number,
69
+ from finalcif.tools.misc import (next_path, celltxt, to_float, is_database_number,
69
70
  open_file, strip_finalcif_of_name, file_age_in_days)
70
71
  from finalcif.tools.options import Options
71
72
  from finalcif.tools.platon import PlatonRunner
@@ -101,7 +102,6 @@ class AppWindow(QMainWindow):
101
102
  self.sources: dict[str, tuple[str, str | None]] | None = None
102
103
  self.cif: CifContainer | None = None
103
104
  self.report_picture_path: Path | None = None
104
- self.loopcreate: LoopCreator | None = None
105
105
  self.checkdef: list[str] = []
106
106
  self.changes_answer: int = 0
107
107
  self.validation_response_forms_list = []
@@ -113,6 +113,8 @@ class AppWindow(QMainWindow):
113
113
  self.ui = Ui_FinalCifWindow()
114
114
  self.ui.setupUi(self)
115
115
  self.ui.page_MainTable.setParent(self.ui.MainStackedWidget)
116
+ # Inject the Author Editor tab (defined in the .ui file) into LoopsPage
117
+ self.ui.loops_page.set_author_editor_tab(self.ui.author_editor_widget)
116
118
  self.fixfont = QtGui.QFontDatabase.systemFont(QtGui.QFontDatabase.SystemFont.FixedFont)
117
119
  self.ui.shelx_TextEdit.setFont(self.fixfont)
118
120
  self.ui.shelx_warn_TextEdit.setFont(self.fixfont)
@@ -333,7 +335,6 @@ class AppWindow(QMainWindow):
333
335
  except Exception:
334
336
  self.ui.CCDCpushButton.setIcon(qta.icon('fa5s.cloud-upload-alt'))
335
337
  self.ui.CODpushButton.setIcon(qta.icon('mdi.upload'))
336
- self.ui.SavePushButton.setIcon(qta.icon('mdi.content-save'))
337
338
  self.ui.revertLoopsPushButton.setIcon(qta.icon('mdi.backup-restore'))
338
339
  # Backbuttons:
339
340
  self.ui.BackpushButtonDetails.setIcon(qta.icon('mdi.keyboard-backspace'))
@@ -382,18 +383,17 @@ class AppWindow(QMainWindow):
382
383
  self.ui.ExploreDirButton.clicked.connect(self.explore_current_dir)
383
384
  self.ui.LoopsPushButton.clicked.connect(self._on_go_to_loops_page)
384
385
  self.ui.LoopsPushButton.clicked.connect(
385
- lambda x: self.ui.LoopsTabWidget.setCurrentIndex(1) if self.ui.LoopsTabWidget.count() > 0 else None)
386
+ lambda x: self.ui.loops_page.tab_widget.setCurrentIndex(1) if self.ui.loops_page.tab_widget.count() > 0 else None)
386
387
  self.ui.LoopsPushButton.clicked.connect(lambda x: self.ui.TemplatesStackedWidget.setCurrentIndex(1))
387
388
  self.ui.AuthorEditPushButton.clicked.connect(self._on_go_to_loops_page)
388
389
  self.ui.AuthorEditPushButton.clicked.connect(lambda x: self.ui.TemplatesStackedWidget.setCurrentIndex(1))
389
- self.ui.AuthorEditPushButton.clicked.connect(lambda x: self.ui.LoopsTabWidget.setCurrentIndex(0))
390
+ self.ui.AuthorEditPushButton.clicked.connect(lambda x: self.ui.loops_page.tab_widget.setCurrentIndex(0))
390
391
  # checkcif
391
392
  self.ui.CheckcifStartButton.clicked.connect(self.open_checkcif_page)
392
393
  self.ui.CheckcifButton.clicked.connect(self.do_offline_checkcif)
393
394
  self.ui.CheckcifHTMLOnlineButton.clicked.connect(self.do_html_checkcif)
394
395
  self.ui.CheckcifPDFOnlineButton.clicked.connect(self.do_pdf_checkcif)
395
396
  self.ui.BackFromPlatonPushButton.clicked.connect(self.back_to_main_noload)
396
- self.ui.SavePushButton.clicked.connect(self.save_responses)
397
397
  # open, import
398
398
  self.ui.SelectCif_PushButton.clicked.connect(self.load_cif_file)
399
399
  self.ui.SaveCifButton.clicked.connect(self.save_cif_and_display)
@@ -404,8 +404,10 @@ class AppWindow(QMainWindow):
404
404
  self.ui.cif_main_table.row_deleted.connect(self._deleted_row)
405
405
  self.ui.CODpushButton.clicked.connect(self.open_cod_page)
406
406
  self.ui.CCDCpushButton.clicked.connect(self._ccdc_deposit)
407
- self.ui.newLoopPushButton.clicked.connect(self._go_to_new_loop_page)
408
- self.ui.deleteLoopButton.clicked.connect(self._on_delete_current_loop)
407
+ self.ui.newLoopPushButton.clicked.connect(self.ui.loops_page.go_to_new_loop_page)
408
+ self.ui.loops_page.new_loop_requested.connect(self._on_new_loop_requested)
409
+ self.ui.deleteLoopButton.clicked.connect(self.ui.loops_page.on_delete_current_loop)
410
+ self.ui.revertLoopsPushButton.clicked.connect(self.ui.loops_page.revert_all_loops)
409
411
  save_shortcut = QtGui.QShortcut(QtGui.QKeySequence('Ctrl+S'), self)
410
412
  save_shortcut.activated.connect(self.save_current_cif_file)
411
413
  save_shortcut = QtGui.QShortcut(QtGui.QKeySequence('Ctrl+H'), self)
@@ -427,8 +429,6 @@ class AppWindow(QMainWindow):
427
429
  # help
428
430
  self.ui.HelpPushButton.clicked.connect(self.show_help)
429
431
  self.ui.ReportPicPushButton.clicked.connect(self.select_report_picture)
430
- # brings the html checkcif in from in order to avoid confusion of an "empty" checkcif report page:
431
- self.ui.CheckCIFResultsTabWidget.currentChanged.connect(lambda: self.ui.ResponsesTabWidget.setCurrentIndex(0))
432
432
  ##
433
433
  self.ui.SelectCif_LineEdit.returnPressed.connect(self.check_if_file_field_contains_database_number)
434
434
  self.ui.fullIucrCheckBox.clicked.connect(self.toggle_hkl_option)
@@ -473,9 +473,15 @@ class AppWindow(QMainWindow):
473
473
  return self.cif.finalcif_file_prefixed(prefix='', suffix='-finalcif_changes.cif', force_strip=True)
474
474
 
475
475
  def _on_go_to_loops_page(self) -> None:
476
- self.make_loops_tables()
476
+ self.ui.loops_page.load(self.cif)
477
477
  self.ui.MainStackedWidget.go_to_loops_page()
478
478
 
479
+ def _on_new_loop_requested(self) -> None:
480
+ """Hide loop-management buttons when the LoopCreator tab is active."""
481
+ self.ui.revertLoopsPushButton.hide()
482
+ self.ui.newLoopPushButton.hide()
483
+ self.ui.deleteLoopButton.hide()
484
+
479
485
  def export_all_templates(self, filename: Path | None = None) -> None:
480
486
  from finalcif.tools.settings import _custom_encoder
481
487
  if not filename:
@@ -660,7 +666,15 @@ class AppWindow(QMainWindow):
660
666
  text = self.textedit.ui.plainTextEdit.toPlainText()
661
667
  if text:
662
668
  TextEditItem._num = 1
663
- self.ui.cif_main_table.setText(key=self.textedit.cif_key, column=Column.EDIT, txt=text)
669
+ cif_key = self.textedit.cif_key
670
+ # If the template was opened from a VRF widget, write the text into
671
+ # that widget's response field directly (the table cell is spanned).
672
+ if cif_key and cif_key.startswith('_vrf_'):
673
+ vrf_widget = self.ui.cif_main_table.get_vrf_widget(cif_key)
674
+ if vrf_widget is not None:
675
+ vrf_widget.response_text_edit.setPlainText(text)
676
+ else:
677
+ self.ui.cif_main_table.setText(key=cif_key, column=Column.EDIT, txt=text)
664
678
  self.ui.MainStackedWidget.got_to_main_page()
665
679
  self.textedit.clear_fields()
666
680
  else:
@@ -959,7 +973,7 @@ class AppWindow(QMainWindow):
959
973
  self.ui.CheckCifLogPlainTextEdit.appendHtml('<b>CheckCIF failed to finish. '
960
974
  'Please try it at https://checkcif.iucr.org/ instead.</b>')
961
975
  return
962
- self.checkcif_browser = QtWebEngineWidgets.QWebEngineView(self.ui.htmlTabwidgetPage)
976
+ self.checkcif_browser = QtWebEngineWidgets.QWebEngineView(parent=self)
963
977
  self.ui.htmlCHeckCifGridLayout.addWidget(self.checkcif_browser)
964
978
  url = QtCore.QUrl.fromLocalFile(str(self.htmlfile.resolve()))
965
979
  self.ui.MainStackedWidget.go_to_checkcif_page()
@@ -968,30 +982,65 @@ class AppWindow(QMainWindow):
968
982
  self.checkcif_browser.setHtml(self.htmlfile.resolve().read_text('utf-8', 'ignore'))
969
983
  except FileNotFoundError:
970
984
  print(f'{self.htmlfile} not found')
971
- self.ui.ResponsesTabWidget.setCurrentIndex(0)
972
985
  threading.Thread(target=self._display_structure_factor_report, args=(parser,), daemon=True).start()
973
986
  # The picture file linked in the html file:
974
987
  threading.Thread(target=parser.save_image, args=(self.cif.finalcif_file.with_suffix('.gif'),),
975
988
  daemon=True).start()
976
989
  self.ui.CheckCifLogPlainTextEdit.appendPlainText('CheckCIF Report finished.')
977
- forms = parser.response_forms
978
- # makes all gray:
979
- # self.ui.responseFormsListWidget.setStyleSheet("background: 'gray';")
990
+ html_entries = parser.response_forms
991
+ # Merge HTML-parsed entries into the main table:
992
+ # - Entries already shown in the main table (source='cif') get their source badge
993
+ # updated to 'checkcif' to indicate they were returned by this run. The existing
994
+ # response text is preserved.
995
+ # - New entries (not yet in the CIF or table) are added as new rows at the top of
996
+ # the main table, marked 'checkcif'.
980
997
  a = AlertHelp(self.checkdef)
981
- self.validation_response_forms_list = []
982
- self.ui.responseFormsListWidget.clear()
983
- for form in forms:
984
- vrf = MyVRFContainer(form, a.get_help(form['alert_num']), parent=self, is_multi_cif=self.cif.is_multi_cif)
985
- self.validation_response_forms_list.append(vrf)
986
- item = QListWidgetItem()
987
- item.setSizeHint(vrf.sizeHint())
988
- self.ui.responseFormsListWidget.addItem(item)
989
- self.ui.responseFormsListWidget.setItemWidget(item, vrf)
990
- if not forms:
991
- iteme = QListWidgetItem(' ')
992
- item = QListWidgetItem(' No level A, B or C alerts to explain.')
993
- self.ui.responseFormsListWidget.addItem(iteme)
994
- self.ui.responseFormsListWidget.addItem(item)
998
+ table = self.ui.cif_main_table
999
+ # Build a lookup of already-saved CIF VRF entries once, outside the loop.
1000
+ existing_by_key = {e.key: e for e in self.cif.get_vrf_entries()}
1001
+ for entry in html_entries:
1002
+ existing_widget = table.get_vrf_widget(entry.key)
1003
+ if existing_widget is not None:
1004
+ # Row already in table (loaded from CIF): update badge to "From CheckCIF"
1005
+ # but keep the user's existing response text.
1006
+ existing_widget.update_source('checkcif')
1007
+ else:
1008
+ # New entry not yet in the CIF: add a row at the top of the table.
1009
+ # Preserve any existing CIF response if the key happens to be in the CIF
1010
+ # block even though it didn't appear in the table (edge-case safety).
1011
+ if entry.key in existing_by_key:
1012
+ entry.response = existing_by_key[entry.key].response
1013
+ insert_pos = self._vrf_insert_position()
1014
+ table.add_row(key=entry.key, position=insert_pos)
1015
+ if entry.key not in self.cif.order:
1016
+ self.cif.order.insert(insert_pos, entry.key)
1017
+ vrf = MyVRFContainer(entry, a.get_help(entry.alert_num), parent=self,
1018
+ is_multi_cif=self.cif.is_multi_cif)
1019
+ vrf.deleted.connect(self._on_vrf_deleted)
1020
+ vrf.template_requested.connect(self._on_vrf_template_requested)
1021
+ self.validation_response_forms_list.append(vrf)
1022
+ table.place_vrf_widget(insert_pos, vrf)
1023
+ if html_entries:
1024
+ n = len(html_entries)
1025
+ form_noun = 'form' if n == 1 else 'forms'
1026
+ self.ui.CheckCifLogPlainTextEdit.appendHtml(
1027
+ f'<br><b>{n} validation response forms {form_noun} added to the main table.</b>')
1028
+ else:
1029
+ self.ui.CheckCifLogPlainTextEdit.appendHtml(
1030
+ '<br><b>No level A, B or C alerts require a response.</b>')
1031
+
1032
+ def _vrf_insert_position(self) -> int:
1033
+ """Return the row index just after the last existing ``_vrf_*`` row in the main table.
1034
+
1035
+ New VRF rows added during a CheckCIF run are inserted here so that all
1036
+ VRF entries remain grouped at the top of the table.
1037
+ """
1038
+ keys = self.ui.cif_main_table.vheaderitems
1039
+ pos = 0
1040
+ for i, k in enumerate(keys):
1041
+ if k.startswith('_vrf_'):
1042
+ pos = i + 1
1043
+ return pos
995
1044
 
996
1045
  def _display_structure_factor_report(self, parser: MyHTMLParser) -> None:
997
1046
  self.ui.ckf_textedit.setPlainText(parser.get_ckf())
@@ -1041,43 +1090,65 @@ class AppWindow(QMainWindow):
1041
1090
  else:
1042
1091
  self.get_checkdef_for_response_forms()
1043
1092
 
1044
- def save_responses(self) -> None:
1045
- """
1046
- Saves the validation response form text to _vrf_ CIF entries.
1047
- :return: None
1093
+ def _write_vrf_data_to_cif(self) -> int:
1094
+ """Flush inline VRF response text and pending deletions into the CIF document.
1095
+
1096
+ Returns the number of non-empty responses written. This must be called
1097
+ **before** ``self.cif.save()`` so that the CIF file on disk is up-to-date.
1048
1098
  """
1049
- current_block = self.ui.datanameComboBox.currentIndex()
1050
- if not self.validation_response_forms_list:
1051
- return
1052
1099
  n = 0
1053
1100
  for response_row in self.validation_response_forms_list:
1054
1101
  response_txt = response_row.response_text_edit.toPlainText()
1055
1102
  if not response_txt:
1056
- # No response was written
1057
1103
  continue
1058
1104
  n += 1
1059
- v = VREF()
1060
- v.key = response_row.form['name']
1061
- v.problem = response_row.form['problem']
1062
- v.response = response_txt
1063
- v.data_name = response_row.form['data_name']
1064
- for block in self.cif.doc:
1065
- if block.name == v.data_name:
1066
- block.set_pair(v.key, quote(utf8_to_str(v.value)))
1067
- self.save_current_cif_file()
1068
- self.load_cif_file(self.cif.finalcif_file, current_block, load_changes=False)
1069
- if n:
1070
- self.ui.CheckCifLogPlainTextEdit.appendPlainText('Forms saved')
1071
- else:
1072
- self.ui.CheckCifLogPlainTextEdit.appendPlainText('No forms were filled in.')
1073
-
1074
- def _switch_to_report(self) -> None:
1075
- self.ui.ResponsesTabWidget.setCurrentIndex(0)
1105
+ vrf_entry = response_row.vrf_entry
1106
+ vrf_entry.response = response_txt
1107
+ self.cif.block.set_pair(vrf_entry.key, quote(utf8_to_str(vrf_entry.value)))
1108
+ return n
1076
1109
 
1077
1110
  def _pdf_checkcif_finished(self) -> None:
1078
1111
  self.ui.CheckcifPDFOnlineButton.setEnabled(True)
1079
1112
  self.ui.CheckcifHTMLOnlineButton.setEnabled(True)
1080
1113
 
1114
+ def _on_vrf_deleted(self, vrf_widget) -> None:
1115
+ """
1116
+ Handles the ``deleted`` signal emitted by a ``MyVRFContainer`` widget.
1117
+
1118
+ Removes the widget from ``validation_response_forms_list`` and from the
1119
+ main CIF table row. The table's ``row_deleted`` signal then calls
1120
+ ``_deleted_row`` which removes the key from the in-memory CIF block; the
1121
+ deletion is written to disk on the next save.
1122
+ """
1123
+ if vrf_widget in self.validation_response_forms_list:
1124
+ self.validation_response_forms_list.remove(vrf_widget)
1125
+ # Remove the row from the main table (this also emits row_deleted → _deleted_row
1126
+ # which removes the key from the CIF block in memory).
1127
+ table = self.ui.cif_main_table
1128
+ row = table.row_from_key(vrf_widget.vrf_entry.key)
1129
+ table.delete_row(row)
1130
+
1131
+ def _on_vrf_template_requested(self, key: str) -> None:
1132
+ """
1133
+ Opens the text-snippet template editor for the given VRF *key*.
1134
+
1135
+ The editor is keyed on the alert code portion of the key
1136
+ (e.g. ``_vrf_PLAT307`` regardless of the data-block suffix), so all
1137
+ structures sharing the same alert code share their templates.
1138
+ The current response text from the VRF widget is pre-filled in the
1139
+ editor's preview field so the user can combine it with a snippet.
1140
+ """
1141
+ alert_code = self.get_vrf_errortype(key)
1142
+ self.textedit.cif_key = key
1143
+ self.textedit.ui.cifKeyLineEdit.setText(alert_code)
1144
+ self.textedit.add_textfields(self.settings.load_settings_list('text_templates', alert_code))
1145
+ # Pre-fill the editor preview with the current response text from the VRF widget.
1146
+ vrf_widget = self.ui.cif_main_table.get_vrf_widget(key)
1147
+ if vrf_widget is not None:
1148
+ current_response = vrf_widget.response_text_edit.toPlainText()
1149
+ self.textedit.ui.plainTextEdit.setPlainText(current_response)
1150
+ self.ui.MainStackedWidget.go_to_text_template_page()
1151
+
1081
1152
  def do_pdf_checkcif(self) -> None:
1082
1153
  """
1083
1154
  Performs an online checkcif and shows the result as pdf.
@@ -1339,6 +1410,9 @@ class AppWindow(QMainWindow):
1339
1410
  if not self.cif:
1340
1411
  # No file is opened
1341
1412
  return None
1413
+ # Flush VRF response text *before* rename_data_name, because the
1414
+ # rename replaces VRF keys with new names derived from the block name.
1415
+ self._write_vrf_data_to_cif()
1342
1416
  if not self.cif.is_multi_cif:
1343
1417
  self.cif.rename_data_name(''.join(self.ui.datanameComboBox.currentText().split(' ')))
1344
1418
  self.store_data_from_table_rows()
@@ -1475,7 +1549,9 @@ class AppWindow(QMainWindow):
1475
1549
  # increases the pont size every time a bit more :)
1476
1550
  # size = font.pointSize()
1477
1551
  # font.setPointSize(14)
1478
- doc.setDefaultFont(self.fixfont)
1552
+ fixfont = QtGui.QFontDatabase.systemFont(QtGui.QFontDatabase.SystemFont.FixedFont)
1553
+ fixfont.setPointSize(fixfont.pointSize() + 2)
1554
+ doc.setDefaultFont(fixfont)
1479
1555
  final_textedit.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap)
1480
1556
  final_textedit.setPlainText(self.cif.finalcif_file.read_text(encoding='utf-8', errors='ignore'))
1481
1557
  QtCore.QTimer().singleShot(0, lambda: final_textedit.highlighter.setDocument(doc))
@@ -1484,7 +1560,7 @@ class AppWindow(QMainWindow):
1484
1560
  """
1485
1561
  Import an additional cif file to the main table.
1486
1562
  """
1487
- imp_cif: CifContainer | None = None
1563
+ imp_cif: CifContainer
1488
1564
  if not filename:
1489
1565
  filename = cif_file_open_dialog(parent=self, filter="CIF file (*.cif *.pcf *.cif_od *.cfx *.sqf)")
1490
1566
  if not filename:
@@ -1569,8 +1645,8 @@ class AppWindow(QMainWindow):
1569
1645
  self._load_block(block, load_changes=load_changes)
1570
1646
  self.add_data_names_to_combobox()
1571
1647
  self.ui.datanameComboBox.setCurrentIndex(block)
1572
- QtCore.QTimer.singleShot(0, self.ui.cif_main_table.resizeRowsToContents)
1573
1648
  self.ui.datanameComboBox.blockSignals(False)
1649
+ QtCore.QTimer.singleShot(500, self.ui.cif_main_table.resizeRowsToContents)
1574
1650
  if self.cif.is_multi_cif:
1575
1651
  self._flash_block_combobox()
1576
1652
  self.cif.set_order_keys(self.ui.cifOrderWidget.order_keys)
@@ -1613,8 +1689,10 @@ class AppWindow(QMainWindow):
1613
1689
  self.ui.ckf_textedit.clear()
1614
1690
  # Set to empty state before loading:
1615
1691
  self.missing_data = set()
1616
- # clean table and vheader before loading:
1692
+ # clean table (including any inline VRF widgets) before loading:
1617
1693
  self.ui.cif_main_table.delete_content()
1694
+ # reset VRF tracking lists; fill_cif_table repopulates them via inline widgets
1695
+ self.validation_response_forms_list = []
1618
1696
 
1619
1697
  def _load_block(self, index: int, load_changes: bool = True) -> None:
1620
1698
  if not self.cif:
@@ -2098,11 +2176,21 @@ class AppWindow(QMainWindow):
2098
2176
  # Phase 3 – create widgets (with view repaints suspended)
2099
2177
  # ------------------------------------------------------------------
2100
2178
  table.setUpdatesEnabled(False)
2179
+ alert_help = AlertHelp(self.checkdef)
2101
2180
  try:
2102
2181
  for row_num, row_data in enumerate(model_rows):
2103
2182
  if row_data.is_separator:
2104
2183
  table.add_separation_line(row_num)
2105
2184
  self.complete_data_row = row_num
2185
+ elif row_data.key.startswith('_vrf_'):
2186
+ # Show a VRF response-form widget spanning all 3 columns inline.
2187
+ entry = VRFEntry.from_cif_pair(row_data.key, row_data.raw_value)
2188
+ vrf = MyVRFContainer(entry, alert_help.get_help(entry.alert_num),
2189
+ parent=self, is_multi_cif=self.cif.is_multi_cif)
2190
+ vrf.deleted.connect(self._on_vrf_deleted)
2191
+ vrf.template_requested.connect(self._on_vrf_template_requested)
2192
+ self.validation_response_forms_list.append(vrf)
2193
+ table.place_vrf_widget(row_num, vrf)
2106
2194
  else:
2107
2195
  table.setText(row=row_num, key=row_data.key, column=Column.CIF,
2108
2196
  txt=row_data.strval)
@@ -2140,78 +2228,6 @@ class AppWindow(QMainWindow):
2140
2228
  self.ui.cif_main_table.setText(key=key, column=Column.DATA, txt=strval)
2141
2229
  self.ui.cif_main_table.setText(key=key, column=Column.EDIT, txt=strval)
2142
2230
 
2143
- def make_loops_tables(self) -> None:
2144
- for _ in range(self.ui.LoopsTabWidget.count()):
2145
- # I use this, so that always the first tab stays.
2146
- self.ui.LoopsTabWidget.removeTab(1)
2147
- if self.cif and self.cif.loops:
2148
- self.add_loops_tables()
2149
-
2150
- def add_loops_tables(self) -> None:
2151
- """
2152
- Generates a list of tables containing the cif loops.
2153
- """
2154
- # do_not_display = ('_diffrn_refln_index_h')
2155
- for num, loop in enumerate(self.cif.loops):
2156
- tags = loop.tags
2157
- if not tags or len(tags) < 1:
2158
- continue
2159
- # if tags[0] in do_not_display:
2160
- # continue
2161
- self.new_loop_tab(loop, num, tags)
2162
- if self.cif.res_file_data:
2163
- self.add_res_file_to_loops()
2164
-
2165
- def new_loop_tab(self, loop: gemmi.cif.Loop, num: int, tags: list[str]):
2166
- loop = Loop(tags, values=grouper(loop.values, loop.width()),
2167
- parent=self.ui.LoopsTabWidget, block=self.cif.block)
2168
- self.add_loop_widget(loop, first_tag=tags[0])
2169
- self.ui.LoopsTabWidget.setTabToolTip(num + 1, tags[0])
2170
- self.ui.revertLoopsPushButton.clicked.connect(loop.model.revert)
2171
-
2172
- def add_loop_widget(self, loop: Loop, first_tag: str) -> None:
2173
- self.ui.LoopsTabWidget.addTab(loop.tableview, cif_to_header_label.get(first_tag) or first_tag)
2174
-
2175
- def add_res_file_to_loops(self) -> None:
2176
- textedit = QPlainTextEdit()
2177
- self.ui.LoopsTabWidget.addTab(textedit, 'SHELX res file')
2178
- textedit.setPlainText(self.cif.res_file_data[1:-1])
2179
- doc = textedit.document()
2180
- doc.setDefaultFont(self.fixfont)
2181
- textedit.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap)
2182
- textedit.setReadOnly(True)
2183
-
2184
- def _go_to_new_loop_page(self) -> None:
2185
- self.loopcreate = LoopCreator(parent=self, cif=self.cif)
2186
- self.ui.LoopsTabWidget.addTab(self.loopcreate, 'Create Loops')
2187
- self.ui.LoopsTabWidget.setCurrentIndex(self.ui.LoopsTabWidget.count() - 1)
2188
- self.ui.revertLoopsPushButton.hide()
2189
- self.ui.newLoopPushButton.hide()
2190
- self.ui.deleteLoopButton.hide()
2191
- self.loopcreate.saveLoopPushButton.clicked.connect(self.save_new_loop_to_cif)
2192
-
2193
- def _on_delete_current_loop(self) -> None:
2194
- current_tab_index = self.ui.LoopsTabWidget.currentIndex()
2195
- current_table_view: MyQTableView = self.ui.LoopsTabWidget.widget(current_tab_index)
2196
- try:
2197
- header_model: LoopTableModel | QtCore.QAbstractItemModel = current_table_view.horizontalHeader().model()
2198
- except AttributeError:
2199
- # Not a QTableView
2200
- return
2201
- header_item = header_model._header[0]
2202
- loop: gemmi.cif.Loop = self.cif.block.find_loop(header_item).get_loop()
2203
- table: gemmi.cif.Table = self.cif.block.find(loop.tags)
2204
- table.erase()
2205
- self.ui.LoopsTabWidget.removeTab(current_tab_index)
2206
-
2207
- def save_new_loop_to_cif(self):
2208
- if self.loopcreate:
2209
- loop = self.loopcreate.save_new_loop_to_cif()
2210
- self.new_loop_tab(loop=loop, num=self.ui.LoopsTabWidget.count(), tags=self.loopcreate.tags)
2211
- self.ui.LoopsTabWidget.removeTab(self.ui.LoopsTabWidget.count() - 2)
2212
- self.loopcreate.deleteLater()
2213
- self.ui.LoopsTabWidget.setCurrentIndex(self.ui.LoopsTabWidget.count() - 1)
2214
-
2215
2231
  def add_row(self, key: str, value: str, at_start=False, position: int | None = None) -> None:
2216
2232
  """
2217
2233
  Create a empty row at bottom of cif_main_table. This method only fills cif data in the
@@ -19,6 +19,7 @@ from requests import Response
19
19
  from requests.exceptions import MissingSchema
20
20
 
21
21
  from finalcif.cif.cif_file_io import CifContainer
22
+ from finalcif.cif.vrf_entry import VRFEntry
22
23
 
23
24
 
24
25
  class CheckCif(QThread):
@@ -198,37 +199,39 @@ class MyHTMLParser(HTMLParser):
198
199
  return ''
199
200
 
200
201
  @property
201
- def response_forms(self) -> list[dict[str, str]]:
202
+ def response_forms(self) -> list[VRFEntry]:
202
203
  """
203
- :returns
204
- [
205
- {'level' : 'PLAT035_ALERT_1_B',
206
- 'name' : '_vrf_PLAT035_DK_zucker2_0m',
207
- 'problem' : '_chemical_absolute_configuration Info Not Given Please Do '
208
- '! ',
209
- 'alert_num': 'PLAT035'},
210
- {...},
211
- ]
204
+ :returns a list of VRFEntry instances, one per alert with a validation response form.
212
205
  """
213
- forms = []
214
- single_form = {}
206
+ entries = []
207
+ current_key = ''
208
+ current_plat = ''
209
+ current_data = ''
210
+ current_level = ''
215
211
  for line in self.vrf.split('\n'):
216
212
  if line.startswith('_vrf'):
217
- single_form = {'level': ''}
218
- plat = line.split('_')[2]
219
- data = line.split('_', 3)[3]
220
- single_form.update({'name': line, 'alert_num': plat, 'data_name': data})
213
+ parts = line.split('_')
214
+ current_key = line
215
+ current_plat = parts[2] if len(parts) > 2 else ''
216
+ current_data = line.split('_', 3)[3] if len(parts) > 3 else ''
217
+ current_level = ''
221
218
  if line.startswith(';'):
222
219
  continue
223
220
  if line.startswith('PROBLEM'):
224
221
  problem = line[9:]
225
- single_form.update({'problem': problem})
226
222
  for x in self.alert_levels:
227
- if single_form['alert_num'] == x[:7]:
228
- single_form.update({'level': x})
223
+ if current_plat == x[:7]:
224
+ current_level = x
229
225
  break
230
- forms.append(single_form)
231
- return forms
226
+ entries.append(VRFEntry(
227
+ key=current_key,
228
+ data_name=current_data,
229
+ problem=problem,
230
+ response='?',
231
+ alert_num=current_plat,
232
+ level=current_level,
233
+ ))
234
+ return entries
232
235
 
233
236
 
234
237
  class AlertHelp:
@@ -238,7 +241,7 @@ class AlertHelp:
238
241
  def get_help(self, alert: str) -> str:
239
242
  checkdef_help = self._parse_checkdef(alert)
240
243
  if not checkdef_help or 'PLAT' not in alert:
241
- return 'No help available.'
244
+ return ''
242
245
  return checkdef_help
243
246
 
244
247
  def _parse_checkdef(self, alert: str) -> str:
@@ -965,6 +965,22 @@ class CifContainer:
965
965
  if item.pair is not None:
966
966
  yield item.pair
967
967
 
968
+ def get_vrf_entries(self) -> list:
969
+ """
970
+ Returns a list of VRFEntry instances for every ``_vrf_*`` key/value pair
971
+ found in the current block. The VRFEntry objects are constructed by
972
+ parsing the semicolon-delimited text value that stores PROBLEM and
973
+ RESPONSE lines.
974
+ """
975
+ from finalcif.cif.vrf_entry import VRFEntry
976
+ entries = []
977
+ for item in self.block:
978
+ if item.pair is not None:
979
+ key, raw_value = item.pair
980
+ if key.startswith('_vrf_'):
981
+ entries.append(VRFEntry.from_cif_pair(key, raw_value))
982
+ return entries
983
+
968
984
  def _is_centrokey(self, key) -> bool:
969
985
  """
970
986
  Is True if the kurrent key is only valid