finalcif 152__tar.gz → 154__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 (166) hide show
  1. {finalcif-152 → finalcif-154}/.gitignore +2 -0
  2. finalcif-154/.python-version +1 -0
  3. {finalcif-152 → finalcif-154}/Finalcif_installer_win.spec +1 -0
  4. {finalcif-152 → finalcif-154}/PKG-INFO +6 -5
  5. finalcif-154/finalcif/__init__.py +2 -0
  6. {finalcif-152 → finalcif-154}/finalcif/appwindow.py +50 -22
  7. {finalcif-152 → finalcif-154}/finalcif/cif/atoms.py +3 -3
  8. {finalcif-152 → finalcif-154}/finalcif/cif/checkcif/checkcif.py +1 -1
  9. {finalcif-152 → finalcif-154}/finalcif/cif/cif_file_io.py +53 -36
  10. {finalcif-152 → finalcif-154}/finalcif/cif/cod/deposit.py +5 -5
  11. {finalcif-152 → finalcif-154}/finalcif/ciforder/order.py +1 -1
  12. {finalcif-152 → finalcif-154}/finalcif/ciforder/order_ui.py +3 -3
  13. {finalcif-152 → finalcif-154}/finalcif/datafiles/shelx_lst.py +21 -12
  14. {finalcif-152 → finalcif-154}/finalcif/displaymol/molecule2D.py +3 -3
  15. {finalcif-152 → finalcif-154}/finalcif/displaymol/vtk_molecule.py +2 -2
  16. {finalcif-152 → finalcif-154}/finalcif/equip_property/author_loop_templates.py +20 -26
  17. {finalcif-152 → finalcif-154}/finalcif/equip_property/equipment.py +2 -2
  18. {finalcif-152 → finalcif-154}/finalcif/equip_property/properties.py +2 -2
  19. {finalcif-152 → finalcif-154}/finalcif/finalcif_start.py +5 -3
  20. {finalcif-152 → finalcif-154}/finalcif/gui/block_combobox.py +1 -1
  21. {finalcif-152 → finalcif-154}/finalcif/gui/combobox.py +3 -3
  22. {finalcif-152 → finalcif-154}/finalcif/gui/custom_classes.py +4 -4
  23. {finalcif-152 → finalcif-154}/finalcif/gui/dialogs.py +19 -15
  24. {finalcif-152 → finalcif-154}/finalcif/gui/edit_button.py +1 -1
  25. {finalcif-152 → finalcif-154}/finalcif/gui/equipmenttable.py +2 -2
  26. {finalcif-152 → finalcif-154}/finalcif/gui/file_editor.py +5 -5
  27. {finalcif-152 → finalcif-154}/finalcif/gui/finalcif_gui_ui.py +3 -3
  28. {finalcif-152 → finalcif-154}/finalcif/gui/import_selector_ui.py +3 -3
  29. {finalcif-152 → finalcif-154}/finalcif/gui/loop_creator.py +2 -2
  30. {finalcif-152 → finalcif-154}/finalcif/gui/loop_creator_ui.py +3 -3
  31. {finalcif-152 → finalcif-154}/finalcif/gui/loops.py +4 -4
  32. {finalcif-152 → finalcif-154}/finalcif/gui/mainstackwidget.py +1 -1
  33. {finalcif-152 → finalcif-154}/finalcif/gui/new_key_dialog.py +3 -3
  34. {finalcif-152 → finalcif-154}/finalcif/gui/new_key_dialog_ui.py +3 -3
  35. {finalcif-152 → finalcif-154}/finalcif/gui/plaintextedit.py +6 -6
  36. {finalcif-152 → finalcif-154}/finalcif/gui/playground.py +4 -4
  37. {finalcif-152 → finalcif-154}/finalcif/gui/propertytable.py +1 -1
  38. {finalcif-152 → finalcif-154}/finalcif/gui/spell_check_edit.py +4 -4
  39. {finalcif-152 → finalcif-154}/finalcif/gui/text_templates_ui.py +3 -3
  40. {finalcif-152 → finalcif-154}/finalcif/gui/text_value_editor.py +4 -4
  41. {finalcif-152 → finalcif-154}/finalcif/gui/vrf_classes.py +4 -4
  42. {finalcif-152 → finalcif-154}/finalcif/report/gui/mainwindow.py +3 -3
  43. {finalcif-152 → finalcif-154}/finalcif/report/templated_report.py +63 -40
  44. {finalcif-152 → finalcif-154}/finalcif/template/report.tmpl +7 -1
  45. {finalcif-152 → finalcif-154}/finalcif/template/templates.py +7 -6
  46. {finalcif-152 → finalcif-154}/finalcif/tools/download.py +3 -3
  47. {finalcif-152 → finalcif-154}/finalcif/tools/platon.py +12 -9
  48. {finalcif-152 → finalcif-154}/finalcif/tools/settings.py +4 -3
  49. {finalcif-152 → finalcif-154}/finalcif/tools/sumformula.py +0 -2
  50. finalcif-154/platon/README.txt +29 -0
  51. finalcif-154/platon/compile_platon.bat +4 -0
  52. finalcif-154/platon/platon_special.exe +0 -0
  53. finalcif-154/platon/platon_special.f +167105 -0
  54. {finalcif-152 → finalcif-154}/pyproject.toml +6 -6
  55. {finalcif-152 → finalcif-154}/requirements.txt +2 -2
  56. finalcif-154/uv.lock +1273 -0
  57. finalcif-152/finalcif/__init__.py +0 -2
  58. finalcif-152/uv.lock +0 -1130
  59. {finalcif-152 → finalcif-154}/.gitattributes +0 -0
  60. {finalcif-152 → finalcif-154}/.ruff.toml +0 -0
  61. {finalcif-152 → finalcif-154}/.run/Make Documentation MAC.run.xml +0 -0
  62. {finalcif-152 → finalcif-154}/.run/compile_ui_files.run.xml +0 -0
  63. {finalcif-152 → finalcif-154}/.run/make documentation Windows.run.xml +0 -0
  64. {finalcif-152 → finalcif-154}/CODE_OF_CONDUCT.md +0 -0
  65. {finalcif-152 → finalcif-154}/CONTRIBUTING.md +0 -0
  66. {finalcif-152 → finalcif-154}/Finalcif_linux_onefile.spec +0 -0
  67. {finalcif-152 → finalcif-154}/Finalcif_mac.spec +0 -0
  68. {finalcif-152 → finalcif-154}/Hydrogen_solution_type.cif +0 -0
  69. {finalcif-152 → finalcif-154}/LICENSE +0 -0
  70. {finalcif-152 → finalcif-154}/README.md +0 -0
  71. {finalcif-152 → finalcif-154}/finalcif/app_path.py +0 -0
  72. {finalcif-152 → finalcif-154}/finalcif/cif/__init__.py +0 -0
  73. {finalcif-152 → finalcif-154}/finalcif/cif/all_cif_dicts.py +0 -0
  74. {finalcif-152 → finalcif-154}/finalcif/cif/checkcif/__init__.py +0 -0
  75. {finalcif-152 → finalcif-154}/finalcif/cif/cif_dict_foo.py +0 -0
  76. {finalcif-152 → finalcif-154}/finalcif/cif/cif_order.py +0 -0
  77. {finalcif-152 → finalcif-154}/finalcif/cif/cod/__init__.py +0 -0
  78. {finalcif-152 → finalcif-154}/finalcif/cif/cod/deposit_check.py +0 -0
  79. {finalcif-152 → finalcif-154}/finalcif/cif/cod/deposition_list.py +0 -0
  80. {finalcif-152 → finalcif-154}/finalcif/cif/cod/doi.py +0 -0
  81. {finalcif-152 → finalcif-154}/finalcif/cif/cod/upload.py +0 -0
  82. {finalcif-152 → finalcif-154}/finalcif/cif/cod/website_parser.py +0 -0
  83. {finalcif-152 → finalcif-154}/finalcif/cif/core_dict.py +0 -0
  84. {finalcif-152 → finalcif-154}/finalcif/cif/hkl.py +0 -0
  85. {finalcif-152 → finalcif-154}/finalcif/cif/modulation_dict.py +0 -0
  86. {finalcif-152 → finalcif-154}/finalcif/cif/powder_dict.py +0 -0
  87. {finalcif-152 → finalcif-154}/finalcif/cif/reference.py +0 -0
  88. {finalcif-152 → finalcif-154}/finalcif/cif/restraints_dict.py +0 -0
  89. {finalcif-152 → finalcif-154}/finalcif/cif/text.py +0 -0
  90. {finalcif-152 → finalcif-154}/finalcif/cif/twin_dict.py +0 -0
  91. {finalcif-152 → finalcif-154}/finalcif/ciforder/__init__.py +0 -0
  92. {finalcif-152 → finalcif-154}/finalcif/ciforder/order_ui.ui +0 -0
  93. {finalcif-152 → finalcif-154}/finalcif/datafiles/__init__.py +0 -0
  94. {finalcif-152 → finalcif-154}/finalcif/datafiles/bruker_data.py +0 -0
  95. {finalcif-152 → finalcif-154}/finalcif/datafiles/bruker_frame.py +0 -0
  96. {finalcif-152 → finalcif-154}/finalcif/datafiles/ccdc_mail.py +0 -0
  97. {finalcif-152 → finalcif-154}/finalcif/datafiles/data.py +0 -0
  98. {finalcif-152 → finalcif-154}/finalcif/datafiles/p4p_reader.py +0 -0
  99. {finalcif-152 → finalcif-154}/finalcif/datafiles/sadabs.py +0 -0
  100. {finalcif-152 → finalcif-154}/finalcif/datafiles/saint.py +0 -0
  101. {finalcif-152 → finalcif-154}/finalcif/datafiles/utils.py +0 -0
  102. {finalcif-152 → finalcif-154}/finalcif/displaymol/__init__.py +0 -0
  103. {finalcif-152 → finalcif-154}/finalcif/displaymol/sdm.py +0 -0
  104. {finalcif-152 → finalcif-154}/finalcif/equip_property/__init__.py +0 -0
  105. {finalcif-152 → finalcif-154}/finalcif/equip_property/tools.py +0 -0
  106. {finalcif-152 → finalcif-154}/finalcif/gui/__init__.py +0 -0
  107. {finalcif-152 → finalcif-154}/finalcif/gui/finalcif_gui_ui.ui +0 -0
  108. {finalcif-152 → finalcif-154}/finalcif/gui/import_selector.py +0 -0
  109. {finalcif-152 → finalcif-154}/finalcif/gui/import_selector_ui.ui +0 -0
  110. {finalcif-152 → finalcif-154}/finalcif/gui/loop_creator_ui.ui +0 -0
  111. {finalcif-152 → finalcif-154}/finalcif/gui/mixins.py +0 -0
  112. {finalcif-152 → finalcif-154}/finalcif/gui/new_key_dialog_ui.ui +0 -0
  113. {finalcif-152 → finalcif-154}/finalcif/gui/text_templates_ui.ui +0 -0
  114. {finalcif-152 → finalcif-154}/finalcif/gui/validators.py +0 -0
  115. {finalcif-152 → finalcif-154}/finalcif/icon/__init__.py +0 -0
  116. {finalcif-152 → finalcif-154}/finalcif/icon/finalcif.png +0 -0
  117. {finalcif-152 → finalcif-154}/finalcif/icon/finalcif2.ico +0 -0
  118. {finalcif-152 → finalcif-154}/finalcif/icon/finalcif2.png +0 -0
  119. {finalcif-152 → finalcif-154}/finalcif/icon/multitable.ico +0 -0
  120. {finalcif-152 → finalcif-154}/finalcif/icon/multitable.png +0 -0
  121. {finalcif-152 → finalcif-154}/finalcif/icon/multitable.xcf +0 -0
  122. {finalcif-152 → finalcif-154}/finalcif/report/__init__.py +0 -0
  123. {finalcif-152 → finalcif-154}/finalcif/report/archive_report.py +0 -0
  124. {finalcif-152 → finalcif-154}/finalcif/report/gui/__init__.py +0 -0
  125. {finalcif-152 → finalcif-154}/finalcif/report/gui/mainwindow.ui +0 -0
  126. {finalcif-152 → finalcif-154}/finalcif/report/mtools.py +0 -0
  127. {finalcif-152 → finalcif-154}/finalcif/report/references.py +0 -0
  128. {finalcif-152 → finalcif-154}/finalcif/report/report_text.py +0 -0
  129. {finalcif-152 → finalcif-154}/finalcif/report/symm.py +0 -0
  130. {finalcif-152 → finalcif-154}/finalcif/report/tables.py +0 -0
  131. {finalcif-152 → finalcif-154}/finalcif/template/__init__.py +0 -0
  132. {finalcif-152 → finalcif-154}/finalcif/template/bootstrap/bootstrap.min.css +0 -0
  133. {finalcif-152 → finalcif-154}/finalcif/template/machines/D8_VENTURE.cif +0 -0
  134. {finalcif-152 → finalcif-154}/finalcif/template/mathml2omml.xsl +0 -0
  135. {finalcif-152 → finalcif-154}/finalcif/template/report_default.docx +0 -0
  136. {finalcif-152 → finalcif-154}/finalcif/template/template1.docx +0 -0
  137. {finalcif-152 → finalcif-154}/finalcif/template/template_for_multitable.docx +0 -0
  138. {finalcif-152 → finalcif-154}/finalcif/template/template_text.docx +0 -0
  139. {finalcif-152 → finalcif-154}/finalcif/template/template_without_text.docx +0 -0
  140. {finalcif-152 → finalcif-154}/finalcif/template/xsl/convert.py +0 -0
  141. {finalcif-152 → finalcif-154}/finalcif/template/xsl/xmltohtml.xsl +0 -0
  142. {finalcif-152 → finalcif-154}/finalcif/tools/__init__.py +0 -0
  143. {finalcif-152 → finalcif-154}/finalcif/tools/chemparse.py +0 -0
  144. {finalcif-152 → finalcif-154}/finalcif/tools/dsrmath.py +0 -0
  145. {finalcif-152 → finalcif-154}/finalcif/tools/misc.py +0 -0
  146. {finalcif-152 → finalcif-154}/finalcif/tools/options.py +0 -0
  147. {finalcif-152 → finalcif-154}/finalcif/tools/pupdate.py +0 -0
  148. {finalcif-152 → finalcif-154}/finalcif/tools/shred.py +0 -0
  149. {finalcif-152 → finalcif-154}/finalcif/tools/space_groups.py +0 -0
  150. {finalcif-152 → finalcif-154}/finalcif/tools/spgr_format.py +0 -0
  151. {finalcif-152 → finalcif-154}/finalcif/tools/statusbar.py +0 -0
  152. {finalcif-152 → finalcif-154}/finalcif.exe +0 -0
  153. {finalcif-152 → finalcif-154}/install_requirements +0 -0
  154. {finalcif-152 → finalcif-154}/install_requirements.bat +0 -0
  155. {finalcif-152 → finalcif-154}/requirements_devel.txt +0 -0
  156. {finalcif-152 → finalcif-154}/requirements_doku.txt +0 -0
  157. {finalcif-152 → finalcif-154}/run_finalcif +0 -0
  158. {finalcif-152 → finalcif-154}/run_finalcif.bat +0 -0
  159. {finalcif-152 → finalcif-154}/screenshots/finalcif_checkcif.png +0 -0
  160. {finalcif-152 → finalcif-154}/screenshots/finalcif_details.png +0 -0
  161. {finalcif-152 → finalcif-154}/screenshots/finalcif_loops.png +0 -0
  162. {finalcif-152 → finalcif-154}/screenshots/finalcif_loops2.png +0 -0
  163. {finalcif-152 → finalcif-154}/screenshots/finalcif_main.png +0 -0
  164. {finalcif-152 → finalcif-154}/screenshots/finalcif_report.png +0 -0
  165. {finalcif-152 → finalcif-154}/screenshots/finalcif_responses.png +0 -0
  166. {finalcif-152 → finalcif-154}/update.exe +0 -0
@@ -225,3 +225,5 @@ tests/examples/multi-multitable.docx
225
225
  tests/examples/report_multi-finalcif.docx
226
226
  tests/examples/work/cu_BruecknerJK_153F40_0m.lxt
227
227
  finalcif_keys_order.cif
228
+ !/.python-version
229
+ /platon/*.mod
@@ -0,0 +1 @@
1
+ cpython@3.12
@@ -2,6 +2,7 @@
2
2
 
3
3
  import sys
4
4
  from os import path
5
+ #pyinstaller myapp.py --noconfirm --onedir --hidden-import PySide6.QtWebEngineWidgets --exclude-module PySide6.QtMultimedia
5
6
 
6
7
  block_cipher = None
7
8
  site_packages = next(p for p in sys.path if 'site-packages' in p)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: finalcif
3
- Version: 152
3
+ Version: 154
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
@@ -14,19 +14,20 @@ Classifier: Topic :: Scientific/Engineering :: Chemistry
14
14
  Requires-Python: >=3.10
15
15
  Requires-Dist: certifi
16
16
  Requires-Dist: chardet
17
+ Requires-Dist: charset-normalizer
17
18
  Requires-Dist: crossrefapi
18
19
  Requires-Dist: docxtpl
19
20
  Requires-Dist: gemmi
20
- Requires-Dist: hatchling>=1.27.0
21
+ Requires-Dist: hatchling
21
22
  Requires-Dist: html2text
22
23
  Requires-Dist: lxml
23
24
  Requires-Dist: numpy
25
+ Requires-Dist: packaging
24
26
  Requires-Dist: pefile
25
27
  Requires-Dist: pybind11
26
28
  Requires-Dist: pyenchant
27
- Requires-Dist: pyqt5
28
- Requires-Dist: pyqt5-qt5==5.15.2
29
- Requires-Dist: pyside6<6.9
29
+ Requires-Dist: pyside6
30
+ Requires-Dist: pyside6-addons
30
31
  Requires-Dist: python-docx
31
32
  Requires-Dist: qtawesome
32
33
  Requires-Dist: qtpy
@@ -0,0 +1,2 @@
1
+
2
+ VERSION = 154
@@ -14,12 +14,16 @@ from datetime import datetime
14
14
  from math import sin, radians
15
15
  from pathlib import Path, WindowsPath
16
16
 
17
- import gemmi.cif
17
+ import gemmi
18
+
19
+ if hasattr(gemmi, 'set_leak_warnings'):
20
+ gemmi.set_leak_warnings(False)
21
+
18
22
  import requests
19
- from PySide6 import QtCore, QtGui, QtWebEngineWidgets, QtWidgets
20
- from PySide6.QtCore import Qt, QEvent
21
- from PySide6.QtWidgets import (QMainWindow, QCheckBox, QListWidgetItem, QApplication,
22
- QPlainTextEdit, QFileDialog, QMessageBox, QScrollBar)
23
+ from qtpy import QtCore, QtGui, QtWebEngineWidgets, QtWidgets, compat
24
+ from qtpy.QtCore import Qt, QEvent
25
+ from qtpy.QtWidgets import (QMainWindow, QCheckBox, QListWidgetItem, QApplication,
26
+ QPlainTextEdit, QMessageBox, QScrollBar)
23
27
  from gemmi import cif
24
28
 
25
29
  from finalcif import VERSION
@@ -123,6 +127,25 @@ class AppWindow(QMainWindow):
123
127
  if file:
124
128
  self.load_cif_file(file)
125
129
  self.load_recent_cifs_list()
130
+ self.ui.CheckcifPlaintextEdit.insertPlainText(
131
+ "\n"
132
+ " Offline CheckCIF uses PLATON from Anthony L. Spek, Utrecht University, The Netherlands."
133
+ "\n"
134
+ " https://platonsoft.nl\n"
135
+ " \n"
136
+ " Please cite PLATON in your publication if you use it.\n"
137
+ " \n"
138
+ " - PLATON References : \n"
139
+ " Spek, A.L. (2003). J. Appl. Cryst. 36, 7-13.\n"
140
+ " Spek, A.L. (2009). Acta Cryst. D65, 148-155.\n"
141
+ " Spek, A.L. (2015). Acta Cryst. C71, 9-18.\n"
142
+ " Spek, A.L. (2018). Inorg. Chim. Acta, 470, 232-237.\n"
143
+ " Spek, A.L. (2020). Acta Cryst. E76, 1-11.\n"
144
+ " \n"
145
+ " \n"
146
+ " - Recent versions of PLATON may be obtained from: https://platonsoft.nl/xraysoft\n"
147
+ " "
148
+ )
126
149
  self.set_checkcif_output_font(self.ui.CheckcifPlaintextEdit)
127
150
  # To make file drag&drop working:
128
151
  self.setAcceptDrops(True)
@@ -417,11 +440,12 @@ class AppWindow(QMainWindow):
417
440
  def export_all_templates(self, filename: Path | None = None):
418
441
  import pickle
419
442
  if not filename:
420
- filename, _ = QFileDialog.getSaveFileName(dir=str(Path(self.get_last_workdir()).joinpath(
421
- f'finalcif_templates_{time.strftime("%Y-%m-%d")}.dat')),
422
- selectedFilter="Template File (*.dat)",
423
- filter="Template File (*.dat)",
424
- caption='Save templates')
443
+ filename, _ = compat.getsavefilename(parent=self,
444
+ basedir=str(Path(self.get_last_workdir()).joinpath(
445
+ f'finalcif_templates_{time.strftime("%Y-%m-%d")}.dat')),
446
+ selectedfilter="Template File (*.dat)",
447
+ filters="Template File (*.dat)",
448
+ caption='Save templates')
425
449
  if not filename:
426
450
  return
427
451
  templates = {'text' : self.export_raw_text_templates(),
@@ -440,10 +464,11 @@ class AppWindow(QMainWindow):
440
464
  def import_all_templates(self, filename: Path | None = None):
441
465
  import pickle
442
466
  if not filename:
443
- filename, _ = QFileDialog.getOpenFileName(dir=self.get_last_workdir(),
444
- selectedFilter="Template File (*.dat)",
445
- filter="Template File (*.dat)",
446
- caption='Save templates')
467
+ filename, _ = compat.getopenfilename(parent=self,
468
+ basedir=self.get_last_workdir(),
469
+ selectedfilter="Template File (*.dat)",
470
+ filters="Template File (*.dat)",
471
+ caption='Save templates')
447
472
  if not filename:
448
473
  return
449
474
  try:
@@ -495,7 +520,7 @@ class AppWindow(QMainWindow):
495
520
  """
496
521
  Imports a text template from a CIF file.
497
522
  """
498
- filename = cif_file_open_dialog()
523
+ filename = cif_file_open_dialog(parent=self)
499
524
  if not filename:
500
525
  return
501
526
  try:
@@ -536,7 +561,8 @@ class AppWindow(QMainWindow):
536
561
  if not filename.strip():
537
562
  return
538
563
  try:
539
- doc.write_file(filename, style=cif.Style.Indent35)
564
+ write_options = cif.WriteOptions(cif.Style.Indent35)
565
+ doc.write_file(filename, options=write_options)
540
566
  # Path(filename).write_text(doc.as_string(cif.Style.Indent35))
541
567
  except PermissionError:
542
568
  if Path(filename).is_dir():
@@ -1066,6 +1092,7 @@ class AppWindow(QMainWindow):
1066
1092
  runner.finished.connect(lambda: self.ui.CheckcifButton.setEnabled(True))
1067
1093
  runner.run_process()
1068
1094
  runner.formula.connect(self.add_moiety_furmula)
1095
+ return None
1069
1096
 
1070
1097
  def add_moiety_furmula(self, formula_moiety):
1071
1098
  moiety = self.ui.cif_main_table.getTextFromKey(key='_chemical_formula_moiety', col=Column.CIF)
@@ -1103,9 +1130,10 @@ class AppWindow(QMainWindow):
1103
1130
  if file:
1104
1131
  self.report_picture_path = Path(file)
1105
1132
  else:
1106
- filename, _ = QFileDialog.getOpenFileName(filter="Image Files (*.png *.jpg *.jpeg *.bmp "
1107
- "*.gif *.tif *.tiff *.eps *.emf *.wmf)",
1108
- caption='Open a Report Picture')
1133
+ filename, _ = compat.getopenfilename(parent=self,
1134
+ filters="Image Files (*.png *.jpg *.jpeg *.bmp "
1135
+ "*.gif *.tif *.tiff *.eps *.emf *.wmf)",
1136
+ caption='Open a Report Picture')
1109
1137
  self.set_report_picture_path(filename)
1110
1138
 
1111
1139
  def set_report_picture_path(self, filename: str):
@@ -1409,7 +1437,7 @@ class AppWindow(QMainWindow):
1409
1437
  """
1410
1438
  imp_cif: CifContainer | None = None
1411
1439
  if not filename:
1412
- filename = cif_file_open_dialog(filter="CIF file (*.cif *.pcf *.cif_od *.cfx *.sqf)")
1440
+ filename = cif_file_open_dialog(parent=self, filter="CIF file (*.cif *.pcf *.cif_od *.cfx *.sqf)")
1413
1441
  if not filename:
1414
1442
  return
1415
1443
  try:
@@ -1460,7 +1488,7 @@ class AppWindow(QMainWindow):
1460
1488
  """
1461
1489
  self.ui.datanameComboBox.blockSignals(True)
1462
1490
  if not filepath:
1463
- filepath = Path(cif_file_open_dialog(last_dir=self.get_last_workdir()))
1491
+ filepath = Path(cif_file_open_dialog(parent=self, last_dir=self.get_last_workdir()))
1464
1492
  if not filepath.is_file():
1465
1493
  return
1466
1494
  self.set_path_display_in_file_selector(str(filepath))
@@ -1669,7 +1697,7 @@ class AppWindow(QMainWindow):
1669
1697
  self.load_cif_file(filepath=self.cif.fileobj, load_changes=False)
1670
1698
 
1671
1699
  def get_file_from_dialog(self) -> Path | None:
1672
- fp = cif_file_open_dialog(last_dir=self.get_last_workdir())
1700
+ fp = cif_file_open_dialog(parent=self, last_dir=self.get_last_workdir())
1673
1701
  if not fp:
1674
1702
  return None
1675
1703
  filepath = Path(fp)
@@ -816,7 +816,7 @@ def get_atomlabel(input_atom: str) -> str:
816
816
  else:
817
817
  # print('*** {} is not a valid atom!! ***'.format(atom))
818
818
  raise KeyError
819
- except(IndexError):
819
+ except IndexError:
820
820
  # print('*** {} is not a valid atom! ***'.format(atom))
821
821
  raise KeyError
822
822
 
@@ -826,6 +826,6 @@ if __name__ == '__main__':
826
826
 
827
827
  failed, attempted = doctest.testmod() # verbose=True)
828
828
  if failed == 0:
829
- print('passed all {} tests!'.format(attempted))
829
+ print(f'passed all {attempted} tests!')
830
830
  else:
831
- print('{} of {} tests failed'.format(failed, attempted))
831
+ print(f'{failed} of {attempted} tests failed')
@@ -14,7 +14,7 @@ from html.parser import HTMLParser
14
14
  from pathlib import Path
15
15
 
16
16
  import requests
17
- from PySide6.QtCore import QThread, Signal
17
+ from qtpy.QtCore import QThread, Signal
18
18
  from requests.exceptions import MissingSchema
19
19
 
20
20
  from finalcif.cif.cif_file_io import CifContainer
@@ -5,6 +5,7 @@
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
+ import os
8
9
  import re
9
10
  from collections import namedtuple
10
11
  from collections.abc import Generator
@@ -12,6 +13,12 @@ from contextlib import suppress
12
13
  from pathlib import Path
13
14
 
14
15
  import gemmi
16
+
17
+ from finalcif.cif import atoms
18
+
19
+ if hasattr(gemmi, 'set_leak_warnings'):
20
+ gemmi.set_leak_warnings(False)
21
+
15
22
  from gemmi.cif import as_string, Document, Loop
16
23
  from packaging.version import Version
17
24
  from shelxfile import Shelxfile
@@ -20,8 +27,7 @@ from finalcif.cif.cif_order import order, special_keys, essential_keys, non_cent
20
27
  from finalcif.cif.hkl import HKL, Limit
21
28
  from finalcif.cif.text import utf8_to_str, quote, retranslate_delimiter
22
29
  from finalcif.datafiles.utils import DSRFind
23
- from finalcif.tools.misc import isnumeric, grouper, strip_finalcif_of_name, \
24
- _angstrom_to_x
30
+ from finalcif.tools.misc import isnumeric, grouper, strip_finalcif_of_name, _angstrom_to_x
25
31
 
26
32
 
27
33
  class GemmiError(Exception):
@@ -41,6 +47,7 @@ class CifContainer:
41
47
  new_block: Create a new block (new file) if a name is given. Otherwise, just
42
48
  the existing document is opened.
43
49
  """
50
+ self.chars_ok = True
44
51
  self.picometer: bool = False
45
52
  self._abs_hkl_details: dict[str, str] = {}
46
53
  if isinstance(file, str):
@@ -68,6 +75,7 @@ class CifContainer:
68
75
  self._on_load()
69
76
  self.order = []
70
77
  self.essential_keys = []
78
+ # May be overwritten later:
71
79
  self.set_order_keys(order)
72
80
  self.set_essential_keys(essential_keys)
73
81
 
@@ -119,7 +127,7 @@ class CifContainer:
119
127
  for num, line in enumerate(filepath.read_bytes().splitlines(keepends=True)):
120
128
  try:
121
129
  line.decode('ascii')
122
- except(UnicodeDecodeError):
130
+ except UnicodeDecodeError:
123
131
  line_numbers.append(num)
124
132
  return line_numbers
125
133
 
@@ -151,9 +159,8 @@ class CifContainer:
151
159
  self.dsr_used = DSRFind(self.res_file_data).dsr_used
152
160
  self.atomic_struct: gemmi.SmallStructure = gemmi.make_small_structure_from_block(self.block)
153
161
  # A dictionary to convert Atom names like 'C1_2' or 'Ga3' into Element names like 'C' or 'Ga'
154
- self._name2elements = dict(
155
- zip([x.upper() for x in self.block.find_loop('_atom_site_label')],
156
- [x.upper() for x in self.block.find_loop('_atom_site_type_symbol')], strict=True))
162
+ self._name2elements = dict(zip([x.upper() for x in self.block.find_loop('_atom_site_label')],
163
+ [y.upper() for y in self.block.find_loop('_atom_site_type_symbol')], strict=False))
157
164
  self.check_hkl_min_max()
158
165
 
159
166
  @property
@@ -207,18 +214,20 @@ class CifContainer:
207
214
  return doc
208
215
 
209
216
  def cif_as_string(self, without_hkl=False) -> str:
217
+ opt = gemmi.cif.WriteOptions()
218
+ opt.align_pairs = 33
210
219
  if without_hkl:
211
220
  # return a copy, do not delete hkl from original:
212
221
  doc = gemmi.cif.Document()
213
- doc.parse_string(self.doc.as_string(style=gemmi.cif.Style.Indent35))
222
+ doc.parse_string(self.doc.as_string(options=opt))
214
223
  for block in doc:
215
224
  if block.find_pair_item('_shelx_hkl_file'):
216
225
  block.find_pair_item('_shelx_hkl_file').erase()
217
226
  if block.find_pair_item('_shelx_fcf_file'):
218
227
  block.find_pair_item('_shelx_fcf_file').erase()
219
- return doc.as_string(style=gemmi.cif.Style.Indent35)
228
+ return doc.as_string(options=opt)
220
229
  else:
221
- return self.doc.as_string(style=gemmi.cif.Style.Indent35)
230
+ return self.doc.as_string(options=opt)
222
231
 
223
232
  def __getitem__(self, item: str) -> str:
224
233
  """
@@ -251,7 +260,6 @@ class CifContainer:
251
260
  f", {self.nangles()} angles")
252
261
 
253
262
  def file_is_there_and_writable(self) -> bool:
254
- import os
255
263
  return self.fileobj.exists() and self.fileobj.is_file() and os.access(self.fileobj, os.W_OK)
256
264
 
257
265
  def set_pair_delimited(self, key: str, txt: str):
@@ -272,13 +280,13 @@ class CifContainer:
272
280
 
273
281
  def save(self, filename: Path | None = None) -> None:
274
282
  """
275
- Saves the current cif file.
276
- :param filename: Name to save cif file to.
283
+ Saves the current cif file. It uses the '[basename]-finalcif.cif' suffix by default.
284
+ :param filename: Name to save cif file to if the default is not sufficient.
277
285
  """
278
286
  if not filename:
279
287
  filename = self.finalcif_file
280
288
  if self.is_empty():
281
- print(f'File {filename} is empty.')
289
+ print(f'DBG> CIF file {filename} is empty.')
282
290
  return
283
291
  if not self.is_writable(filename):
284
292
  raise PermissionError(f'Failed to open {filename.resolve()} for writing: Operation not permitted')
@@ -708,18 +716,18 @@ class CifContainer:
708
716
  Atoms from the CIF where values are returned as string like in the CIF with esds.
709
717
  """
710
718
  labels = self.block.find_loop('_atom_site_label')
711
- types = self.block.find_loop('_atom_site_type_symbol')
719
+ types = self.block.find_loop('_atom_site_type_symbol') or [atoms.get_atomlabel(x) for x in labels]
712
720
  x = self.block.find_loop('_atom_site_fract_x')
713
721
  y = self.block.find_loop('_atom_site_fract_y')
714
722
  z = self.block.find_loop('_atom_site_fract_z')
715
- part = self.block.find_loop('_atom_site_disorder_group')
716
- occ = self.block.find_loop('_atom_site_occupancy')
717
- u_eq = self.block.find_loop('_atom_site_U_iso_or_equiv')
723
+ part = self.block.find_loop('_atom_site_disorder_group') or ('0',) * len(labels)
724
+ occ = self.block.find_loop('_atom_site_occupancy') or ('1.000000',) * len(labels)
725
+ u_eq = self.block.find_loop('_atom_site_U_iso_or_equiv') or ('.',) * len(labels)
718
726
  atom = namedtuple('atom', ('label', 'type', 'x', 'y', 'z', 'part', 'occ', 'u_eq'))
719
727
  for label, type, x, y, z, part, occ, u_eq in zip(labels, types, x, y, z,
720
- part if part else ('0',) * len(labels),
721
- occ if occ else ('1.000000',) * len(labels),
722
- u_eq, strict=True):
728
+ part,
729
+ occ,
730
+ u_eq, strict=False):
723
731
  if without_h and self.ishydrogen(label):
724
732
  continue
725
733
  if self.picometer:
@@ -755,7 +763,7 @@ class CifContainer:
755
763
  u13 = self.block.find_loop('_atom_site_aniso_U_13')
756
764
  u12 = self.block.find_loop('_atom_site_aniso_U_12')
757
765
  adp = namedtuple('adp', ('label', 'U11', 'U22', 'U33', 'U23', 'U13', 'U12'))
758
- for label, u11, u22, u33, u23, u13, u12 in zip(labels, u11, u22, u33, u23, u13, u12):
766
+ for label, u11, u22, u33, u23, u13, u12 in zip(labels, u11, u22, u33, u23, u13, u12, strict=True):
759
767
  if self.picometer:
760
768
  u11 = _angstrom_to_x(u11, factor=100 ** 2)
761
769
  u22 = _angstrom_to_x(u22, factor=100 ** 2)
@@ -809,9 +817,9 @@ class CifContainer:
809
817
  label2 = self.block.find_loop('_geom_bond_atom_site_label_2')
810
818
  dist = self.block.find_loop('_geom_bond_distance')
811
819
  symm = self.block.find_loop('_geom_bond_site_symmetry_2')
812
- publ_loop = self.block.find_loop('_geom_bond_publ_flag')
820
+ publ_loop = self.block.find_loop('_geom_bond_publ_flag') or len(label1) * ['?']
813
821
  bond = namedtuple('bond', ('label1', 'label2', 'dist', 'symm'))
814
- for label1, label2, dist, symm, publ in zip(label1, label2, dist, symm, publ_loop):
822
+ for label1, label2, dist, symm, publ in zip(label1, label2, dist, symm, publ_loop, strict=False):
815
823
  if (without_h and (self.ishydrogen(label1) or self.ishydrogen(label2))) or self.yes_not_set(publ):
816
824
  continue
817
825
  else:
@@ -847,19 +855,22 @@ class CifContainer:
847
855
  angle_val = self.block.find_loop('_geom_angle')
848
856
  symm1 = self.block.find_loop('_geom_angle_site_symmetry_1')
849
857
  symm2 = self.block.find_loop('_geom_angle_site_symmetry_3')
850
- publ_loop = self.block.find_loop('_geom_angle_publ_flag')
858
+ publ_loop = self.block.find_loop('_geom_angle_publ_flag') or len(label1) * ['?']
851
859
  angle = namedtuple('angle', ('label1', 'label2', 'label3', 'angle_val', 'symm1', 'symm2'))
852
- for label1, label2, label3, angle_val, symm1, symm2, publ in \
853
- zip(label1, label2, label3, angle_val, symm1, symm2, publ_loop, strict=True):
854
- if ((without_H and (self.ishydrogen(label1) or self.ishydrogen(label2) or
855
- self.ishydrogen(label3))) or (self.yes_not_set(publ))):
860
+ for label1, label2, label3, angle_val, symm1, symm2, publ in (
861
+ zip(label1, label2, label3, angle_val, symm1, symm2, publ_loop, strict=False)):
862
+ if ((without_H and (self.ishydrogen(label1) or self.ishydrogen(label2) or self.ishydrogen(label3))) or
863
+ self.yes_not_set(publ)):
856
864
  continue
857
865
  else:
858
866
  yield angle(label1=label1, label2=label2, label3=label3, angle_val=angle_val,
859
867
  symm1=self.checksymm(symm1), symm2=self.checksymm(symm2))
860
868
 
861
869
  def _iselement(self, name: str) -> str:
862
- return self._name2elements[name.upper()]
870
+ try:
871
+ return self._name2elements[name.upper()]
872
+ except KeyError:
873
+ return atoms.get_atomlabel(name)
863
874
 
864
875
  def natoms(self, without_h: bool = False) -> int:
865
876
  return len(tuple(self.atoms(without_h)))
@@ -892,14 +903,15 @@ class CifContainer:
892
903
  symm2 = self.block.find_loop('_geom_torsion_site_symmetry_2')
893
904
  symm3 = self.block.find_loop('_geom_torsion_site_symmetry_3')
894
905
  symm4 = self.block.find_loop('_geom_torsion_site_symmetry_4')
895
- publ_loop = self.block.find_loop('_geom_torsion_publ_flag')
906
+ publ_loop = self.block.find_loop('_geom_torsion_publ_flag') or len(label1) * ['?']
896
907
  tors = namedtuple('Torsion',
897
908
  ('label1', 'label2', 'label3', 'label4', 'torsang', 'symm1', 'symm2', 'symm3', 'symm4'))
898
- for label1, label2, label3, label4, torsang, symm1, symm2, symm3, symm4, publ in zip(label1, label2, label3, # noqa: B020
909
+ for label1, label2, label3, label4, torsang, symm1, symm2, symm3, symm4, publ in zip(label1, label2, label3,
899
910
  label4,
900
911
  torsang, symm1, symm2,
901
912
  symm3,
902
- symm4, publ_loop, strict=True):
913
+ symm4, publ_loop,
914
+ strict=False):
903
915
  if ((without_h and (self.ishydrogen(label1) or self.ishydrogen(label2)
904
916
  or self.ishydrogen(label3) or self.ishydrogen(label3))) or self.yes_not_set(publ)):
905
917
  continue
@@ -918,11 +930,11 @@ class CifContainer:
918
930
  dist_da = self.block.find_loop('_geom_hbond_distance_DA')
919
931
  angle_dha = self.block.find_loop('_geom_hbond_angle_DHA')
920
932
  symm = self.block.find_loop('_geom_hbond_site_symmetry_A')
921
- publ_loop = self.block.find_loop('_geom_hbond_publ_flag')
933
+ publ_loop = self.block.find_loop('_geom_hbond_publ_flag') or len(label_d) * ['?']
922
934
  hydr = namedtuple('HydrogenBond', ('label_d', 'label_h', 'label_a', 'dist_dh', 'dist_ha', 'dist_da',
923
935
  'angle_dha', 'symm'))
924
936
  for label_d, label_h, label_a, dist_dh, dist_ha, dist_da, angle_dha, symm, publ in (
925
- zip(label_d, label_h, label_a, dist_dh, dist_ha, dist_da, angle_dha, symm, publ_loop)):
937
+ zip(label_d, label_h, label_a, dist_dh, dist_ha, dist_da, angle_dha, symm, publ_loop, strict=False)):
926
938
  if self.yes_not_set(publ):
927
939
  continue
928
940
  if self.picometer:
@@ -939,7 +951,12 @@ class CifContainer:
939
951
  Returns the key/value pairs of a cif file sorted by priority.
940
952
  """
941
953
  keys_without_values, keys_with_values = self.keys_with_essentials()
942
- return keys_without_values + [('These below are already in:', '---------------------')] + keys_with_values
954
+ return [*keys_without_values, ('These below are already in:', '---------------------'), *keys_with_values]
955
+
956
+ def pairs(self):
957
+ for item in self.block:
958
+ if item.pair is not None:
959
+ yield item.pair
943
960
 
944
961
  def _is_centrokey(self, key) -> bool:
945
962
  """
@@ -965,7 +982,7 @@ class CifContainer:
965
982
  # continue
966
983
  if self._is_centrokey(key):
967
984
  continue
968
- if not value or value == '?' or value == "'?'":
985
+ if not value or value in {'?', "'?'"}:
969
986
  questions.append((key, value))
970
987
  else:
971
988
  with_values.append((key, value))
@@ -10,7 +10,7 @@ from pathlib import Path
10
10
  from urllib.parse import urlparse
11
11
 
12
12
  import requests
13
- from PySide6.QtWidgets import QTableWidgetItem, QTextBrowser, QFrame
13
+ from qtpy.QtWidgets import QTableWidgetItem, QTextBrowser, QFrame
14
14
 
15
15
  from finalcif.cif.cif_file_io import CifContainer
16
16
  from finalcif.cif.cod.deposition_list import CODFetcher
@@ -210,7 +210,7 @@ class CODdeposit:
210
210
  if f.authenticated:
211
211
  self.token_valid = True
212
212
  else:
213
- self.ui.statusBar.showMessage('Unable to login. Check username/password.', timeout=12000)
213
+ self.ui.statusBar.showMessage('Unable to login. Check username/password.', 12000)
214
214
  f.get_table_data_by_token(self._cod_token)
215
215
  parser = MyCODStructuresParser()
216
216
  parser.feed(f.table_html)
@@ -346,14 +346,14 @@ class CODdeposit:
346
346
  self.ui.depositionOptionsStackedWidget.setCurrentIndex(self.deposition_type_to_int(deposition_type))
347
347
 
348
348
  def reset_deposit_button_state_to_initial(self):
349
- self.ui.depositCIFpushButton.disconnect()
349
+ self.ui.depositCIFpushButton.clicked.disconnect()
350
350
  self.ui.depositOutputTextBrowser.clear()
351
351
  self.ui.depositCIFpushButton.setText("Deposit CIF")
352
352
  self.ui.depositCIFpushButton.clicked.connect(self._init_deposit)
353
353
 
354
354
  def set_deposit_button_to_try_again(self):
355
355
  self.ui.depositCIFpushButton.setText("Try Again")
356
- self.ui.depositCIFpushButton.disconnect()
356
+ self.ui.depositCIFpushButton.clicked.disconnect()
357
357
  self.ui.depositCIFpushButton.clicked.connect(lambda: self.switch_to_page(self.deposition_type))
358
358
 
359
359
  def log_response_text(self, resp: requests.Response, *args, **kwargs):
@@ -380,7 +380,7 @@ class CODdeposit:
380
380
  self.user_email = text
381
381
 
382
382
  def _set_external_hkl_file(self) -> None:
383
- file = cif_file_open_dialog(filter="HKL file (*.hkl *.fcf)")
383
+ file = cif_file_open_dialog(parent=self, filter="HKL file (*.hkl *.fcf)")
384
384
  if file.endswith('.hkl'):
385
385
  if self.cif.res_file_data:
386
386
  hklf = self.cif._hklf_number_from_shelxl_file()
@@ -6,7 +6,7 @@ from pathlib import Path
6
6
  from typing import TYPE_CHECKING
7
7
 
8
8
  import gemmi
9
- from PySide6 import QtWidgets, QtCore
9
+ from qtpy import QtWidgets, QtCore
10
10
 
11
11
  from finalcif.cif import cif_order
12
12
  from finalcif.ciforder.order_ui import Ui_CifOrderForm
@@ -6,14 +6,14 @@
6
6
  ##
7
7
  ################################################################################
8
8
 
9
- from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
9
+ from qtpy.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
10
10
  QMetaObject, QObject, QPoint, QRect,
11
11
  QSize, QTime, QUrl, Qt)
12
- from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
12
+ from qtpy.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
13
13
  QFont, QFontDatabase, QGradient, QIcon,
14
14
  QImage, QKeySequence, QLinearGradient, QPainter,
15
15
  QPalette, QPixmap, QRadialGradient, QTransform)
16
- from PySide6.QtWidgets import (QAbstractItemView, QApplication, QGridLayout, QHeaderView,
16
+ from qtpy.QtWidgets import (QAbstractItemView, QApplication, QGridLayout, QHeaderView,
17
17
  QLabel, QPushButton, QSizePolicy, QSpacerItem,
18
18
  QTableWidget, QTableWidgetItem, QVBoxLayout, QWidget)
19
19
 
@@ -19,7 +19,7 @@ class SHELXTlistfile(ParserMixin):
19
19
  self.solutions = {}
20
20
  self.parse_file()
21
21
 
22
- def parse_file(self):
22
+ def parse_file(self) -> None:
23
23
  """
24
24
  + SHELXT - CRYSTAL STRUCTURE SOLUTION - VERSION 2018/2 +
25
25
  """
@@ -29,11 +29,15 @@ class SHELXTlistfile(ParserMixin):
29
29
  if 'SHELXTL' in line:
30
30
  self.version = 'SHELXT ' + line.split()[-1]
31
31
  if line.strip().startswith('R1 Rweak Alpha'):
32
- for n in range(100):
33
- if not self._text[num + 1 + n]:
34
- break
35
- if self._text[num + 1]:
36
- self.solutions[self._text[num + 1 + n][58:76].strip()] = self._text[num + 1 + n][37:51].strip()
32
+ try:
33
+ for n in range(100):
34
+ if not self._text[num + 1 + n]:
35
+ break
36
+ if self._text[num + 1]:
37
+ self.solutions[self._text[num + 1 + n][58:76].strip()] = self._text[num + 1 + n][37:51].strip()
38
+ except IndexError:
39
+ return None
40
+ return None
37
41
 
38
42
 
39
43
  class SolutionProgram:
@@ -58,17 +62,22 @@ class SolutionProgram:
58
62
  except (TypeError, AttributeError):
59
63
  res = ''
60
64
  byxt = res.find('REM SHELXT solution in')
61
- for x in xt_files:
62
- shelxt = SHELXTlistfile(x.as_posix())
63
- if shelxt.version and byxt:
64
- self.method = 'direct'
65
- self.solution_listfile = 'foo' # x.name
66
- return shelxt
67
65
  if byxt > 0:
66
+ for x in xt_files:
67
+ shelxt = SHELXTlistfile(x.as_posix())
68
+ if shelxt.version and byxt:
69
+ self.method = 'direct'
70
+ self.solution_listfile = 'foo' # x.name
71
+ return shelxt
68
72
  xt = SHELXTlistfile('')
69
73
  xt.version = "SHELXT (G. Sheldrick)"
70
74
  self.method = 'direct'
71
75
  return xt
76
+ if res.find('Best SHELXD solution'):
77
+ xt = SHELXTlistfile('')
78
+ xt.version = "SHELXD (G. Sheldrick)"
79
+ self.method = 'dual space'
80
+ return xt
72
81
  xt = SHELXTlistfile('')
73
82
  xt.version = "SHELXS (G. Sheldrick)"
74
83
  self.method = 'direct'
@@ -5,9 +5,9 @@ from math import sqrt, cos, sin, dist
5
5
  from pathlib import Path
6
6
 
7
7
  import numpy as np
8
- from PySide6 import QtWidgets, QtCore, QtGui
9
- from PySide6.QtCore import Qt
10
- from PySide6.QtGui import QPainter, QPen, QBrush, QColor, QMouseEvent, QPalette, QImage, QResizeEvent, QWheelEvent
8
+ from qtpy import QtWidgets, QtCore, QtGui
9
+ from qtpy.QtCore import Qt
10
+ from qtpy.QtGui import QPainter, QPen, QBrush, QColor, QMouseEvent, QPalette, QImage, QResizeEvent, QWheelEvent
11
11
 
12
12
  from finalcif.cif.atoms import get_radius_from_element, element2color
13
13
  from finalcif.cif.cif_file_io import CifContainer
@@ -1,8 +1,8 @@
1
1
  import sys
2
2
 
3
3
  import vtk
4
- from PySide6 import QtWidgets, QtCore
5
- from PySide6.QtGui import QSurfaceFormat
4
+ from qtpy import QtWidgets, QtCore
5
+ from qtpy.QtGui import QSurfaceFormat
6
6
  from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
7
7
  from vtkmodules.vtkCommonCore import vtkLookupTable
8
8
  from vtkmodules.vtkDomainsChemistry import vtkPeriodicTable