MoleditPy 2.4.7__tar.gz → 2.4.9__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 (64) hide show
  1. {moleditpy-2.4.7 → moleditpy-2.4.9}/PKG-INFO +1 -1
  2. {moleditpy-2.4.7 → moleditpy-2.4.9}/pyproject.toml +1 -1
  3. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/MoleditPy.egg-info/PKG-INFO +1 -1
  4. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/constants.py +1 -1
  5. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/main_window_app_state.py +43 -3
  6. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/main_window_main_init.py +33 -0
  7. {moleditpy-2.4.7 → moleditpy-2.4.9}/LICENSE +0 -0
  8. {moleditpy-2.4.7 → moleditpy-2.4.9}/README.md +0 -0
  9. {moleditpy-2.4.7 → moleditpy-2.4.9}/setup.cfg +0 -0
  10. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/MoleditPy.egg-info/SOURCES.txt +0 -0
  11. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/MoleditPy.egg-info/dependency_links.txt +0 -0
  12. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/MoleditPy.egg-info/entry_points.txt +0 -0
  13. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/MoleditPy.egg-info/requires.txt +0 -0
  14. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/MoleditPy.egg-info/top_level.txt +0 -0
  15. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/__init__.py +0 -0
  16. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/__main__.py +0 -0
  17. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/main.py +0 -0
  18. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/__init__.py +0 -0
  19. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/about_dialog.py +0 -0
  20. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/align_plane_dialog.py +0 -0
  21. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/alignment_dialog.py +0 -0
  22. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/analysis_window.py +0 -0
  23. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/angle_dialog.py +0 -0
  24. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/assets/file_icon.ico +0 -0
  25. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/assets/icon.icns +0 -0
  26. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/assets/icon.ico +0 -0
  27. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/assets/icon.png +0 -0
  28. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/atom_item.py +0 -0
  29. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/bond_item.py +0 -0
  30. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/bond_length_dialog.py +0 -0
  31. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/calculation_worker.py +0 -0
  32. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/color_settings_dialog.py +0 -0
  33. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/constrained_optimization_dialog.py +0 -0
  34. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/custom_interactor_style.py +0 -0
  35. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/custom_qt_interactor.py +0 -0
  36. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/dialog3_d_picking_mixin.py +0 -0
  37. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/dihedral_dialog.py +0 -0
  38. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/main_window.py +0 -0
  39. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/main_window_compute.py +0 -0
  40. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/main_window_dialog_manager.py +0 -0
  41. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/main_window_edit_3d.py +0 -0
  42. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/main_window_edit_actions.py +0 -0
  43. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/main_window_export.py +0 -0
  44. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/main_window_molecular_parsers.py +0 -0
  45. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/main_window_project_io.py +0 -0
  46. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/main_window_string_importers.py +0 -0
  47. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/main_window_ui_manager.py +0 -0
  48. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/main_window_view_3d.py +0 -0
  49. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/main_window_view_loaders.py +0 -0
  50. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/mirror_dialog.py +0 -0
  51. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/molecular_data.py +0 -0
  52. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/molecule_scene.py +0 -0
  53. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/move_group_dialog.py +0 -0
  54. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/periodic_table_dialog.py +0 -0
  55. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/planarize_dialog.py +0 -0
  56. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/plugin_interface.py +0 -0
  57. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/plugin_manager.py +0 -0
  58. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/plugin_manager_window.py +0 -0
  59. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/settings_dialog.py +0 -0
  60. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/template_preview_item.py +0 -0
  61. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/template_preview_view.py +0 -0
  62. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/translation_dialog.py +0 -0
  63. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/user_template_dialog.py +0 -0
  64. {moleditpy-2.4.7 → moleditpy-2.4.9}/src/moleditpy/modules/zoomable_view.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MoleditPy
3
- Version: 2.4.7
3
+ Version: 2.4.9
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 = "2.4.7"
8
+ version = "2.4.9"
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: 2.4.7
3
+ Version: 2.4.9
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
@@ -16,7 +16,7 @@ from PyQt6.QtGui import QFont, QColor
16
16
  from rdkit import Chem
17
17
 
18
18
  #Version
19
- VERSION = '2.4.7'
19
+ VERSION = '2.4.9'
20
20
 
21
21
  ATOM_RADIUS = 18
22
22
  BOND_OFFSET = 3.5
@@ -109,7 +109,21 @@ class MainWindowAppState(object):
109
109
 
110
110
  state['version'] = VERSION
111
111
 
112
- if self.current_mol: state['mol_3d'] = self.current_mol.ToBinary()
112
+ if self.current_mol:
113
+ state['mol_3d'] = self.current_mol.ToBinary()
114
+ # RDKit binary serialization does not preserve custom properties like _original_atom_id.
115
+ # We store them separately to ensure we can restore the 2D-3D link after undo/redo.
116
+ mol_3d_atom_ids = []
117
+ for i in range(self.current_mol.GetNumAtoms()):
118
+ atom = self.current_mol.GetAtomWithIdx(i)
119
+ try:
120
+ if atom.HasProp("_original_atom_id"):
121
+ mol_3d_atom_ids.append(atom.GetIntProp("_original_atom_id"))
122
+ else:
123
+ mol_3d_atom_ids.append(None)
124
+ except Exception:
125
+ mol_3d_atom_ids.append(None)
126
+ state['mol_3d_atom_ids'] = mol_3d_atom_ids
113
127
 
114
128
  state['is_3d_viewer_mode'] = not self.is_2d_editable
115
129
 
@@ -204,6 +218,27 @@ class MainWindowAppState(object):
204
218
  self.current_mol = Chem.Mol(loaded_data['mol_3d'])
205
219
  # デバッグ:3D構造が有効かチェック
206
220
  if self.current_mol and self.current_mol.GetNumAtoms() > 0:
221
+ # Restore _original_atom_id if present in saved state
222
+ if 'mol_3d_atom_ids' in loaded_data:
223
+ atom_ids = loaded_data['mol_3d_atom_ids']
224
+ if len(atom_ids) == self.current_mol.GetNumAtoms():
225
+ for i, aid in enumerate(atom_ids):
226
+ if aid is not None:
227
+ try:
228
+ self.current_mol.GetAtomWithIdx(i).SetIntProp("_original_atom_id", int(aid))
229
+ except Exception:
230
+ pass
231
+
232
+ # Re-create atom ID mapping to synchronize 2D atoms with 3D actors
233
+ # This MUST be done before draw_molecule_3d for labels to be correct.
234
+ try:
235
+ self.create_atom_id_mapping()
236
+ self.update_atom_id_menu_text()
237
+ self.update_atom_id_menu_state()
238
+ except Exception:
239
+ pass
240
+
241
+ # draw_molecule_3d will use the restored IDs for labels/picking if show_all_atom_info is called.
207
242
  self.draw_molecule_3d(self.current_mol)
208
243
  self.plotter.reset_camera()
209
244
  # 3D関連機能を統一的に有効化
@@ -254,7 +289,11 @@ class MainWindowAppState(object):
254
289
  'atoms': {k: (v['symbol'], v['item'].pos().x(), v['item'].pos().y(), v.get('charge', 0), v.get('radical', 0)) for k, v in self.data.atoms.items()},
255
290
  'bonds': {k: (v['order'], v.get('stereo', 0)) for k, v in self.data.bonds.items()},
256
291
  '_next_atom_id': self.data._next_atom_id,
257
- 'mol_3d': self.current_mol.ToBinary() if self.current_mol else None
292
+ 'mol_3d': self.current_mol.ToBinary() if self.current_mol else None,
293
+ 'mol_3d_atom_ids': [
294
+ (a.GetIntProp("_original_atom_id") if a.HasProp("_original_atom_id") else None)
295
+ for a in self.current_mol.GetAtoms()
296
+ ] if self.current_mol else None
258
297
  }
259
298
 
260
299
  last_state_for_comparison = None
@@ -266,7 +305,8 @@ class MainWindowAppState(object):
266
305
  'atoms': {k: (v['symbol'], v['pos'][0], v['pos'][1], v.get('charge', 0), v.get('radical', 0)) for k, v in last_atoms.items()},
267
306
  'bonds': {k: (v['order'], v.get('stereo', 0)) for k, v in last_bonds.items()},
268
307
  '_next_atom_id': last_state.get('_next_atom_id'),
269
- 'mol_3d': last_state.get('mol_3d', None)
308
+ 'mol_3d': last_state.get('mol_3d', None),
309
+ 'mol_3d_atom_ids': last_state.get('mol_3d_atom_ids', None)
270
310
  }
271
311
 
272
312
  if not last_state_for_comparison or current_state_for_comparison != last_state_for_comparison:
@@ -1838,6 +1838,39 @@ class MainWindowMainInit(object):
1838
1838
  # Add dynamic plugin actions (Legacy + New Registration)
1839
1839
  plugins = self.plugin_manager.discover_plugins(self)
1840
1840
 
1841
+ # --- Update 3D Style Menu with discovered styles ---
1842
+ # This menu is built in init_ui (before plugins are discovered), so we must update it here.
1843
+ if hasattr(self, 'style_button') and self.style_button.menu():
1844
+ style_menu = self.style_button.menu()
1845
+
1846
+ # Find the exclusive ActionGroup from an existing action (e.g. Ball & Stick)
1847
+ style_group = None
1848
+ for action in style_menu.actions():
1849
+ if action.actionGroup():
1850
+ style_group = action.actionGroup()
1851
+ break
1852
+
1853
+ if style_group and self.plugin_manager.custom_3d_styles:
1854
+ # Add separator if not present at end
1855
+ actions = style_menu.actions()
1856
+ if actions and not actions[-1].isSeparator():
1857
+ style_menu.addSeparator()
1858
+
1859
+ for style_name in self.plugin_manager.custom_3d_styles:
1860
+ # Check if already added to avoid duplicates
1861
+ exists = False
1862
+ for act in style_menu.actions():
1863
+ if act.text() == style_name:
1864
+ exists = True
1865
+ break
1866
+
1867
+ if not exists:
1868
+ action = QAction(style_name, self, checkable=True)
1869
+ # Use default arg to capture loop variable
1870
+ action.triggered.connect(lambda checked=False, s=style_name: self.set_3d_style(s))
1871
+ style_menu.addAction(action)
1872
+ style_group.addAction(action)
1873
+
1841
1874
  # 1. Add Registered Menu Actions (New System)
1842
1875
  if self.plugin_manager.menu_actions:
1843
1876
  for action_def in self.plugin_manager.menu_actions:
File without changes
File without changes
File without changes