MoleditPy-linux 4.9.0__tar.gz → 4.9.1__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 (85) hide show
  1. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/PKG-INFO +1 -1
  2. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/pyproject.toml +1 -1
  3. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/MoleditPy_linux.egg-info/PKG-INFO +1 -1
  4. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/io_logic.py +70 -24
  5. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/LICENSE +0 -0
  6. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/README.md +0 -0
  7. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/setup.cfg +0 -0
  8. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/MoleditPy_linux.egg-info/SOURCES.txt +0 -0
  9. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/MoleditPy_linux.egg-info/dependency_links.txt +0 -0
  10. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/MoleditPy_linux.egg-info/entry_points.txt +0 -0
  11. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/MoleditPy_linux.egg-info/requires.txt +0 -0
  12. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/MoleditPy_linux.egg-info/top_level.txt +0 -0
  13. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/__init__.py +0 -0
  14. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/__main__.py +0 -0
  15. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/assets/file_icon.ico +0 -0
  16. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/assets/icon.icns +0 -0
  17. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/assets/icon.ico +0 -0
  18. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/assets/icon.png +0 -0
  19. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/core/__init__.py +0 -0
  20. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/core/mol_geometry.py +0 -0
  21. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/core/molecular_data.py +0 -0
  22. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/main.py +0 -0
  23. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/plugins/__init__.py +0 -0
  24. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/plugins/plugin_interface.py +0 -0
  25. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/plugins/plugin_manager.py +0 -0
  26. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/plugins/plugin_manager_window.py +0 -0
  27. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/__init__.py +0 -0
  28. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/about_dialog.py +0 -0
  29. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/align_plane_dialog.py +0 -0
  30. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/alignment_dialog.py +0 -0
  31. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/analysis_window.py +0 -0
  32. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/angle_dialog.py +0 -0
  33. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/app_state.py +0 -0
  34. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/atom_item.py +0 -0
  35. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/atom_picking.py +0 -0
  36. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/base_picking_dialog.py +0 -0
  37. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/bond_item.py +0 -0
  38. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/bond_length_dialog.py +0 -0
  39. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/calculation_worker.py +0 -0
  40. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/chain_mixin.py +0 -0
  41. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/color_settings_dialog.py +0 -0
  42. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/compute_logic.py +0 -0
  43. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/constrained_optimization_dialog.py +0 -0
  44. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/custom_interactor_style.py +0 -0
  45. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/custom_qt_interactor.py +0 -0
  46. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/dialog_3d_picking_mixin.py +0 -0
  47. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/dialog_logic.py +0 -0
  48. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/dihedral_dialog.py +0 -0
  49. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/edit_3d_logic.py +0 -0
  50. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/edit_actions_logic.py +0 -0
  51. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/export_logic.py +0 -0
  52. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/geometry_base_dialog.py +0 -0
  53. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/main_window.py +0 -0
  54. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/main_window_init.py +0 -0
  55. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/mirror_dialog.py +0 -0
  56. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/molecular_scene_handler.py +0 -0
  57. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/molecule_scene.py +0 -0
  58. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/move_group_dialog.py +0 -0
  59. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/move_selected_atoms_dialog.py +0 -0
  60. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/periodic_table_dialog.py +0 -0
  61. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/planarize_dialog.py +0 -0
  62. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/plugin_menu_manager.py +0 -0
  63. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/preview_molecule.py +0 -0
  64. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/settings_dialog.py +0 -0
  65. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/settings_tabs/__init__.py +0 -0
  66. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/settings_tabs/settings_2d_cleanup_tab.py +0 -0
  67. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/settings_tabs/settings_2d_tab.py +0 -0
  68. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/settings_tabs/settings_3d_tabs.py +0 -0
  69. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/settings_tabs/settings_other_tab.py +0 -0
  70. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/settings_tabs/settings_tab_base.py +0 -0
  71. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/string_importers.py +0 -0
  72. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/template_preview_item.py +0 -0
  73. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/template_preview_view.py +0 -0
  74. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/translation_dialog.py +0 -0
  75. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/ui_manager.py +0 -0
  76. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/user_template_dialog.py +0 -0
  77. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/view_3d_logic.py +0 -0
  78. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/ui/zoomable_view.py +0 -0
  79. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/utils/__init__.py +0 -0
  80. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/utils/constants.py +0 -0
  81. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/utils/default_settings.py +0 -0
  82. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/utils/hit_radius.py +0 -0
  83. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/utils/sip_isdeleted_safe.py +0 -0
  84. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/utils/suppress_log.py +0 -0
  85. {moleditpy_linux-4.9.0 → moleditpy_linux-4.9.1}/src/moleditpy_linux/utils/system_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MoleditPy-linux
3
- Version: 4.9.0
3
+ Version: 4.9.1
4
4
  Summary: A cross-platform, simple, and intuitive molecular structure editor built in Python. It allows 2D molecular drawing and 3D structure visualization. It supports exporting structure files for input to DFT calculation software.
5
5
  Author-email: HiroYokoyama <titech.yoko.hiro@gmail.com>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  name = "MoleditPy-linux"
7
7
 
8
- version = "4.9.0"
8
+ version = "4.9.1"
9
9
 
10
10
  license = {file = "LICENSE"}
11
11
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MoleditPy-linux
3
- Version: 4.9.0
3
+ Version: 4.9.1
4
4
  Summary: A cross-platform, simple, and intuitive molecular structure editor built in Python. It allows 2D molecular drawing and 3D structure visualization. It supports exporting structure files for input to DFT calculation software.
5
5
  Author-email: HiroYokoyama <titech.yoko.hiro@gmail.com>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -30,6 +30,7 @@ from PyQt6.QtWidgets import (
30
30
  QPushButton,
31
31
  QFileDialog,
32
32
  QMessageBox,
33
+ QWidget,
33
34
  )
34
35
 
35
36
  from rdkit import Chem
@@ -109,6 +110,63 @@ class IOManager:
109
110
  lines[3] = self.fix_mol_counts_line(lines[3])
110
111
  return "\n".join(lines)
111
112
 
113
+ @staticmethod
114
+ def _first_sdf_record(sdf_text: str) -> str:
115
+ """Return the MOL block of the first record in SDF text.
116
+
117
+ Chem.SDMolSupplier(file_path) reads the file itself and cannot be
118
+ given pre-decoded text, so it never sees the encoding fallbacks in
119
+ _read_text_lines_flexible and mishandles CP932/Shift-JIS SDF files.
120
+ Extracting the first record's MOL block (everything before the first
121
+ "$$$$" separator) lets the already-decoded text go through
122
+ MolFromMolBlock instead, matching the .mol import path. Trailing SDF
123
+ property tags after "M END" are harmless — MolFromMolBlock stops
124
+ reading once it hits the end of the connection table.
125
+ """
126
+ record_lines = []
127
+ for line in sdf_text.splitlines():
128
+ if line.strip() == "$$$$":
129
+ break
130
+ record_lines.append(line)
131
+ return "\n".join(record_lines)
132
+
133
+ def _load_mol_block_text(self, file_path: str) -> str:
134
+ """Read a .mol/.sdf file flexibly-encoded and return a MolFromMolBlock-ready block.
135
+
136
+ Shared by load_mol_file and load_mol_file_for_3d_viewing so the
137
+ flexible-encoding read, .sdf first-record extraction, and V2000
138
+ counts-line fix live in one place.
139
+ """
140
+ raw = "".join(self._read_text_lines_flexible(file_path))
141
+ if file_path.lower().endswith(".sdf"):
142
+ raw = self._first_sdf_record(raw)
143
+ return self.fix_mol_block(raw)
144
+
145
+ @staticmethod
146
+ def _read_text_lines_flexible(file_path: str) -> list[str]:
147
+ """Read a text file's lines, tolerating non-UTF-8 encodings.
148
+
149
+ XYZ comment lines are free-form and files authored on Japanese
150
+ (or other non-UTF-8) locales are commonly saved as Shift-JIS/CP932
151
+ rather than UTF-8, which raises UnicodeDecodeError under a strict
152
+ utf-8 open(). Fall back through the encodings Windows/macOS/Linux
153
+ editors actually produce before giving up and replacing the
154
+ undecodable bytes, so a mojibake comment never blocks the atoms
155
+ themselves from loading.
156
+ """
157
+ # euc_jp must be tried before cp932/shift_jis: cp932 silently accepts
158
+ # some genuinely EUC-JP byte sequences and decodes them to the wrong
159
+ # (mojibake) characters instead of raising, so trying cp932 first
160
+ # would never let euc_jp text reach its correct decoding.
161
+ for encoding in ("utf-8-sig", "euc_jp", "cp932", "shift_jis"):
162
+ try:
163
+ with open(file_path, "r", encoding=encoding) as f:
164
+ return f.readlines()
165
+ except UnicodeDecodeError:
166
+ continue
167
+ with open(file_path, "r", encoding="utf-8", errors="replace") as f:
168
+ return f.readlines()
169
+
112
170
  @staticmethod
113
171
  def _is_numeric_token(token: str) -> bool:
114
172
  """True if *token* parses as a float."""
@@ -326,7 +384,8 @@ class IOManager:
326
384
  if status_bar is not None:
327
385
  status_bar.showMessage(message)
328
386
  if not os.environ.get("MOLEDITPY_HEADLESS"):
329
- QMessageBox.warning(self.host, title, message)
387
+ parent = self.host if isinstance(self.host, QWidget) else None
388
+ QMessageBox.warning(parent, title, message)
330
389
 
331
390
  def load_xyz_file(self, file_path: str) -> Optional[Any]:
332
391
  """Load XYZ file and create RDKit Mol with charge prompt and bond determination."""
@@ -334,8 +393,7 @@ class IOManager:
334
393
  return None
335
394
 
336
395
  try:
337
- with open(file_path, "r", encoding="utf-8") as f:
338
- return self._mol_from_xyz_lines(f.readlines())
396
+ return self._mol_from_xyz_lines(self._read_text_lines_flexible(file_path))
339
397
  except (RuntimeError, TypeError, ValueError, UnicodeDecodeError) as e:
340
398
  self.host.statusBar().showMessage(f"Error parsing XYZ file: {e}")
341
399
  return None
@@ -777,19 +835,13 @@ class IOManager:
777
835
  return
778
836
 
779
837
  try:
780
- if file_path.lower().endswith(".mol"):
781
- with open(file_path, "r", encoding="utf-8", errors="replace") as fh:
782
- raw = fh.read()
783
- fixed_block = self.fix_mol_block(raw)
784
- # Both readers can come back empty on a malformed file; the
785
- # stub for MolFromMolBlock does not say so, hence the explicit
786
- # declaration. The None check below covers both branches.
787
- mol: Optional[Chem.Mol] = Chem.MolFromMolBlock(
788
- fixed_block, sanitize=True, removeHs=False
789
- )
790
- else:
791
- suppl = Chem.SDMolSupplier(file_path, removeHs=False)
792
- mol = next(suppl, None)
838
+ fixed_block = self._load_mol_block_text(file_path)
839
+ # MolFromMolBlock can come back empty on a malformed file; the
840
+ # stub does not say so, hence the explicit declaration. The None
841
+ # check below covers that.
842
+ mol: Optional[Chem.Mol] = Chem.MolFromMolBlock(
843
+ fixed_block, sanitize=True, removeHs=False
844
+ )
793
845
 
794
846
  if mol is None:
795
847
  raise ValueError("Failed to read molecule from file.")
@@ -991,14 +1043,8 @@ class IOManager:
991
1043
  if not file_path:
992
1044
  return
993
1045
  try:
994
- if file_path.lower().endswith(".sdf"):
995
- suppl = Chem.SDMolSupplier(file_path, removeHs=False)
996
- mol = next(suppl, None)
997
- else:
998
- with open(file_path, "r", encoding="utf-8", errors="replace") as fh:
999
- raw = fh.read()
1000
- fixed_block = self.fix_mol_block(raw)
1001
- mol = Chem.MolFromMolBlock(fixed_block, sanitize=True, removeHs=False)
1046
+ fixed_block = self._load_mol_block_text(file_path)
1047
+ mol = Chem.MolFromMolBlock(fixed_block, sanitize=True, removeHs=False)
1002
1048
 
1003
1049
  if mol is None:
1004
1050
  raise ValueError("Failed to load molecule.")
File without changes