MoleditPy 3.0.0a8__tar.gz → 3.0.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 (78) hide show
  1. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/PKG-INFO +1 -1
  2. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/pyproject.toml +1 -1
  3. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/MoleditPy.egg-info/PKG-INFO +1 -1
  4. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/plugins/plugin_interface.py +33 -2
  5. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/plugins/plugin_manager_window.py +56 -56
  6. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/angle_dialog.py +404 -404
  7. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/bond_length_dialog.py +351 -351
  8. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/io_logic.py +2 -0
  9. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/main_window.py +7 -2
  10. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/main_window_init.py +16 -20
  11. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/settings_dialog.py +5 -1
  12. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/settings_tabs/settings_3d_tabs.py +2 -4
  13. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/ui_manager.py +51 -8
  14. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/view_3d_logic.py +2 -2
  15. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/utils/constants.py +1 -1
  16. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/utils/default_settings.py +85 -85
  17. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/LICENSE +0 -0
  18. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/README.md +0 -0
  19. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/setup.cfg +0 -0
  20. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/MoleditPy.egg-info/SOURCES.txt +0 -0
  21. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/MoleditPy.egg-info/dependency_links.txt +0 -0
  22. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/MoleditPy.egg-info/entry_points.txt +0 -0
  23. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/MoleditPy.egg-info/requires.txt +0 -0
  24. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/MoleditPy.egg-info/top_level.txt +0 -0
  25. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/__init__.py +0 -0
  26. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/__main__.py +0 -0
  27. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/assets/file_icon.ico +0 -0
  28. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/assets/icon.icns +0 -0
  29. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/assets/icon.ico +0 -0
  30. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/assets/icon.png +0 -0
  31. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/core/__init__.py +0 -0
  32. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/core/mol_geometry.py +0 -0
  33. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/core/molecular_data.py +0 -0
  34. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/main.py +0 -0
  35. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/plugins/__init__.py +0 -0
  36. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/plugins/plugin_manager.py +0 -0
  37. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/__init__.py +0 -0
  38. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/about_dialog.py +0 -0
  39. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/align_plane_dialog.py +0 -0
  40. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/alignment_dialog.py +0 -0
  41. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/analysis_window.py +0 -0
  42. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/app_state.py +0 -0
  43. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/atom_item.py +0 -0
  44. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/base_picking_dialog.py +0 -0
  45. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/bond_item.py +0 -0
  46. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/calculation_worker.py +0 -0
  47. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/color_settings_dialog.py +0 -0
  48. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/compute_logic.py +0 -0
  49. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/constrained_optimization_dialog.py +0 -0
  50. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/custom_interactor_style.py +0 -0
  51. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/custom_qt_interactor.py +0 -0
  52. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/dialog_3d_picking_mixin.py +0 -0
  53. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/dialog_logic.py +0 -0
  54. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/dihedral_dialog.py +0 -0
  55. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/edit_3d_logic.py +0 -0
  56. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/edit_actions_logic.py +0 -0
  57. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/export_logic.py +0 -0
  58. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/geometry_base_dialog.py +0 -0
  59. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/mirror_dialog.py +0 -0
  60. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/molecular_scene_handler.py +0 -0
  61. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/molecule_scene.py +0 -0
  62. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/move_group_dialog.py +0 -0
  63. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/periodic_table_dialog.py +0 -0
  64. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/planarize_dialog.py +0 -0
  65. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/settings_tabs/__init__.py +0 -0
  66. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/settings_tabs/settings_2d_tab.py +0 -0
  67. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/settings_tabs/settings_other_tab.py +0 -0
  68. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/settings_tabs/settings_tab_base.py +0 -0
  69. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/sip_isdeleted_safe.py +0 -0
  70. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/string_importers.py +0 -0
  71. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/template_preview_item.py +0 -0
  72. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/template_preview_view.py +0 -0
  73. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/translation_dialog.py +0 -0
  74. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/user_template_dialog.py +0 -0
  75. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/ui/zoomable_view.py +0 -0
  76. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/utils/__init__.py +0 -0
  77. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/src/moleditpy/utils/sip_isdeleted_safe.py +0 -0
  78. {moleditpy-3.0.0a8 → moleditpy-3.0.1}/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.0a8
3
+ Version: 3.0.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"
7
7
 
8
- version = "3.0.0a8"
8
+ version = "3.0.1"
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.0a8
3
+ Version: 3.0.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
@@ -173,6 +173,14 @@ class PluginContext:
173
173
  """
174
174
  return self._manager.get_main_window()
175
175
 
176
+ def show_status_message(self, message: str, timeout: int = 3000) -> None:
177
+ """
178
+ Display a temporary message in the status bar of the main window.
179
+ """
180
+ mw = self.get_main_window()
181
+ if mw and hasattr(mw, "ui_manager"):
182
+ mw.ui_manager.update_status_bar(message)
183
+
176
184
  @property
177
185
  def current_mol(self) -> Any:
178
186
  """
@@ -186,8 +194,7 @@ class PluginContext:
186
194
  mw = self.get_main_window()
187
195
  if mw and hasattr(mw, "view_3d_manager"):
188
196
  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)
197
+ mw.view_3d_manager.draw_molecule_3d(mol)
191
198
 
192
199
  @property
193
200
  def current_molecule(self) -> Any:
@@ -214,6 +221,30 @@ class PluginContext:
214
221
  mw = self.get_main_window()
215
222
  return mw.init_manager.scene if mw and hasattr(mw, "init_manager") else None
216
223
 
224
+ def draw_molecule_3d(self, mol: Any) -> None:
225
+ """Draw a molecule in the 3D scene (Direct manager call)."""
226
+ mw = self.get_main_window()
227
+ if mw and hasattr(mw, "view_3d_manager"):
228
+ mw.view_3d_manager.draw_molecule_3d(mol)
229
+
230
+ def refresh_3d_view(self) -> None:
231
+ """Force the 3D window to redraw using the current molecule."""
232
+ mw = self.get_main_window()
233
+ if mw and hasattr(mw, "view_3d_manager"):
234
+ mol = getattr(mw.view_3d_manager, "current_mol", None)
235
+ if mol:
236
+ mw.view_3d_manager.draw_molecule_3d(mol)
237
+ else:
238
+ # Also redraw/clear plotter if no molecule
239
+ if hasattr(mw.view_3d_manager, "plotter") and mw.view_3d_manager.plotter:
240
+ mw.view_3d_manager.plotter.render()
241
+
242
+ def reset_3d_camera(self) -> None:
243
+ """Zoom in and re-center the 3D viewport to fit the current molecule."""
244
+ mw = self.get_main_window()
245
+ if mw and hasattr(mw, "view_3d_manager") and mw.view_3d_manager.plotter:
246
+ mw.view_3d_manager.plotter.reset_camera()
247
+
217
248
  def add_export_action(self, label: str, callback: Callable):
218
249
  """
219
250
  Register a custom export action.
@@ -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"