MoleditPy 3.0.0b1__tar.gz → 3.0.2__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 (78) hide show
  1. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/PKG-INFO +1 -1
  2. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/pyproject.toml +1 -1
  3. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/MoleditPy.egg-info/PKG-INFO +1 -1
  4. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/MoleditPy.egg-info/SOURCES.txt +0 -6
  5. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/plugins/plugin_interface.py +44 -22
  6. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/plugins/plugin_manager.py +0 -1
  7. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/plugins/plugin_manager_window.py +56 -56
  8. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/angle_dialog.py +404 -404
  9. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/app_state.py +0 -1
  10. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/bond_item.py +0 -7
  11. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/bond_length_dialog.py +351 -351
  12. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/custom_interactor_style.py +0 -6
  13. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/dialog_logic.py +0 -4
  14. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/edit_actions_logic.py +1 -1
  15. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/io_logic.py +10 -6
  16. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/main_window.py +7 -2
  17. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/main_window_init.py +34 -45
  18. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/molecular_scene_handler.py +1 -3
  19. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/molecule_scene.py +0 -4
  20. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/settings_dialog.py +5 -2
  21. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/settings_tabs/settings_3d_tabs.py +2 -4
  22. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/ui_manager.py +51 -10
  23. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/user_template_dialog.py +1 -1
  24. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/view_3d_logic.py +3 -3
  25. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/zoomable_view.py +0 -3
  26. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/utils/constants.py +1 -1
  27. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/utils/default_settings.py +85 -85
  28. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/LICENSE +0 -0
  29. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/README.md +0 -0
  30. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/setup.cfg +0 -0
  31. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/MoleditPy.egg-info/dependency_links.txt +0 -0
  32. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/MoleditPy.egg-info/entry_points.txt +0 -0
  33. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/MoleditPy.egg-info/requires.txt +0 -0
  34. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/MoleditPy.egg-info/top_level.txt +0 -0
  35. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/__init__.py +0 -0
  36. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/__main__.py +0 -0
  37. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/assets/file_icon.ico +0 -0
  38. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/assets/icon.icns +0 -0
  39. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/assets/icon.ico +0 -0
  40. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/assets/icon.png +0 -0
  41. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/core/__init__.py +0 -0
  42. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/core/mol_geometry.py +0 -0
  43. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/core/molecular_data.py +0 -0
  44. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/main.py +0 -0
  45. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/plugins/__init__.py +0 -0
  46. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/__init__.py +0 -0
  47. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/about_dialog.py +0 -0
  48. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/align_plane_dialog.py +0 -0
  49. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/alignment_dialog.py +0 -0
  50. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/analysis_window.py +0 -0
  51. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/atom_item.py +0 -0
  52. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/base_picking_dialog.py +0 -0
  53. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/calculation_worker.py +0 -0
  54. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/color_settings_dialog.py +0 -0
  55. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/compute_logic.py +0 -0
  56. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/constrained_optimization_dialog.py +0 -0
  57. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/custom_qt_interactor.py +0 -0
  58. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/dialog_3d_picking_mixin.py +0 -0
  59. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/dihedral_dialog.py +0 -0
  60. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/edit_3d_logic.py +0 -0
  61. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/export_logic.py +0 -0
  62. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/geometry_base_dialog.py +0 -0
  63. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/mirror_dialog.py +0 -0
  64. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/move_group_dialog.py +0 -0
  65. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/periodic_table_dialog.py +0 -0
  66. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/planarize_dialog.py +0 -0
  67. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/settings_tabs/__init__.py +0 -0
  68. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/settings_tabs/settings_2d_tab.py +0 -0
  69. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/settings_tabs/settings_other_tab.py +0 -0
  70. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/settings_tabs/settings_tab_base.py +0 -0
  71. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/sip_isdeleted_safe.py +0 -0
  72. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/string_importers.py +0 -0
  73. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/template_preview_item.py +0 -0
  74. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/template_preview_view.py +0 -0
  75. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/ui/translation_dialog.py +0 -0
  76. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/utils/__init__.py +0 -0
  77. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/utils/sip_isdeleted_safe.py +0 -0
  78. {moleditpy-3.0.0b1 → moleditpy-3.0.2}/src/moleditpy/utils/system_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MoleditPy
3
- Version: 3.0.0b1
3
+ Version: 3.0.2
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"
7
7
 
8
- version = "3.0.0b1"
8
+ version = "3.0.2"
9
9
 
10
10
  license = {file = "LICENSE"}
11
11
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MoleditPy
3
- Version: 3.0.0b1
3
+ Version: 3.0.2
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
@@ -10,12 +10,6 @@ src/MoleditPy.egg-info/top_level.txt
10
10
  src/moleditpy/__init__.py
11
11
  src/moleditpy/__main__.py
12
12
  src/moleditpy/main.py
13
- src/moleditpy.egg-info/PKG-INFO
14
- src/moleditpy.egg-info/SOURCES.txt
15
- src/moleditpy.egg-info/dependency_links.txt
16
- src/moleditpy.egg-info/entry_points.txt
17
- src/moleditpy.egg-info/requires.txt
18
- src/moleditpy.egg-info/top_level.txt
19
13
  src/moleditpy/assets/file_icon.ico
20
14
  src/moleditpy/assets/icon.icns
21
15
  src/moleditpy/assets/icon.ico
@@ -119,12 +119,6 @@ class PluginContext:
119
119
  """
120
120
  return Plugin3DController(self._manager.get_main_window())
121
121
 
122
- def show_status_message(self, message: str, timeout: int = 3000) -> None:
123
- """
124
- Display a message in the application status bar.
125
- """
126
- self._manager.show_status_message(message, timeout)
127
-
128
122
  def push_undo_checkpoint(self) -> None:
129
123
  """
130
124
  Create an undo checkpoint for the current state.
@@ -133,18 +127,6 @@ class PluginContext:
133
127
  """
134
128
  self._manager.push_undo_checkpoint()
135
129
 
136
- def refresh_3d_view(self) -> None:
137
- """
138
- Force a refresh (re-render) of the 3D scene.
139
- """
140
- self._manager.refresh_3d_view()
141
-
142
- def reset_3d_camera(self) -> None:
143
- """
144
- Resets the 3D camera to fit the current molecule.
145
- """
146
- self._manager.reset_3d_camera()
147
-
148
130
  def get_selected_atom_indices(self) -> List[int]:
149
131
  """
150
132
  Returns a list of RDKit atom indices currently selected in the 2D or 3D view.
@@ -173,21 +155,30 @@ class PluginContext:
173
155
  """
174
156
  return self._manager.get_main_window()
175
157
 
158
+ def show_status_message(self, message: str, timeout: int = 3000) -> None:
159
+ """
160
+ Display a temporary message in the status bar of the main window.
161
+ """
162
+ self._manager.show_status_message(message, timeout)
163
+
176
164
  @property
177
165
  def current_mol(self) -> Any:
178
166
  """
179
167
  Get or set the current molecule (RDKit Mol object). Shortcut for current_molecule.
180
168
  """
181
169
  mw = self.get_main_window()
182
- return mw.view_3d_manager.current_mol if mw and hasattr(mw, "view_3d_manager") else None
170
+ return (
171
+ mw.view_3d_manager.current_mol
172
+ if mw and hasattr(mw, "view_3d_manager")
173
+ else None
174
+ )
183
175
 
184
176
  @current_mol.setter
185
177
  def current_mol(self, mol: Any):
186
178
  mw = self.get_main_window()
187
179
  if mw and hasattr(mw, "view_3d_manager"):
188
180
  mw.view_3d_manager.current_mol = mol
189
- if hasattr(mw.view_3d_manager, "draw_molecule_3d"):
190
- mw.view_3d_manager.draw_molecule_3d(mol)
181
+ mw.view_3d_manager.draw_molecule_3d(mol)
191
182
 
192
183
  @property
193
184
  def current_molecule(self) -> Any:
@@ -204,7 +195,11 @@ class PluginContext:
204
195
  Returns the PyVista plotter from the MainWindow.
205
196
  """
206
197
  mw = self.get_main_window()
207
- return mw.view_3d_manager.plotter if mw and hasattr(mw, "view_3d_manager") else None
198
+ return (
199
+ mw.view_3d_manager.plotter
200
+ if mw and hasattr(mw, "view_3d_manager")
201
+ else None
202
+ )
208
203
 
209
204
  @property
210
205
  def scene(self) -> Any:
@@ -214,6 +209,33 @@ class PluginContext:
214
209
  mw = self.get_main_window()
215
210
  return mw.init_manager.scene if mw and hasattr(mw, "init_manager") else None
216
211
 
212
+ def draw_molecule_3d(self, mol: Any) -> None:
213
+ """Draw a molecule in the 3D scene (Direct manager call)."""
214
+ mw = self.get_main_window()
215
+ if mw and hasattr(mw, "view_3d_manager"):
216
+ mw.view_3d_manager.draw_molecule_3d(mol)
217
+
218
+ def refresh_3d_view(self) -> None:
219
+ """Force the 3D window to redraw using the current molecule."""
220
+ mw = self.get_main_window()
221
+ if mw and hasattr(mw, "view_3d_manager"):
222
+ mol = getattr(mw.view_3d_manager, "current_mol", None)
223
+ if mol:
224
+ mw.view_3d_manager.draw_molecule_3d(mol)
225
+ else:
226
+ # Also redraw/clear plotter if no molecule
227
+ if (
228
+ hasattr(mw.view_3d_manager, "plotter")
229
+ and mw.view_3d_manager.plotter
230
+ ):
231
+ mw.view_3d_manager.plotter.render()
232
+
233
+ def reset_3d_camera(self) -> None:
234
+ """Zoom in and re-center the 3D viewport to fit the current molecule."""
235
+ mw = self.get_main_window()
236
+ if mw and hasattr(mw, "view_3d_manager") and mw.view_3d_manager.plotter:
237
+ mw.view_3d_manager.plotter.reset_camera()
238
+
217
239
  def add_export_action(self, label: str, callback: Callable):
218
240
  """
219
241
  Register a custom export action.
@@ -514,7 +514,6 @@ class PluginManager:
514
514
 
515
515
  # Check 2D selection
516
516
  try:
517
- from .plugin_interface import PluginContext
518
517
  # We need to access the scene items.
519
518
  # In MoleditPy, atoms in the scene are AtomItem objects which have an 'atom_id'.
520
519
  # These atom_ids map to entries in state_manager.data.atoms.
@@ -10,9 +10,9 @@ Repo: https://github.com/HiroYokoyama/python_molecular_editor
10
10
  DOI: 10.5281/zenodo.17268532
11
11
  """
12
12
 
13
- import os
14
- import shutil
15
- import hashlib
13
+ import os
14
+ import shutil
15
+ import hashlib
16
16
 
17
17
  from PyQt6.QtCore import Qt, QUrl
18
18
  from PyQt6.QtGui import QDesktopServices, QDragEnterEvent, QDropEvent
@@ -258,10 +258,10 @@ class PluginManagerWindow(QDialog):
258
258
  is_valid = True
259
259
  is_folder = True
260
260
 
261
- if is_valid:
262
- sha256_value = self._compute_sha256(file_path)
263
- # Extract info and confirm
264
- info = {
261
+ if is_valid:
262
+ sha256_value = self._compute_sha256(file_path)
263
+ # Extract info and confirm
264
+ info = {
265
265
  "name": os.path.basename(file_path),
266
266
  "version": "Unknown",
267
267
  "author": "Unknown",
@@ -281,15 +281,15 @@ class PluginManagerWindow(QDialog):
281
281
  elif file_path.endswith(".py"):
282
282
  info = self.plugin_manager.get_plugin_info_safe(file_path)
283
283
 
284
- msg = (
285
- f"Do you want to install this plugin?\n\n"
286
- f"Name: {info['name']}\n"
287
- f"Author: {info['author']}\n"
288
- f"Version: {info['version']}\n"
289
- f"Description: {info['description']}\n\n"
290
- f"File: {os.path.basename(file_path)}\n"
291
- f"SHA-256: {sha256_value}"
292
- )
284
+ msg = (
285
+ f"Do you want to install this plugin?\n\n"
286
+ f"Name: {info['name']}\n"
287
+ f"Author: {info['author']}\n"
288
+ f"Version: {info['version']}\n"
289
+ f"Description: {info['description']}\n\n"
290
+ f"File: {os.path.basename(file_path)}\n"
291
+ f"SHA-256: {sha256_value}"
292
+ )
293
293
 
294
294
  reply = QMessageBox.question(
295
295
  self,
@@ -310,43 +310,43 @@ class PluginManagerWindow(QDialog):
310
310
  summary = ""
311
311
  if files_installed:
312
312
  summary += "Installed:\n" + "\n".join(files_installed) + "\n\n"
313
- if errors:
314
- summary += "Errors:\n" + "\n".join(errors)
315
-
316
- QMessageBox.information(self, "Plugin Installation", summary)
317
-
318
- def _compute_sha256(self, path):
319
- if os.path.isfile(path):
320
- return self._sha256_for_file(path)
321
- if os.path.isdir(path):
322
- return self._sha256_for_directory(path)
323
- return "N/A"
324
-
325
- def _sha256_for_file(self, path):
326
- hasher = hashlib.sha256()
327
- try:
328
- with open(path, "rb") as f:
329
- for chunk in iter(lambda: f.read(8192), b""):
330
- hasher.update(chunk)
331
- return hasher.hexdigest()
332
- except (AttributeError, OSError, RuntimeError, ValueError, TypeError):
333
- return "N/A"
334
-
335
- def _sha256_for_directory(self, dir_path):
336
- hasher = hashlib.sha256()
337
- try:
338
- root = os.path.abspath(dir_path)
339
- for current_root, _dirs, files in os.walk(root):
340
- rel_root = os.path.relpath(current_root, root)
341
- for filename in sorted(files):
342
- file_path = os.path.join(current_root, filename)
343
- rel_path = os.path.normpath(os.path.join(rel_root, filename))
344
- hasher.update(rel_path.encode("utf-8", errors="replace"))
345
- hasher.update(b"\0")
346
- with open(file_path, "rb") as f:
347
- for chunk in iter(lambda: f.read(8192), b""):
348
- hasher.update(chunk)
349
- hasher.update(b"\0")
350
- return hasher.hexdigest()
351
- except (AttributeError, OSError, RuntimeError, ValueError, TypeError):
352
- return "N/A"
313
+ if errors:
314
+ summary += "Errors:\n" + "\n".join(errors)
315
+
316
+ QMessageBox.information(self, "Plugin Installation", summary)
317
+
318
+ def _compute_sha256(self, path):
319
+ if os.path.isfile(path):
320
+ return self._sha256_for_file(path)
321
+ if os.path.isdir(path):
322
+ return self._sha256_for_directory(path)
323
+ return "N/A"
324
+
325
+ def _sha256_for_file(self, path):
326
+ hasher = hashlib.sha256()
327
+ try:
328
+ with open(path, "rb") as f:
329
+ for chunk in iter(lambda: f.read(8192), b""):
330
+ hasher.update(chunk)
331
+ return hasher.hexdigest()
332
+ except (AttributeError, OSError, RuntimeError, ValueError, TypeError):
333
+ return "N/A"
334
+
335
+ def _sha256_for_directory(self, dir_path):
336
+ hasher = hashlib.sha256()
337
+ try:
338
+ root = os.path.abspath(dir_path)
339
+ for current_root, _dirs, files in os.walk(root):
340
+ rel_root = os.path.relpath(current_root, root)
341
+ for filename in sorted(files):
342
+ file_path = os.path.join(current_root, filename)
343
+ rel_path = os.path.normpath(os.path.join(rel_root, filename))
344
+ hasher.update(rel_path.encode("utf-8", errors="replace"))
345
+ hasher.update(b"\0")
346
+ with open(file_path, "rb") as f:
347
+ for chunk in iter(lambda: f.read(8192), b""):
348
+ hasher.update(chunk)
349
+ hasher.update(b"\0")
350
+ return hasher.hexdigest()
351
+ except (AttributeError, OSError, RuntimeError, ValueError, TypeError):
352
+ return "N/A"