MoleditPy-linux 2.5.2__tar.gz → 2.6.0__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 (65) hide show
  1. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/PKG-INFO +1 -1
  2. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/pyproject.toml +2 -2
  3. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/MoleditPy_linux.egg-info/PKG-INFO +1 -1
  4. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/MoleditPy_linux.egg-info/SOURCES.txt +6 -5
  5. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/about_dialog.py +1 -1
  6. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/align_plane_dialog.py +4 -31
  7. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/alignment_dialog.py +1 -29
  8. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/angle_dialog.py +8 -84
  9. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/atom_item.py +2 -2
  10. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/bond_item.py +2 -4
  11. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/bond_length_dialog.py +7 -84
  12. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/color_settings_dialog.py +5 -3
  13. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/constants.py +1 -1
  14. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/constrained_optimization_dialog.py +1 -9
  15. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/dialog3_d_picking_mixin.py +69 -0
  16. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/dihedral_dialog.py +16 -99
  17. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/main_window.py +0 -20
  18. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/main_window_app_state.py +0 -15
  19. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/main_window_compute.py +5 -29
  20. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/main_window_dialog_manager.py +0 -16
  21. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/main_window_edit_3d.py +8 -55
  22. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/main_window_edit_actions.py +6 -32
  23. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/main_window_export.py +2 -16
  24. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/main_window_main_init.py +17 -22
  25. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/main_window_molecular_parsers.py +1 -17
  26. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/main_window_project_io.py +1 -16
  27. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/main_window_string_importers.py +1 -15
  28. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/main_window_ui_manager.py +0 -16
  29. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/main_window_view_3d.py +0 -16
  30. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/main_window_view_loaders.py +0 -17
  31. moleditpy_linux-2.6.0/src/moleditpy_linux/modules/mol_geometry.py +165 -0
  32. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/move_group_dialog.py +5 -6
  33. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/planarize_dialog.py +6 -24
  34. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/plugin_interface.py +0 -7
  35. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/plugin_manager_window.py +3 -3
  36. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/settings_dialog.py +7 -147
  37. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/translation_dialog.py +2 -8
  38. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/LICENSE +0 -0
  39. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/README.md +0 -0
  40. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/setup.cfg +0 -0
  41. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/MoleditPy_linux.egg-info/dependency_links.txt +0 -0
  42. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/MoleditPy_linux.egg-info/entry_points.txt +0 -0
  43. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/MoleditPy_linux.egg-info/requires.txt +0 -0
  44. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/MoleditPy_linux.egg-info/top_level.txt +0 -0
  45. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/__init__.py +0 -0
  46. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/__main__.py +0 -0
  47. {moleditpy_linux-2.5.2/src/moleditpy_linux/modules → moleditpy_linux-2.6.0/src/moleditpy_linux}/assets/file_icon.ico +0 -0
  48. {moleditpy_linux-2.5.2/src/moleditpy_linux/modules → moleditpy_linux-2.6.0/src/moleditpy_linux}/assets/icon.icns +0 -0
  49. {moleditpy_linux-2.5.2/src/moleditpy_linux/modules → moleditpy_linux-2.6.0/src/moleditpy_linux}/assets/icon.ico +0 -0
  50. {moleditpy_linux-2.5.2/src/moleditpy_linux/modules → moleditpy_linux-2.6.0/src/moleditpy_linux}/assets/icon.png +0 -0
  51. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/main.py +0 -0
  52. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/__init__.py +0 -0
  53. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/analysis_window.py +0 -0
  54. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/calculation_worker.py +0 -0
  55. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/custom_interactor_style.py +0 -0
  56. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/custom_qt_interactor.py +0 -0
  57. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/mirror_dialog.py +0 -0
  58. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/molecular_data.py +0 -0
  59. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/molecule_scene.py +0 -0
  60. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/periodic_table_dialog.py +0 -0
  61. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/plugin_manager.py +0 -0
  62. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/template_preview_item.py +0 -0
  63. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/template_preview_view.py +0 -0
  64. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/user_template_dialog.py +0 -0
  65. {moleditpy_linux-2.5.2 → moleditpy_linux-2.6.0}/src/moleditpy_linux/modules/zoomable_view.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MoleditPy-linux
3
- Version: 2.5.2
3
+ Version: 2.6.0
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 = "2.5.2"
8
+ version = "2.6.0"
9
9
 
10
10
  license = {file = "LICENSE"}
11
11
 
@@ -48,5 +48,5 @@ Issues = "https://github.com/HiroYokoyama/python_molecular_editor/issues"
48
48
  moleditpy = "moleditpy_linux.__main__:main"
49
49
 
50
50
  [tool.setuptools.package-data]
51
- "moleditpy_linux" = ["modules/assets/*"]
51
+ "moleditpy_linux" = ["assets/*"]
52
52
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MoleditPy-linux
3
- Version: 2.5.2
3
+ Version: 2.6.0
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,6 +10,10 @@ src/MoleditPy_linux.egg-info/top_level.txt
10
10
  src/moleditpy_linux/__init__.py
11
11
  src/moleditpy_linux/__main__.py
12
12
  src/moleditpy_linux/main.py
13
+ src/moleditpy_linux/assets/file_icon.ico
14
+ src/moleditpy_linux/assets/icon.icns
15
+ src/moleditpy_linux/assets/icon.ico
16
+ src/moleditpy_linux/assets/icon.png
13
17
  src/moleditpy_linux/modules/__init__.py
14
18
  src/moleditpy_linux/modules/about_dialog.py
15
19
  src/moleditpy_linux/modules/align_plane_dialog.py
@@ -42,6 +46,7 @@ src/moleditpy_linux/modules/main_window_ui_manager.py
42
46
  src/moleditpy_linux/modules/main_window_view_3d.py
43
47
  src/moleditpy_linux/modules/main_window_view_loaders.py
44
48
  src/moleditpy_linux/modules/mirror_dialog.py
49
+ src/moleditpy_linux/modules/mol_geometry.py
45
50
  src/moleditpy_linux/modules/molecular_data.py
46
51
  src/moleditpy_linux/modules/molecule_scene.py
47
52
  src/moleditpy_linux/modules/move_group_dialog.py
@@ -55,8 +60,4 @@ src/moleditpy_linux/modules/template_preview_item.py
55
60
  src/moleditpy_linux/modules/template_preview_view.py
56
61
  src/moleditpy_linux/modules/translation_dialog.py
57
62
  src/moleditpy_linux/modules/user_template_dialog.py
58
- src/moleditpy_linux/modules/zoomable_view.py
59
- src/moleditpy_linux/modules/assets/file_icon.ico
60
- src/moleditpy_linux/modules/assets/icon.icns
61
- src/moleditpy_linux/modules/assets/icon.ico
62
- src/moleditpy_linux/modules/assets/icon.png
63
+ src/moleditpy_linux/modules/zoomable_view.py
@@ -37,7 +37,7 @@ class AboutDialog(QDialog): # pragma: no cover
37
37
  self.image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
38
38
 
39
39
  # Load the original icon image
40
- icon_path = os.path.join(os.path.dirname(__file__), 'assets', 'icon.png')
40
+ icon_path = os.path.join(os.path.dirname(__file__), '..', 'assets', 'icon.png')
41
41
  if os.path.exists(icon_path):
42
42
  original_pixmap = QPixmap(icon_path)
43
43
  # Scale to 2x size (160x160)
@@ -163,39 +163,12 @@ class AlignPlaneDialog(Dialog3DPickingMixin, QDialog): # pragma: no cover
163
163
 
164
164
  def show_atom_labels(self):
165
165
  """選択された原子にラベルを表示"""
166
- # 既存のラベルをクリア
167
- self.clear_atom_labels()
168
-
169
- # 新しいラベルを表示
170
- if not hasattr(self, 'selection_labels'):
171
- self.selection_labels = []
172
-
173
166
  if self.selected_atoms:
174
167
  sorted_atoms = sorted(self.selected_atoms)
175
-
176
- for i, atom_idx in enumerate(sorted_atoms):
177
- pos = self.main_window.atom_positions_3d[atom_idx]
178
- label_text = f"#{i+1}"
179
-
180
- # ラベルを追加
181
- label_actor = self.main_window.plotter.add_point_labels(
182
- [pos], [label_text],
183
- point_size=20,
184
- font_size=12,
185
- text_color='blue',
186
- always_visible=True
187
- )
188
- self.selection_labels.append(label_actor)
189
-
190
- def clear_atom_labels(self):
191
- """原子ラベルをクリア"""
192
- if hasattr(self, 'selection_labels'):
193
- for label_actor in self.selection_labels:
194
- try:
195
- self.main_window.plotter.remove_actor(label_actor)
196
- except Exception:
197
- pass
198
- self.selection_labels = []
168
+ pairs = [(idx, f"#{i+1}") for i, idx in enumerate(sorted_atoms)]
169
+ self.show_atom_labels_for(pairs, color='blue')
170
+ else:
171
+ self.clear_atom_labels()
199
172
 
200
173
  def apply_PlaneAlign(self):
201
174
  """alignを適用(回転ベース)"""
@@ -13,7 +13,7 @@ DOI: 10.5281/zenodo.17268532
13
13
  from PyQt6.QtWidgets import (
14
14
  QDialog, QVBoxLayout, QLabel, QHBoxLayout, QPushButton
15
15
  )
16
- from PyQt6.QtCore import Qt
16
+
17
17
  from PyQt6.QtWidgets import QMessageBox
18
18
  import numpy as np
19
19
 
@@ -133,24 +133,6 @@ class AlignmentDialog(Dialog3DPickingMixin, QDialog): # pragma: no cover
133
133
  self.selected_atoms.clear()
134
134
  self.update_display()
135
135
 
136
- def add_selection_label(self, atom_idx, label_text):
137
- """選択された原子にラベルを追加"""
138
- if not hasattr(self, 'selection_labels'):
139
- self.selection_labels = []
140
-
141
- # 原子の位置を取得
142
- pos = self.main_window.atom_positions_3d[atom_idx]
143
-
144
- # ラベルを追加
145
- label_actor = self.main_window.plotter.add_point_labels(
146
- [pos], [label_text],
147
- point_size=20,
148
- font_size=12,
149
- text_color='yellow',
150
- always_visible=True
151
- )
152
- self.selection_labels.append(label_actor)
153
-
154
136
  def remove_atom_label(self, atom_idx):
155
137
  """特定の原子のラベルを削除"""
156
138
  # 簡単化のため、全ラベルをクリアして再描画
@@ -159,16 +141,6 @@ class AlignmentDialog(Dialog3DPickingMixin, QDialog): # pragma: no cover
159
141
  if idx != atom_idx:
160
142
  self.add_selection_label(idx, f"Atom {i}")
161
143
 
162
- def clear_selection_labels(self):
163
- """選択ラベルをクリア"""
164
- if hasattr(self, 'selection_labels'):
165
- for label_actor in self.selection_labels:
166
- try:
167
- self.main_window.plotter.remove_actor(label_actor)
168
- except Exception:
169
- pass
170
- self.selection_labels = []
171
-
172
144
  def apply_alignment(self):
173
145
  """アライメントを適用"""
174
146
  if len(self.selected_atoms) != 2:
@@ -16,8 +16,10 @@ from PyQt6.QtWidgets import (
16
16
 
17
17
  try:
18
18
  from .dialog3_d_picking_mixin import Dialog3DPickingMixin
19
+ from .mol_geometry import get_connected_group
19
20
  except Exception:
20
21
  from modules.dialog3_d_picking_mixin import Dialog3DPickingMixin
22
+ from modules.mol_geometry import get_connected_group
21
23
 
22
24
  from PyQt6.QtCore import Qt
23
25
  from PyQt6.QtWidgets import QMessageBox
@@ -169,69 +171,10 @@ class AngleDialog(Dialog3DPickingMixin, QDialog): # pragma: no cover
169
171
 
170
172
  def show_atom_labels(self):
171
173
  """選択された原子にラベルを表示"""
172
- # 既存のラベルをクリア
173
- self.clear_atom_labels()
174
-
175
- # 新しいラベルを表示
176
- if not hasattr(self, 'selection_labels'):
177
- self.selection_labels = []
178
-
179
174
  selected_atoms = [self.atom1_idx, self.atom2_idx, self.atom3_idx]
180
175
  labels = ["1st", "2nd (vertex)", "3rd"]
181
- colors = ["yellow", "yellow", "yellow"] # 全て黄色に統一
182
-
183
- for i, atom_idx in enumerate(selected_atoms):
184
- if atom_idx is not None:
185
- pos = self.main_window.atom_positions_3d[atom_idx]
186
- label_text = f"{labels[i]}"
187
-
188
- # ラベルを追加
189
- label_actor = self.main_window.plotter.add_point_labels(
190
- [pos], [label_text],
191
- point_size=20,
192
- font_size=12,
193
- text_color=colors[i],
194
- always_visible=True
195
- )
196
- self.selection_labels.append(label_actor)
197
-
198
- def clear_atom_labels(self):
199
- """原子ラベルをクリア"""
200
- if hasattr(self, 'selection_labels'):
201
- for label_actor in self.selection_labels:
202
- try:
203
- self.main_window.plotter.remove_actor(label_actor)
204
- except Exception:
205
- pass
206
- self.selection_labels = []
207
-
208
- def clear_selection_labels(self):
209
- """選択ラベルをクリア"""
210
- if hasattr(self, 'selection_labels'):
211
- for label_actor in self.selection_labels:
212
- try:
213
- self.main_window.plotter.remove_actor(label_actor)
214
- except Exception:
215
- pass
216
- self.selection_labels = []
217
-
218
- def add_selection_label(self, atom_idx, label_text):
219
- """選択された原子にラベルを追加"""
220
- if not hasattr(self, 'selection_labels'):
221
- self.selection_labels = []
222
-
223
- # 原子の位置を取得
224
- pos = self.main_window.atom_positions_3d[atom_idx]
225
-
226
- # ラベルを追加
227
- label_actor = self.main_window.plotter.add_point_labels(
228
- [pos], [label_text],
229
- point_size=20,
230
- font_size=12,
231
- text_color='yellow',
232
- always_visible=True
233
- )
234
- self.selection_labels.append(label_actor)
176
+ pairs = [(idx, labels[i]) for i, idx in enumerate(selected_atoms) if idx is not None]
177
+ self.show_atom_labels_for(pairs)
235
178
 
236
179
  def update_display(self):
237
180
  """表示を更新"""
@@ -372,8 +315,8 @@ class AngleDialog(Dialog3DPickingMixin, QDialog): # pragma: no cover
372
315
  half_rotation = total_rotation_angle / 2
373
316
 
374
317
  # Get both connected groups
375
- group1_atoms = self.get_connected_group(self.atom1_idx, exclude=self.atom2_idx)
376
- group3_atoms = self.get_connected_group(self.atom3_idx, exclude=self.atom2_idx)
318
+ group1_atoms = get_connected_group(self.mol, self.atom1_idx, exclude=self.atom2_idx)
319
+ group3_atoms = get_connected_group(self.mol, self.atom3_idx, exclude=self.atom2_idx)
377
320
 
378
321
  # Rotate group 1 by -half_rotation
379
322
  for atom_idx in group1_atoms:
@@ -401,7 +344,7 @@ class AngleDialog(Dialog3DPickingMixin, QDialog): # pragma: no cover
401
344
  self.main_window.atom_positions_3d[self.atom3_idx] = new_pos3
402
345
  else:
403
346
  # Rotate the connected group around atom2 (vertex) - default behavior
404
- atoms_to_move = self.get_connected_group(self.atom3_idx, exclude=self.atom2_idx)
347
+ atoms_to_move = get_connected_group(self.mol, self.atom3_idx, exclude=self.atom2_idx)
405
348
 
406
349
  for atom_idx in atoms_to_move:
407
350
  current_pos = np.array(conf.GetAtomPosition(atom_idx))
@@ -417,23 +360,4 @@ class AngleDialog(Dialog3DPickingMixin, QDialog): # pragma: no cover
417
360
  # Update the 3D view
418
361
  self.main_window.draw_molecule_3d(self.mol)
419
362
 
420
- def get_connected_group(self, start_atom, exclude=None):
421
- """指定された原子から連結されているグループを取得"""
422
- visited = set()
423
- to_visit = [start_atom]
424
-
425
- while to_visit:
426
- current = to_visit.pop()
427
- if current in visited or current == exclude:
428
- continue
429
-
430
- visited.add(current)
431
-
432
- # Get neighboring atoms
433
- atom = self.mol.GetAtomWithIdx(current)
434
- for bond in atom.GetBonds():
435
- other_idx = bond.GetOtherAtomIdx(current)
436
- if other_idx not in visited and other_idx != exclude:
437
- to_visit.append(other_idx)
438
-
439
- return visited
363
+
@@ -23,13 +23,13 @@ from PyQt6.QtCore import (
23
23
  try:
24
24
  from .constants import (
25
25
  ATOM_RADIUS, DESIRED_ATOM_PIXEL_RADIUS,
26
- FONT_FAMILY, FONT_SIZE_LARGE, FONT_WEIGHT_BOLD,
26
+ FONT_FAMILY, FONT_WEIGHT_BOLD,
27
27
  CPK_COLORS,
28
28
  )
29
29
  except Exception:
30
30
  from modules.constants import (
31
31
  ATOM_RADIUS, DESIRED_ATOM_PIXEL_RADIUS,
32
- FONT_FAMILY, FONT_SIZE_LARGE, FONT_WEIGHT_BOLD,
32
+ FONT_FAMILY, FONT_WEIGHT_BOLD,
33
33
  CPK_COLORS,
34
34
  )
35
35
 
@@ -24,18 +24,16 @@ from PyQt6.QtCore import (
24
24
  try:
25
25
  from .constants import (
26
26
  EZ_LABEL_BOX_SIZE, EZ_LABEL_TEXT_OUTLINE, EZ_LABEL_MARGIN,
27
- BOND_OFFSET, FONT_FAMILY, FONT_SIZE_LARGE, FONT_WEIGHT_BOLD,
27
+ FONT_FAMILY, FONT_WEIGHT_BOLD,
28
28
  HOVER_PEN_WIDTH, DESIRED_BOND_PIXEL_WIDTH,
29
29
  )
30
30
  except Exception:
31
31
  from modules.constants import (
32
32
  EZ_LABEL_BOX_SIZE, EZ_LABEL_TEXT_OUTLINE, EZ_LABEL_MARGIN,
33
- BOND_OFFSET, FONT_FAMILY, FONT_SIZE_LARGE, FONT_WEIGHT_BOLD,
33
+ FONT_FAMILY, FONT_WEIGHT_BOLD,
34
34
  HOVER_PEN_WIDTH, DESIRED_BOND_PIXEL_WIDTH,
35
35
  )
36
36
 
37
- from rdkit import Chem
38
-
39
37
  class BondItem(QGraphicsItem):
40
38
 
41
39
  def get_ez_label_rect(self):
@@ -15,6 +15,7 @@ from PyQt6.QtWidgets import (
15
15
  )
16
16
 
17
17
  from .dialog3_d_picking_mixin import Dialog3DPickingMixin
18
+ from .mol_geometry import get_connected_group
18
19
 
19
20
  from PyQt6.QtCore import Qt
20
21
  import numpy as np
@@ -158,69 +159,10 @@ class BondLengthDialog(Dialog3DPickingMixin, QDialog): # pragma: no cover
158
159
 
159
160
  def show_atom_labels(self):
160
161
  """選択された原子にラベルを表示"""
161
- # 既存のラベルをクリア
162
- self.clear_atom_labels()
163
-
164
- # 新しいラベルを表示
165
- if not hasattr(self, 'selection_labels'):
166
- self.selection_labels = []
167
-
168
162
  selected_atoms = [self.atom1_idx, self.atom2_idx]
169
163
  labels = ["1st", "2nd"]
170
- colors = ["yellow", "yellow"]
171
-
172
- for i, atom_idx in enumerate(selected_atoms):
173
- if atom_idx is not None:
174
- pos = self.main_window.atom_positions_3d[atom_idx]
175
- label_text = f"{labels[i]}"
176
-
177
- # ラベルを追加
178
- label_actor = self.main_window.plotter.add_point_labels(
179
- [pos], [label_text],
180
- point_size=20,
181
- font_size=12,
182
- text_color=colors[i],
183
- always_visible=True
184
- )
185
- self.selection_labels.append(label_actor)
186
-
187
- def clear_atom_labels(self):
188
- """原子ラベルをクリア"""
189
- if hasattr(self, 'selection_labels'):
190
- for label_actor in self.selection_labels:
191
- try:
192
- self.main_window.plotter.remove_actor(label_actor)
193
- except Exception:
194
- pass
195
- self.selection_labels = []
196
-
197
- def clear_selection_labels(self):
198
- """選択ラベルをクリア"""
199
- if hasattr(self, 'selection_labels'):
200
- for label_actor in self.selection_labels:
201
- try:
202
- self.main_window.plotter.remove_actor(label_actor)
203
- except Exception:
204
- pass
205
- self.selection_labels = []
206
-
207
- def add_selection_label(self, atom_idx, label_text):
208
- """選択された原子にラベルを追加"""
209
- if not hasattr(self, 'selection_labels'):
210
- self.selection_labels = []
211
-
212
- # 原子の位置を取得
213
- pos = self.main_window.atom_positions_3d[atom_idx]
214
-
215
- # ラベルを追加
216
- label_actor = self.main_window.plotter.add_point_labels(
217
- [pos], [label_text],
218
- point_size=20,
219
- font_size=12,
220
- text_color='yellow',
221
- always_visible=True
222
- )
223
- self.selection_labels.append(label_actor)
164
+ pairs = [(idx, labels[i]) for i, idx in enumerate(selected_atoms) if idx is not None]
165
+ self.show_atom_labels_for(pairs)
224
166
 
225
167
  def update_display(self):
226
168
  """表示を更新"""
@@ -317,8 +259,8 @@ class BondLengthDialog(Dialog3DPickingMixin, QDialog): # pragma: no cover
317
259
  new_pos2 = bond_center + direction * half_distance
318
260
 
319
261
  # Get both connected groups
320
- group1_atoms = self.get_connected_group(self.atom1_idx, exclude=self.atom2_idx)
321
- group2_atoms = self.get_connected_group(self.atom2_idx, exclude=self.atom1_idx)
262
+ group1_atoms = get_connected_group(self.mol, self.atom1_idx, exclude=self.atom2_idx)
263
+ group2_atoms = get_connected_group(self.mol, self.atom2_idx, exclude=self.atom1_idx)
322
264
 
323
265
  # Calculate displacements
324
266
  displacement1 = new_pos1 - pos1
@@ -346,7 +288,7 @@ class BondLengthDialog(Dialog3DPickingMixin, QDialog): # pragma: no cover
346
288
  else:
347
289
  # Move the connected group (default behavior)
348
290
  new_pos2 = pos1 + direction * new_distance
349
- atoms_to_move = self.get_connected_group(self.atom2_idx, exclude=self.atom1_idx)
291
+ atoms_to_move = get_connected_group(self.mol, self.atom2_idx, exclude=self.atom1_idx)
350
292
  displacement = new_pos2 - pos2
351
293
 
352
294
  for atom_idx in atoms_to_move:
@@ -358,23 +300,4 @@ class BondLengthDialog(Dialog3DPickingMixin, QDialog): # pragma: no cover
358
300
  # Update the 3D view
359
301
  self.main_window.draw_molecule_3d(self.mol)
360
302
 
361
- def get_connected_group(self, start_atom, exclude=None):
362
- """指定された原子から連結されているグループを取得"""
363
- visited = set()
364
- to_visit = [start_atom]
365
-
366
- while to_visit:
367
- current = to_visit.pop()
368
- if current in visited or current == exclude:
369
- continue
370
-
371
- visited.add(current)
372
-
373
- # Get neighboring atoms
374
- atom = self.mol.GetAtomWithIdx(current)
375
- for bond in atom.GetBonds():
376
- other_idx = bond.GetOtherAtomIdx(current)
377
- if other_idx not in visited and other_idx != exclude:
378
- to_visit.append(other_idx)
379
-
380
- return visited
303
+
@@ -227,10 +227,12 @@ class ColorSettingsDialog(QDialog): # pragma: no cover
227
227
  # Refresh any open SettingsDialog instances so the ball & stick color preview updates
228
228
  try:
229
229
  # Avoid circular import at module level; import SettingsDialog on demand
230
+ import importlib
230
231
  try:
231
- from .settings_dialog import SettingsDialog
232
- except Exception:
233
- from modules.settings_dialog import SettingsDialog
232
+ mod = importlib.import_module('.settings_dialog', package='moleditpy.modules')
233
+ except ImportError:
234
+ mod = importlib.import_module('modules.settings_dialog')
235
+ SettingsDialog = mod.SettingsDialog
234
236
 
235
237
  for w in QApplication.topLevelWidgets():
236
238
  try:
@@ -16,7 +16,7 @@ from PyQt6.QtGui import QFont, QColor
16
16
  from rdkit import Chem
17
17
 
18
18
  #Version
19
- VERSION = '2.5.2'
19
+ VERSION = '2.6.0'
20
20
 
21
21
  ATOM_RADIUS = 18
22
22
  BOND_OFFSET = 3.5
@@ -585,15 +585,7 @@ class ConstrainedOptimizationDialog(Dialog3DPickingMixin, QDialog): # pragma: no
585
585
  else:
586
586
  self.selection_labels.append(label_actor)
587
587
 
588
- def clear_selection_labels(self):
589
- """選択ラベル(A1, A2...)をクリア"""
590
- if hasattr(self, 'selection_labels'):
591
- for label_actor in self.selection_labels:
592
- try:
593
- self.main_window.plotter.remove_actor(label_actor)
594
- except Exception:
595
- pass
596
- self.selection_labels = []
588
+
597
589
 
598
590
  def on_cell_changed(self, row, column):
599
591
  """テーブルのセルが編集されたときに内部データを更新する"""
@@ -134,3 +134,72 @@ class Dialog3DPickingMixin:
134
134
 
135
135
  def try_alternative_picking(self, x, y):
136
136
  """代替のピッキング方法(使用しない)"""
137
+
138
+ # ------------------------------------------------------------------
139
+ # Label management (shared across dialogs)
140
+ # ------------------------------------------------------------------
141
+
142
+ def clear_atom_labels(self):
143
+ """Remove all label actors from the plotter."""
144
+ if hasattr(self, 'selection_labels'):
145
+ for label_actor in self.selection_labels:
146
+ try:
147
+ self.main_window.plotter.remove_actor(label_actor)
148
+ except Exception:
149
+ pass
150
+ self.selection_labels = []
151
+
152
+ # Alias — some dialogs use this name instead.
153
+ clear_selection_labels = clear_atom_labels
154
+
155
+ def add_selection_label(self, atom_idx, label_text, color='yellow'):
156
+ """Add a point label at the position of *atom_idx*.
157
+
158
+ Parameters
159
+ ----------
160
+ atom_idx : int
161
+ Index into ``self.main_window.atom_positions_3d``.
162
+ label_text : str
163
+ Text shown next to the atom.
164
+ color : str, optional
165
+ Label colour (default ``'yellow'``).
166
+ """
167
+ if not hasattr(self, 'selection_labels'):
168
+ self.selection_labels = []
169
+
170
+ pos = self.main_window.atom_positions_3d[atom_idx]
171
+
172
+ label_actor = self.main_window.plotter.add_point_labels(
173
+ [pos], [label_text],
174
+ point_size=20,
175
+ font_size=12,
176
+ text_color=color,
177
+ always_visible=True
178
+ )
179
+ self.selection_labels.append(label_actor)
180
+
181
+ def show_atom_labels_for(self, atoms_and_labels, color='yellow'):
182
+ """Clear existing labels and add new ones for each *(idx, text)* pair.
183
+
184
+ Parameters
185
+ ----------
186
+ atoms_and_labels : list[tuple[int, str]]
187
+ Each element is ``(atom_idx, label_text)``.
188
+ color : str, optional
189
+ Label colour (default ``'yellow'``).
190
+ """
191
+ self.clear_atom_labels()
192
+
193
+ if not hasattr(self, 'selection_labels'):
194
+ self.selection_labels = []
195
+
196
+ for atom_idx, label_text in atoms_and_labels:
197
+ pos = self.main_window.atom_positions_3d[atom_idx]
198
+ label_actor = self.main_window.plotter.add_point_labels(
199
+ [pos], [label_text],
200
+ point_size=20,
201
+ font_size=12,
202
+ text_color=color,
203
+ always_visible=True
204
+ )
205
+ self.selection_labels.append(label_actor)