MoleditPy-linux 3.0.1__tar.gz → 3.0.3__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.
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/PKG-INFO +1 -1
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/pyproject.toml +1 -1
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/MoleditPy_linux.egg-info/PKG-INFO +1 -1
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/plugins/plugin_interface.py +15 -24
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/plugins/plugin_manager.py +0 -1
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/app_state.py +0 -1
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/bond_item.py +0 -7
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/custom_interactor_style.py +0 -6
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/dialog_logic.py +0 -4
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/edit_actions_logic.py +1 -1
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/io_logic.py +8 -6
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/main_window_init.py +10 -14
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/molecular_scene_handler.py +1 -3
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/molecule_scene.py +0 -4
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/settings_dialog.py +0 -1
- moleditpy_linux-3.0.3/src/moleditpy_linux/ui/translation_dialog.py +340 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/ui_manager.py +0 -2
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/user_template_dialog.py +1 -1
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/view_3d_logic.py +1 -1
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/zoomable_view.py +0 -3
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/utils/constants.py +1 -1
- moleditpy_linux-3.0.1/src/moleditpy_linux/ui/translation_dialog.py +0 -193
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/LICENSE +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/README.md +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/setup.cfg +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/MoleditPy_linux.egg-info/SOURCES.txt +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/MoleditPy_linux.egg-info/dependency_links.txt +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/MoleditPy_linux.egg-info/entry_points.txt +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/MoleditPy_linux.egg-info/requires.txt +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/MoleditPy_linux.egg-info/top_level.txt +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/__init__.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/__main__.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/assets/file_icon.ico +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/assets/icon.icns +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/assets/icon.ico +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/assets/icon.png +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/core/__init__.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/core/mol_geometry.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/core/molecular_data.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/main.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/plugins/__init__.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/plugins/plugin_manager_window.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/__init__.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/about_dialog.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/align_plane_dialog.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/alignment_dialog.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/analysis_window.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/angle_dialog.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/atom_item.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/base_picking_dialog.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/bond_length_dialog.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/calculation_worker.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/color_settings_dialog.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/compute_logic.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/constrained_optimization_dialog.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/custom_qt_interactor.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/dialog_3d_picking_mixin.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/dihedral_dialog.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/edit_3d_logic.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/export_logic.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/geometry_base_dialog.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/main_window.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/mirror_dialog.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/move_group_dialog.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/periodic_table_dialog.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/planarize_dialog.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/settings_tabs/__init__.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/settings_tabs/settings_2d_tab.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/settings_tabs/settings_3d_tabs.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/settings_tabs/settings_other_tab.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/settings_tabs/settings_tab_base.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/sip_isdeleted_safe.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/string_importers.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/template_preview_item.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/template_preview_view.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/utils/__init__.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/utils/default_settings.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/utils/sip_isdeleted_safe.py +0 -0
- {moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/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: 3.0.
|
|
3
|
+
Version: 3.0.3
|
|
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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: MoleditPy-linux
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.3
|
|
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
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/plugins/plugin_interface.py
RENAMED
|
@@ -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.
|
|
@@ -177,9 +159,7 @@ class PluginContext:
|
|
|
177
159
|
"""
|
|
178
160
|
Display a temporary message in the status bar of the main window.
|
|
179
161
|
"""
|
|
180
|
-
|
|
181
|
-
if mw and hasattr(mw, "ui_manager"):
|
|
182
|
-
mw.ui_manager.update_status_bar(message)
|
|
162
|
+
self._manager.show_status_message(message, timeout)
|
|
183
163
|
|
|
184
164
|
@property
|
|
185
165
|
def current_mol(self) -> Any:
|
|
@@ -187,7 +167,11 @@ class PluginContext:
|
|
|
187
167
|
Get or set the current molecule (RDKit Mol object). Shortcut for current_molecule.
|
|
188
168
|
"""
|
|
189
169
|
mw = self.get_main_window()
|
|
190
|
-
return
|
|
170
|
+
return (
|
|
171
|
+
mw.view_3d_manager.current_mol
|
|
172
|
+
if mw and hasattr(mw, "view_3d_manager")
|
|
173
|
+
else None
|
|
174
|
+
)
|
|
191
175
|
|
|
192
176
|
@current_mol.setter
|
|
193
177
|
def current_mol(self, mol: Any):
|
|
@@ -211,7 +195,11 @@ class PluginContext:
|
|
|
211
195
|
Returns the PyVista plotter from the MainWindow.
|
|
212
196
|
"""
|
|
213
197
|
mw = self.get_main_window()
|
|
214
|
-
return
|
|
198
|
+
return (
|
|
199
|
+
mw.view_3d_manager.plotter
|
|
200
|
+
if mw and hasattr(mw, "view_3d_manager")
|
|
201
|
+
else None
|
|
202
|
+
)
|
|
215
203
|
|
|
216
204
|
@property
|
|
217
205
|
def scene(self) -> Any:
|
|
@@ -236,7 +224,10 @@ class PluginContext:
|
|
|
236
224
|
mw.view_3d_manager.draw_molecule_3d(mol)
|
|
237
225
|
else:
|
|
238
226
|
# Also redraw/clear plotter if no molecule
|
|
239
|
-
if
|
|
227
|
+
if (
|
|
228
|
+
hasattr(mw.view_3d_manager, "plotter")
|
|
229
|
+
and mw.view_3d_manager.plotter
|
|
230
|
+
):
|
|
240
231
|
mw.view_3d_manager.plotter.render()
|
|
241
232
|
|
|
242
233
|
def reset_3d_camera(self) -> None:
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/plugins/plugin_manager.py
RENAMED
|
@@ -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.
|
|
@@ -752,7 +752,6 @@ class StateManager:
|
|
|
752
752
|
if self.host.view_3d_manager.current_mol:
|
|
753
753
|
# Set 3D coordinates
|
|
754
754
|
if self.host.view_3d_manager.current_mol.GetNumConformers() > 0:
|
|
755
|
-
conf = self.host.view_3d_manager.current_mol.GetConformer()
|
|
756
755
|
atoms_3d = structure_3d.get("atoms", [])
|
|
757
756
|
# Ensure numpy array size matches atoms in file
|
|
758
757
|
num_atoms_file = len(atoms_3d)
|
|
@@ -242,7 +242,6 @@ class BondItem(QGraphicsItem):
|
|
|
242
242
|
center = line.center()
|
|
243
243
|
|
|
244
244
|
# Logic similar to boundingRect but returning just the label box
|
|
245
|
-
font_size = 20
|
|
246
245
|
# ... (Simpler logic: just return a box around center)
|
|
247
246
|
# Standard size estimate
|
|
248
247
|
box_size = 30
|
|
@@ -265,7 +264,6 @@ class BondItem(QGraphicsItem):
|
|
|
265
264
|
return
|
|
266
265
|
|
|
267
266
|
# Default values
|
|
268
|
-
width_2d = 2.0
|
|
269
267
|
wedge_width_half = 6.0
|
|
270
268
|
num_dashes = 8
|
|
271
269
|
bond_color = QColor("#222222")
|
|
@@ -375,9 +373,6 @@ class BondItem(QGraphicsItem):
|
|
|
375
373
|
|
|
376
374
|
if is_in_ring and ring_center:
|
|
377
375
|
# Ring structure: 1 central line (single bond pos) + 1 short inner line
|
|
378
|
-
# Calculate direction from bond center to ring center
|
|
379
|
-
bond_center = line.center()
|
|
380
|
-
|
|
381
376
|
# Ring center direction in local coords
|
|
382
377
|
# ring_center may be QPointF (from old/legacy code) or tuple (from new decoupled core)
|
|
383
378
|
if isinstance(ring_center, (list, tuple)):
|
|
@@ -512,8 +507,6 @@ class BondItem(QGraphicsItem):
|
|
|
512
507
|
# Continue without crashing
|
|
513
508
|
|
|
514
509
|
def hoverEnterEvent(self, event: Any) -> None:
|
|
515
|
-
scene = self.scene()
|
|
516
|
-
mode = getattr(scene, "mode", "")
|
|
517
510
|
self.hovered = True
|
|
518
511
|
self.update()
|
|
519
512
|
if self.scene():
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/custom_interactor_style.py
RENAMED
|
@@ -182,11 +182,6 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
|
|
|
182
182
|
)
|
|
183
183
|
is_edit_active = mw.edit_3d_manager.is_3d_edit_mode or is_temp_mode
|
|
184
184
|
|
|
185
|
-
# Ctrl+Click for atom selection (3D edit)
|
|
186
|
-
is_ctrl_click = bool(
|
|
187
|
-
QApplication.keyboardModifiers() & Qt.KeyboardModifier.ControlModifier
|
|
188
|
-
)
|
|
189
|
-
|
|
190
185
|
# Handle measurement mode
|
|
191
186
|
if mw.edit_3d_manager.measurement_mode and mw.view_3d_manager.current_mol:
|
|
192
187
|
click_pos = self.GetInteractor().GetEventPosition()
|
|
@@ -419,7 +414,6 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
|
|
|
419
414
|
if self._is_dragging_atom and mw.dragged_atom_info is not None:
|
|
420
415
|
# Custom atom drag
|
|
421
416
|
self.is_dragging = True
|
|
422
|
-
atom_id = mw.dragged_atom_info["id"]
|
|
423
417
|
else:
|
|
424
418
|
# Delegate camera rotation to parent
|
|
425
419
|
super().OnMouseMove()
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/edit_actions_logic.py
RENAMED
|
@@ -22,7 +22,7 @@ from typing import Any, Dict, List, Optional
|
|
|
22
22
|
import numpy as np
|
|
23
23
|
|
|
24
24
|
try:
|
|
25
|
-
from .mol_geometry import
|
|
25
|
+
from .mol_geometry import identify_valence_problems
|
|
26
26
|
except ImportError:
|
|
27
27
|
from moleditpy_linux.core.mol_geometry import identify_valence_problems
|
|
28
28
|
|
|
@@ -207,7 +207,10 @@ class IOManager:
|
|
|
207
207
|
final_mol = _process(charge_val, use_rd_determine=True)
|
|
208
208
|
break
|
|
209
209
|
except (RuntimeError, ValueError, TypeError) as e:
|
|
210
|
-
if
|
|
210
|
+
if (
|
|
211
|
+
hasattr(self.host, "statusBar")
|
|
212
|
+
and self.host.statusBar()
|
|
213
|
+
):
|
|
211
214
|
self.host.statusBar().showMessage(
|
|
212
215
|
f"Chemistry failed for charge {charge_val}: {e}. Try a different charge or skip."
|
|
213
216
|
)
|
|
@@ -919,8 +922,8 @@ class IOManager:
|
|
|
919
922
|
charge = Chem.GetFormalCharge(
|
|
920
923
|
self.host.view_3d_manager.current_mol
|
|
921
924
|
)
|
|
922
|
-
except:
|
|
923
|
-
|
|
925
|
+
except Exception as e:
|
|
926
|
+
logging.warning("Could not compute formal charge: %s", e)
|
|
924
927
|
|
|
925
928
|
multiplicity = 1
|
|
926
929
|
try:
|
|
@@ -930,8 +933,8 @@ class IOManager:
|
|
|
930
933
|
)
|
|
931
934
|
+ 1
|
|
932
935
|
)
|
|
933
|
-
except:
|
|
934
|
-
|
|
936
|
+
except Exception as e:
|
|
937
|
+
logging.warning("Could not compute multiplicity: %s", e)
|
|
935
938
|
|
|
936
939
|
xyz_lines.append(
|
|
937
940
|
f"chrg = {charge} mult = {multiplicity} | Generated by MoleditPy Ver. {VERSION}"
|
|
@@ -1035,4 +1038,3 @@ def _set_mol_prop_safe(mol: Any, key: str, val: Any) -> None:
|
|
|
1035
1038
|
|
|
1036
1039
|
# Backward-compat aliases
|
|
1037
1040
|
IOManager = IOManager
|
|
1038
|
-
|
|
@@ -371,30 +371,28 @@ class MainInitManager:
|
|
|
371
371
|
|
|
372
372
|
modules_to_update.append(constants_mod)
|
|
373
373
|
|
|
374
|
-
for
|
|
375
|
-
|
|
374
|
+
for mod in modules_to_update:
|
|
375
|
+
mod.CPK_COLORS.clear()
|
|
376
376
|
for k, v in DEFAULT_CPK_COLORS.items():
|
|
377
|
-
|
|
378
|
-
QColor(v) if not isinstance(v, QColor) else v
|
|
379
|
-
)
|
|
377
|
+
mod.CPK_COLORS[k] = QColor(v) if not isinstance(v, QColor) else v
|
|
380
378
|
|
|
381
379
|
# Apply overrides from settings
|
|
382
380
|
for k, hexv in overrides.items():
|
|
383
381
|
if isinstance(hexv, str) and hexv:
|
|
384
|
-
|
|
382
|
+
mod.CPK_COLORS[k] = QColor(hexv)
|
|
385
383
|
|
|
386
384
|
# Rebuild the PV representation in-place too
|
|
387
|
-
if hasattr(
|
|
388
|
-
|
|
389
|
-
for k, c in
|
|
390
|
-
|
|
385
|
+
if hasattr(mod, "CPK_COLORS_PV"):
|
|
386
|
+
mod.CPK_COLORS_PV.clear()
|
|
387
|
+
for k, c in mod.CPK_COLORS.items():
|
|
388
|
+
mod.CPK_COLORS_PV[k] = [
|
|
391
389
|
c.redF(),
|
|
392
390
|
c.greenF(),
|
|
393
391
|
c.blueF(),
|
|
394
392
|
]
|
|
395
393
|
else: # [REPORT ERROR MISSING ATTRIBUTE]
|
|
396
394
|
logging.error(
|
|
397
|
-
"REPORT ERROR: Missing attribute 'CPK_COLORS_PV' on
|
|
395
|
+
"REPORT ERROR: Missing attribute 'CPK_COLORS_PV' on constants module"
|
|
398
396
|
)
|
|
399
397
|
except (AttributeError, RuntimeError, TypeError, ValueError) as e:
|
|
400
398
|
print(f"Failed to update CPK colors from settings: {e}")
|
|
@@ -1318,9 +1316,7 @@ class MainInitManager:
|
|
|
1318
1316
|
if key == "ball_and_stick":
|
|
1319
1317
|
action.setChecked(True)
|
|
1320
1318
|
action.triggered.connect(
|
|
1321
|
-
lambda checked=False, k=key: (
|
|
1322
|
-
self.host.view_3d_manager.set_3d_style(k)
|
|
1323
|
-
)
|
|
1319
|
+
lambda checked=False, k=key: (self.host.view_3d_manager.set_3d_style(k))
|
|
1324
1320
|
)
|
|
1325
1321
|
style_menu.addAction(action)
|
|
1326
1322
|
style_group.addAction(action)
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/molecular_scene_handler.py
RENAMED
|
@@ -733,7 +733,7 @@ class KeyboardMixin:
|
|
|
733
733
|
# Case 1: Cursor over atom/bond (one-shot placement)
|
|
734
734
|
if isinstance(item_at_cursor, (AtomItem, BondItem)):
|
|
735
735
|
# Set benzene template parameters
|
|
736
|
-
n
|
|
736
|
+
n = 6
|
|
737
737
|
points, bonds_info, existing_items = [], [], []
|
|
738
738
|
|
|
739
739
|
# Calculate placement like update_template_preview
|
|
@@ -903,7 +903,6 @@ class KeyboardMixin:
|
|
|
903
903
|
self.data.bonds[new_key] = bond_data
|
|
904
904
|
bond.atom1, bond.atom2 = bond.atom2, bond.atom1
|
|
905
905
|
bond.update_position()
|
|
906
|
-
was_reversed = True
|
|
907
906
|
else:
|
|
908
907
|
bond.order = 1
|
|
909
908
|
bond.stereo = 1
|
|
@@ -915,7 +914,6 @@ class KeyboardMixin:
|
|
|
915
914
|
self.data.bonds[new_key] = bond_data
|
|
916
915
|
bond.atom1, bond.atom2 = bond.atom2, bond.atom1
|
|
917
916
|
bond.update_position()
|
|
918
|
-
was_reversed = True
|
|
919
917
|
else:
|
|
920
918
|
bond.order = 1
|
|
921
919
|
bond.stereo = 2
|
|
@@ -32,10 +32,6 @@ except ImportError:
|
|
|
32
32
|
from moleditpy_linux.ui.bond_item import BondItem
|
|
33
33
|
from moleditpy_linux.ui.template_preview_item import TemplatePreviewItem
|
|
34
34
|
|
|
35
|
-
try:
|
|
36
|
-
from ..utils.constants import DEFAULT_BOND_LENGTH, SNAP_DISTANCE, SUM_TOLERANCE
|
|
37
|
-
except ImportError:
|
|
38
|
-
pass
|
|
39
35
|
|
|
40
36
|
try:
|
|
41
37
|
from PyQt6 import sip as _sip # type: ignore
|
|
@@ -27,7 +27,6 @@ from .settings_tabs.settings_3d_tabs import Settings3DSceneTab, SettingsModelTab
|
|
|
27
27
|
from .settings_tabs.settings_other_tab import SettingsOtherTab
|
|
28
28
|
|
|
29
29
|
try:
|
|
30
|
-
from ..utils.constants import CPK_COLORS
|
|
31
30
|
from ..utils.default_settings import DEFAULT_SETTINGS
|
|
32
31
|
except ImportError:
|
|
33
32
|
from moleditpy_linux.utils.default_settings import DEFAULT_SETTINGS
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
MoleditPy — A Python-based molecular editing software
|
|
6
|
+
|
|
7
|
+
Author: Hiromichi Yokoyama
|
|
8
|
+
License: GPL-3.0 license
|
|
9
|
+
Repo: https://github.com/HiroYokoyama/python_molecular_editor
|
|
10
|
+
DOI: 10.5281/zenodo.17268532
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
from PyQt6.QtWidgets import (
|
|
15
|
+
QCheckBox,
|
|
16
|
+
QHBoxLayout,
|
|
17
|
+
QLabel,
|
|
18
|
+
QLineEdit,
|
|
19
|
+
QMessageBox,
|
|
20
|
+
QPushButton,
|
|
21
|
+
QTabWidget,
|
|
22
|
+
QVBoxLayout,
|
|
23
|
+
QWidget,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
from .base_picking_dialog import BasePickingDialog
|
|
28
|
+
except ImportError:
|
|
29
|
+
from moleditpy_linux.ui.base_picking_dialog import BasePickingDialog
|
|
30
|
+
|
|
31
|
+
_TAB_ABSOLUTE = 0
|
|
32
|
+
_TAB_DELTA = 1
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class TranslationDialog(BasePickingDialog):
|
|
36
|
+
def __init__(self, mol, main_window, preselected_atoms=None, parent=None):
|
|
37
|
+
super().__init__(mol, main_window, parent)
|
|
38
|
+
self.selected_atoms = set()
|
|
39
|
+
|
|
40
|
+
if preselected_atoms:
|
|
41
|
+
self.selected_atoms.update(preselected_atoms)
|
|
42
|
+
|
|
43
|
+
self.init_ui()
|
|
44
|
+
|
|
45
|
+
if self.selected_atoms:
|
|
46
|
+
self.show_atom_labels()
|
|
47
|
+
self.update_display()
|
|
48
|
+
|
|
49
|
+
# ------------------------------------------------------------------
|
|
50
|
+
# UI construction
|
|
51
|
+
# ------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
def init_ui(self):
|
|
54
|
+
self.setWindowTitle("Translate Atoms")
|
|
55
|
+
self.setModal(False)
|
|
56
|
+
layout = QVBoxLayout(self)
|
|
57
|
+
|
|
58
|
+
self.tabs = QTabWidget()
|
|
59
|
+
self.tabs.addTab(self._build_absolute_tab(), "Absolute")
|
|
60
|
+
self.tabs.addTab(self._build_delta_tab(), "Delta")
|
|
61
|
+
self.tabs.currentChanged.connect(self._on_tab_changed)
|
|
62
|
+
layout.addWidget(self.tabs)
|
|
63
|
+
|
|
64
|
+
# Shared Close button row
|
|
65
|
+
close_row = QHBoxLayout()
|
|
66
|
+
close_row.addStretch()
|
|
67
|
+
close_btn = QPushButton("Close")
|
|
68
|
+
close_btn.clicked.connect(self.reject)
|
|
69
|
+
close_row.addWidget(close_btn)
|
|
70
|
+
layout.addLayout(close_row)
|
|
71
|
+
|
|
72
|
+
self.enable_picking()
|
|
73
|
+
|
|
74
|
+
def _build_absolute_tab(self):
|
|
75
|
+
widget = QWidget()
|
|
76
|
+
layout = QVBoxLayout(widget)
|
|
77
|
+
|
|
78
|
+
instr = QLabel(
|
|
79
|
+
"Click one atom to select it, then specify its target absolute coordinates (Å)."
|
|
80
|
+
)
|
|
81
|
+
instr.setWordWrap(True)
|
|
82
|
+
layout.addWidget(instr)
|
|
83
|
+
|
|
84
|
+
self.abs_selection_label = QLabel("No atom selected")
|
|
85
|
+
layout.addWidget(self.abs_selection_label)
|
|
86
|
+
|
|
87
|
+
coord_row = QHBoxLayout()
|
|
88
|
+
coord_row.addWidget(QLabel("X:"))
|
|
89
|
+
self.abs_x_input = QLineEdit("0.000")
|
|
90
|
+
coord_row.addWidget(self.abs_x_input)
|
|
91
|
+
coord_row.addWidget(QLabel("Y:"))
|
|
92
|
+
self.abs_y_input = QLineEdit("0.000")
|
|
93
|
+
coord_row.addWidget(self.abs_y_input)
|
|
94
|
+
coord_row.addWidget(QLabel("Z:"))
|
|
95
|
+
self.abs_z_input = QLineEdit("0.000")
|
|
96
|
+
coord_row.addWidget(self.abs_z_input)
|
|
97
|
+
layout.addLayout(coord_row)
|
|
98
|
+
|
|
99
|
+
self.move_mol_checkbox = QCheckBox("Move entire molecule")
|
|
100
|
+
self.move_mol_checkbox.setChecked(True)
|
|
101
|
+
self.move_mol_checkbox.stateChanged.connect(self._on_move_mol_toggled)
|
|
102
|
+
layout.addWidget(self.move_mol_checkbox)
|
|
103
|
+
|
|
104
|
+
btn_row = QHBoxLayout()
|
|
105
|
+
abs_clear_btn = QPushButton("Clear Selection")
|
|
106
|
+
abs_clear_btn.clicked.connect(self._abs_clear_selection)
|
|
107
|
+
btn_row.addWidget(abs_clear_btn)
|
|
108
|
+
origin_btn = QPushButton("Set to Origin")
|
|
109
|
+
origin_btn.setToolTip("Set target coordinates to the origin")
|
|
110
|
+
origin_btn.clicked.connect(self._set_origin)
|
|
111
|
+
btn_row.addWidget(origin_btn)
|
|
112
|
+
btn_row.addStretch()
|
|
113
|
+
self.abs_apply_btn = QPushButton("Move Molecule")
|
|
114
|
+
self.abs_apply_btn.clicked.connect(self.apply_absolute)
|
|
115
|
+
self.abs_apply_btn.setEnabled(False)
|
|
116
|
+
btn_row.addWidget(self.abs_apply_btn)
|
|
117
|
+
layout.addLayout(btn_row)
|
|
118
|
+
|
|
119
|
+
layout.addStretch()
|
|
120
|
+
return widget
|
|
121
|
+
|
|
122
|
+
def _build_delta_tab(self):
|
|
123
|
+
widget = QWidget()
|
|
124
|
+
layout = QVBoxLayout(widget)
|
|
125
|
+
|
|
126
|
+
instr = QLabel(
|
|
127
|
+
"Click atoms in the 3D view to select them, then specify the translation vector (Å)."
|
|
128
|
+
)
|
|
129
|
+
instr.setWordWrap(True)
|
|
130
|
+
layout.addWidget(instr)
|
|
131
|
+
|
|
132
|
+
self.delta_selection_label = QLabel("No atoms selected")
|
|
133
|
+
layout.addWidget(self.delta_selection_label)
|
|
134
|
+
|
|
135
|
+
vector_row = QHBoxLayout()
|
|
136
|
+
vector_row.addWidget(QLabel("dX:"))
|
|
137
|
+
self.dx_input = QLineEdit("0.0")
|
|
138
|
+
vector_row.addWidget(self.dx_input)
|
|
139
|
+
vector_row.addWidget(QLabel("dY:"))
|
|
140
|
+
self.dy_input = QLineEdit("0.0")
|
|
141
|
+
vector_row.addWidget(self.dy_input)
|
|
142
|
+
vector_row.addWidget(QLabel("dZ:"))
|
|
143
|
+
self.dz_input = QLineEdit("0.0")
|
|
144
|
+
vector_row.addWidget(self.dz_input)
|
|
145
|
+
layout.addLayout(vector_row)
|
|
146
|
+
|
|
147
|
+
btn_row = QHBoxLayout()
|
|
148
|
+
clear_btn = QPushButton("Clear Selection")
|
|
149
|
+
clear_btn.clicked.connect(self.clear_selection)
|
|
150
|
+
btn_row.addWidget(clear_btn)
|
|
151
|
+
|
|
152
|
+
select_all_btn = QPushButton("Select All Atoms")
|
|
153
|
+
select_all_btn.setToolTip("Select all atoms in the molecule")
|
|
154
|
+
select_all_btn.clicked.connect(self.select_all_atoms)
|
|
155
|
+
btn_row.addWidget(select_all_btn)
|
|
156
|
+
|
|
157
|
+
btn_row.addStretch()
|
|
158
|
+
self.apply_button = QPushButton("Apply Translation")
|
|
159
|
+
self.apply_button.clicked.connect(self.apply_translation)
|
|
160
|
+
self.apply_button.setEnabled(False)
|
|
161
|
+
btn_row.addWidget(self.apply_button)
|
|
162
|
+
layout.addLayout(btn_row)
|
|
163
|
+
|
|
164
|
+
layout.addStretch()
|
|
165
|
+
return widget
|
|
166
|
+
|
|
167
|
+
# ------------------------------------------------------------------
|
|
168
|
+
# Tab switching
|
|
169
|
+
# ------------------------------------------------------------------
|
|
170
|
+
|
|
171
|
+
def _on_tab_changed(self, index):
|
|
172
|
+
self.selected_atoms.clear()
|
|
173
|
+
self.clear_atom_labels()
|
|
174
|
+
self.update_display()
|
|
175
|
+
|
|
176
|
+
# ------------------------------------------------------------------
|
|
177
|
+
# Atom picking dispatch
|
|
178
|
+
# ------------------------------------------------------------------
|
|
179
|
+
|
|
180
|
+
def on_atom_picked(self, atom_idx):
|
|
181
|
+
if self.tabs.currentIndex() == _TAB_ABSOLUTE:
|
|
182
|
+
self._abs_on_atom_picked(atom_idx)
|
|
183
|
+
else:
|
|
184
|
+
self._delta_on_atom_picked(atom_idx)
|
|
185
|
+
|
|
186
|
+
def _abs_on_atom_picked(self, atom_idx):
|
|
187
|
+
# Enforce single selection: replace previous atom
|
|
188
|
+
self.selected_atoms = {atom_idx}
|
|
189
|
+
self._populate_abs_inputs_from_atom(atom_idx)
|
|
190
|
+
self.show_atom_labels()
|
|
191
|
+
self.update_display()
|
|
192
|
+
|
|
193
|
+
def _delta_on_atom_picked(self, atom_idx):
|
|
194
|
+
if atom_idx in self.selected_atoms:
|
|
195
|
+
self.selected_atoms.remove(atom_idx)
|
|
196
|
+
else:
|
|
197
|
+
self.selected_atoms.add(atom_idx)
|
|
198
|
+
self.show_atom_labels()
|
|
199
|
+
self.update_display()
|
|
200
|
+
|
|
201
|
+
# ------------------------------------------------------------------
|
|
202
|
+
# Absolute tab helpers
|
|
203
|
+
# ------------------------------------------------------------------
|
|
204
|
+
|
|
205
|
+
def _populate_abs_inputs_from_atom(self, atom_idx):
|
|
206
|
+
pos = self.main_window.view_3d_manager.current_mol.GetConformer().GetPositions()[atom_idx]
|
|
207
|
+
self.abs_x_input.setText(f"{pos[0]:.4f}")
|
|
208
|
+
self.abs_y_input.setText(f"{pos[1]:.4f}")
|
|
209
|
+
self.abs_z_input.setText(f"{pos[2]:.4f}")
|
|
210
|
+
|
|
211
|
+
def _abs_clear_selection(self):
|
|
212
|
+
self.selected_atoms.clear()
|
|
213
|
+
self.clear_atom_labels()
|
|
214
|
+
self.abs_x_input.setText("0.000")
|
|
215
|
+
self.abs_y_input.setText("0.000")
|
|
216
|
+
self.abs_z_input.setText("0.000")
|
|
217
|
+
self.update_display()
|
|
218
|
+
|
|
219
|
+
def _set_origin(self):
|
|
220
|
+
self.abs_x_input.setText("0.0000")
|
|
221
|
+
self.abs_y_input.setText("0.0000")
|
|
222
|
+
self.abs_z_input.setText("0.0000")
|
|
223
|
+
|
|
224
|
+
def _on_move_mol_toggled(self, state):
|
|
225
|
+
label = "Move Molecule" if self.move_mol_checkbox.isChecked() else "Move Atom"
|
|
226
|
+
self.abs_apply_btn.setText(label)
|
|
227
|
+
|
|
228
|
+
def apply_absolute(self):
|
|
229
|
+
self.mol = self.main_window.view_3d_manager.current_mol
|
|
230
|
+
if len(self.selected_atoms) != 1:
|
|
231
|
+
QMessageBox.warning(self, "Warning", "Please select exactly one atom.")
|
|
232
|
+
return
|
|
233
|
+
|
|
234
|
+
try:
|
|
235
|
+
tx = float(self.abs_x_input.text())
|
|
236
|
+
ty = float(self.abs_y_input.text())
|
|
237
|
+
tz = float(self.abs_z_input.text())
|
|
238
|
+
except ValueError:
|
|
239
|
+
QMessageBox.warning(self, "Warning", "Please enter valid numbers for X, Y, Z.")
|
|
240
|
+
return
|
|
241
|
+
|
|
242
|
+
atom_idx = next(iter(self.selected_atoms))
|
|
243
|
+
positions = self.mol.GetConformer().GetPositions()
|
|
244
|
+
current = positions[atom_idx]
|
|
245
|
+
delta = np.array([tx, ty, tz]) - current
|
|
246
|
+
|
|
247
|
+
if np.allclose(delta, 0):
|
|
248
|
+
return
|
|
249
|
+
|
|
250
|
+
if self.move_mol_checkbox.isChecked():
|
|
251
|
+
positions += delta
|
|
252
|
+
else:
|
|
253
|
+
positions[atom_idx] += delta
|
|
254
|
+
|
|
255
|
+
self._update_molecule_geometry(positions)
|
|
256
|
+
self._push_undo()
|
|
257
|
+
self.show_atom_labels()
|
|
258
|
+
|
|
259
|
+
# ------------------------------------------------------------------
|
|
260
|
+
# Delta tab methods (unchanged logic)
|
|
261
|
+
# ------------------------------------------------------------------
|
|
262
|
+
|
|
263
|
+
def clear_selection(self):
|
|
264
|
+
self.selected_atoms.clear()
|
|
265
|
+
self.clear_atom_labels()
|
|
266
|
+
self.update_display()
|
|
267
|
+
|
|
268
|
+
def select_all_atoms(self):
|
|
269
|
+
try:
|
|
270
|
+
if hasattr(self, "mol") and self.mol is not None:
|
|
271
|
+
self.selected_atoms = set(range(self.mol.GetNumAtoms()))
|
|
272
|
+
else:
|
|
273
|
+
self.selected_atoms = (
|
|
274
|
+
set(self.main_window.state_manager.data.atoms.keys())
|
|
275
|
+
if hasattr(self.main_window.state_manager, "data")
|
|
276
|
+
else set()
|
|
277
|
+
)
|
|
278
|
+
self.show_atom_labels()
|
|
279
|
+
self.update_display()
|
|
280
|
+
except (AttributeError, RuntimeError, TypeError, KeyError) as e:
|
|
281
|
+
QMessageBox.warning(self, "Warning", f"Failed to select all atoms: {e}")
|
|
282
|
+
|
|
283
|
+
def apply_translation(self):
|
|
284
|
+
self.mol = self.main_window.view_3d_manager.current_mol
|
|
285
|
+
if not self.selected_atoms:
|
|
286
|
+
QMessageBox.warning(self, "Warning", "Please select at least one atom.")
|
|
287
|
+
return
|
|
288
|
+
|
|
289
|
+
try:
|
|
290
|
+
dx = float(self.dx_input.text())
|
|
291
|
+
dy = float(self.dy_input.text())
|
|
292
|
+
dz = float(self.dz_input.text())
|
|
293
|
+
except ValueError:
|
|
294
|
+
QMessageBox.warning(self, "Warning", "Please enter valid numbers for dx, dy, dz.")
|
|
295
|
+
return
|
|
296
|
+
|
|
297
|
+
if dx == 0 and dy == 0 and dz == 0:
|
|
298
|
+
return
|
|
299
|
+
|
|
300
|
+
translation_vec = np.array([dx, dy, dz])
|
|
301
|
+
positions = self.mol.GetConformer().GetPositions()
|
|
302
|
+
for atom_idx in self.selected_atoms:
|
|
303
|
+
positions[atom_idx] += translation_vec
|
|
304
|
+
|
|
305
|
+
self._update_molecule_geometry(positions)
|
|
306
|
+
self._push_undo()
|
|
307
|
+
self.show_atom_labels()
|
|
308
|
+
|
|
309
|
+
# ------------------------------------------------------------------
|
|
310
|
+
# Shared display update
|
|
311
|
+
# ------------------------------------------------------------------
|
|
312
|
+
|
|
313
|
+
def update_display(self):
|
|
314
|
+
tab = self.tabs.currentIndex()
|
|
315
|
+
count = len(self.selected_atoms)
|
|
316
|
+
|
|
317
|
+
if tab == _TAB_ABSOLUTE:
|
|
318
|
+
if count == 0:
|
|
319
|
+
self.abs_selection_label.setText("Click one atom to select it")
|
|
320
|
+
self.abs_apply_btn.setEnabled(False)
|
|
321
|
+
else:
|
|
322
|
+
atom_idx = next(iter(self.selected_atoms))
|
|
323
|
+
sym = self.mol.GetAtomWithIdx(atom_idx).GetSymbol()
|
|
324
|
+
self.abs_selection_label.setText(f"Selected: atom {atom_idx} ({sym})")
|
|
325
|
+
self.abs_apply_btn.setEnabled(True)
|
|
326
|
+
else:
|
|
327
|
+
if count == 0:
|
|
328
|
+
self.delta_selection_label.setText("Click atoms to select (minimum 1 required)")
|
|
329
|
+
self.apply_button.setEnabled(False)
|
|
330
|
+
else:
|
|
331
|
+
self.delta_selection_label.setText(f"Selected {count} atom{'s' if count != 1 else ''}")
|
|
332
|
+
self.apply_button.setEnabled(True)
|
|
333
|
+
|
|
334
|
+
def show_atom_labels(self):
|
|
335
|
+
if self.selected_atoms:
|
|
336
|
+
sorted_atoms = sorted(self.selected_atoms)
|
|
337
|
+
pairs = [(idx, str(i + 1)) for i, idx in enumerate(sorted_atoms)]
|
|
338
|
+
self.show_atom_labels_for(pairs)
|
|
339
|
+
else:
|
|
340
|
+
self.clear_atom_labels()
|
|
@@ -319,7 +319,6 @@ class UIManager(QObject):
|
|
|
319
319
|
self.host.view_3d_manager.plotter.interactor.SetInteractorStyle(style)
|
|
320
320
|
self.host.view_3d_manager.plotter.interactor.Initialize()
|
|
321
321
|
|
|
322
|
-
|
|
323
322
|
def handle_drag_enter_event(self, event):
|
|
324
323
|
"""Internal handler for drag enter event (bypasses PyQt type checks in tests)."""
|
|
325
324
|
if not event.mimeData().hasUrls():
|
|
@@ -344,7 +343,6 @@ class UIManager(QObject):
|
|
|
344
343
|
|
|
345
344
|
event.ignore()
|
|
346
345
|
|
|
347
|
-
|
|
348
346
|
def handle_drop_event(self, event):
|
|
349
347
|
"""Internal handler for file drop event (bypasses PyQt type checks in tests)."""
|
|
350
348
|
urls = event.mimeData().urls()
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/user_template_dialog.py
RENAMED
|
@@ -508,7 +508,7 @@ class UserTemplateDialog(QDialog):
|
|
|
508
508
|
brush = QBrush(Qt.GlobalColor.white)
|
|
509
509
|
ellipse_x = pos.x() - ellipse_width / 2
|
|
510
510
|
ellipse_y = pos.y() - ellipse_height / 2
|
|
511
|
-
|
|
511
|
+
scene.addEllipse(
|
|
512
512
|
ellipse_x, ellipse_y, ellipse_width, ellipse_height, pen, brush
|
|
513
513
|
)
|
|
514
514
|
|
|
@@ -37,7 +37,7 @@ except (ImportError, AttributeError, TypeError):
|
|
|
37
37
|
|
|
38
38
|
try:
|
|
39
39
|
# package relative imports (preferred when running as `python -m moleditpy`)
|
|
40
|
-
from .constants import CPK_COLORS_PV,
|
|
40
|
+
from .constants import CPK_COLORS_PV, VDW_RADII, pt
|
|
41
41
|
from .template_preview_item import TemplatePreviewItem
|
|
42
42
|
except ImportError:
|
|
43
43
|
# Fallback to absolute imports for script-style execution
|
|
@@ -117,9 +117,6 @@ class ZoomableView(QGraphicsView):
|
|
|
117
117
|
# (positive for zoom-in, negative for zoom-out)
|
|
118
118
|
factor = 1.0 + event.value()
|
|
119
119
|
|
|
120
|
-
current_scale = self.transform().m11()
|
|
121
|
-
min_scale, max_scale = 0.05, 20.0
|
|
122
|
-
|
|
123
120
|
# Apply scaling if within limits
|
|
124
121
|
self.scale(factor, factor)
|
|
125
122
|
return True
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
|
|
4
|
-
"""
|
|
5
|
-
MoleditPy — A Python-based molecular editing software
|
|
6
|
-
|
|
7
|
-
Author: Hiromichi Yokoyama
|
|
8
|
-
License: GPL-3.0 license
|
|
9
|
-
Repo: https://github.com/HiroYokoyama/python_molecular_editor
|
|
10
|
-
DOI: 10.5281/zenodo.17268532
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
import numpy as np
|
|
14
|
-
from PyQt6.QtWidgets import (
|
|
15
|
-
QHBoxLayout,
|
|
16
|
-
QLabel,
|
|
17
|
-
QLineEdit,
|
|
18
|
-
QMessageBox,
|
|
19
|
-
QPushButton,
|
|
20
|
-
QVBoxLayout,
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
try:
|
|
24
|
-
from .base_picking_dialog import BasePickingDialog
|
|
25
|
-
except ImportError:
|
|
26
|
-
from moleditpy_linux.ui.base_picking_dialog import BasePickingDialog
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class TranslationDialog(BasePickingDialog):
|
|
30
|
-
def __init__(self, mol, main_window, preselected_atoms=None, parent=None):
|
|
31
|
-
super().__init__(mol, main_window, parent)
|
|
32
|
-
self.selected_atoms = set()
|
|
33
|
-
|
|
34
|
-
# Add preselected atoms
|
|
35
|
-
if preselected_atoms:
|
|
36
|
-
self.selected_atoms.update(preselected_atoms)
|
|
37
|
-
|
|
38
|
-
self.init_ui()
|
|
39
|
-
|
|
40
|
-
# Add labels to preselected atoms
|
|
41
|
-
if self.selected_atoms:
|
|
42
|
-
self.show_atom_labels()
|
|
43
|
-
self.update_display()
|
|
44
|
-
|
|
45
|
-
def init_ui(self):
|
|
46
|
-
self.setWindowTitle("Translate Atoms")
|
|
47
|
-
self.setModal(False)
|
|
48
|
-
layout = QVBoxLayout(self)
|
|
49
|
-
|
|
50
|
-
# Instructions
|
|
51
|
-
instruction_label = QLabel(
|
|
52
|
-
"Click atoms in the 3D view to select them for translation. Specify the translation vector in Å."
|
|
53
|
-
)
|
|
54
|
-
instruction_label.setWordWrap(True)
|
|
55
|
-
layout.addWidget(instruction_label)
|
|
56
|
-
|
|
57
|
-
# Selected atoms display
|
|
58
|
-
self.selection_label = QLabel("No atoms selected")
|
|
59
|
-
layout.addWidget(self.selection_label)
|
|
60
|
-
|
|
61
|
-
# Translation vector input
|
|
62
|
-
vector_layout = QHBoxLayout()
|
|
63
|
-
vector_layout.addWidget(QLabel("dX:"))
|
|
64
|
-
self.dx_input = QLineEdit("0.0")
|
|
65
|
-
vector_layout.addWidget(self.dx_input)
|
|
66
|
-
|
|
67
|
-
vector_layout.addWidget(QLabel("dY:"))
|
|
68
|
-
self.dy_input = QLineEdit("0.0")
|
|
69
|
-
vector_layout.addWidget(self.dy_input)
|
|
70
|
-
|
|
71
|
-
vector_layout.addWidget(QLabel("dZ:"))
|
|
72
|
-
self.dz_input = QLineEdit("0.0")
|
|
73
|
-
vector_layout.addWidget(self.dz_input)
|
|
74
|
-
|
|
75
|
-
layout.addLayout(vector_layout)
|
|
76
|
-
|
|
77
|
-
# Buttons
|
|
78
|
-
button_layout = QHBoxLayout()
|
|
79
|
-
self.clear_button = QPushButton("Clear Selection")
|
|
80
|
-
self.clear_button.clicked.connect(self.clear_selection)
|
|
81
|
-
button_layout.addWidget(self.clear_button)
|
|
82
|
-
|
|
83
|
-
# Select all atoms button
|
|
84
|
-
self.select_all_button = QPushButton("Select All Atoms")
|
|
85
|
-
self.select_all_button.setToolTip("Select all atoms in the molecule")
|
|
86
|
-
self.select_all_button.clicked.connect(self.select_all_atoms)
|
|
87
|
-
button_layout.addWidget(self.select_all_button)
|
|
88
|
-
|
|
89
|
-
button_layout.addStretch()
|
|
90
|
-
|
|
91
|
-
self.apply_button = QPushButton("Apply Translation")
|
|
92
|
-
self.apply_button.clicked.connect(self.apply_translation)
|
|
93
|
-
self.apply_button.setEnabled(False)
|
|
94
|
-
button_layout.addWidget(self.apply_button)
|
|
95
|
-
|
|
96
|
-
close_button = QPushButton("Close")
|
|
97
|
-
close_button.clicked.connect(self.reject)
|
|
98
|
-
button_layout.addWidget(close_button)
|
|
99
|
-
|
|
100
|
-
layout.addLayout(button_layout)
|
|
101
|
-
|
|
102
|
-
# Connect to main window's picker
|
|
103
|
-
self.picker_connection = None
|
|
104
|
-
self.enable_picking()
|
|
105
|
-
|
|
106
|
-
def on_atom_picked(self, atom_idx):
|
|
107
|
-
"""Handle the event when an atom is picked in the 3D view."""
|
|
108
|
-
if atom_idx in self.selected_atoms:
|
|
109
|
-
self.selected_atoms.remove(atom_idx)
|
|
110
|
-
else:
|
|
111
|
-
self.selected_atoms.add(atom_idx)
|
|
112
|
-
|
|
113
|
-
# Display labels on the atoms
|
|
114
|
-
self.show_atom_labels()
|
|
115
|
-
self.update_display()
|
|
116
|
-
|
|
117
|
-
def clear_selection(self):
|
|
118
|
-
"""Clear the current atom selection."""
|
|
119
|
-
self.selected_atoms.clear()
|
|
120
|
-
self.clear_atom_labels()
|
|
121
|
-
self.update_display()
|
|
122
|
-
|
|
123
|
-
def select_all_atoms(self):
|
|
124
|
-
"""Select all atoms in the current molecule."""
|
|
125
|
-
try:
|
|
126
|
-
if hasattr(self, "mol") and self.mol is not None:
|
|
127
|
-
n = self.mol.GetNumAtoms()
|
|
128
|
-
self.selected_atoms = set(range(n))
|
|
129
|
-
else:
|
|
130
|
-
self.selected_atoms = (
|
|
131
|
-
set(self.main_window.state_manager.data.atoms.keys())
|
|
132
|
-
if hasattr(self.main_window.state_manager, "data")
|
|
133
|
-
else set()
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
self.show_atom_labels()
|
|
137
|
-
self.update_display()
|
|
138
|
-
except (AttributeError, RuntimeError, TypeError, KeyError) as e:
|
|
139
|
-
QMessageBox.warning(self, "Warning", f"Failed to select all atoms: {e}")
|
|
140
|
-
|
|
141
|
-
def update_display(self):
|
|
142
|
-
"""Update the UI display with current selection info."""
|
|
143
|
-
count = len(self.selected_atoms)
|
|
144
|
-
if count == 0:
|
|
145
|
-
self.selection_label.setText("Click atoms to select (minimum 1 required)")
|
|
146
|
-
self.apply_button.setEnabled(False)
|
|
147
|
-
else:
|
|
148
|
-
self.selection_label.setText(f"Selected {count} atoms")
|
|
149
|
-
self.apply_button.setEnabled(True)
|
|
150
|
-
|
|
151
|
-
def show_atom_labels(self):
|
|
152
|
-
"""Show numeric labels for the selected atoms."""
|
|
153
|
-
if self.selected_atoms:
|
|
154
|
-
sorted_atoms = sorted(self.selected_atoms)
|
|
155
|
-
pairs = [(idx, str(i + 1)) for i, idx in enumerate(sorted_atoms)]
|
|
156
|
-
self.show_atom_labels_for(pairs)
|
|
157
|
-
else:
|
|
158
|
-
self.clear_atom_labels()
|
|
159
|
-
|
|
160
|
-
def apply_translation(self):
|
|
161
|
-
"""Apply the translation to selected atoms."""
|
|
162
|
-
if not self.selected_atoms:
|
|
163
|
-
QMessageBox.warning(self, "Warning", "Please select at least one atom.")
|
|
164
|
-
return
|
|
165
|
-
|
|
166
|
-
try:
|
|
167
|
-
dx = float(self.dx_input.text())
|
|
168
|
-
dy = float(self.dy_input.text())
|
|
169
|
-
dz = float(self.dz_input.text())
|
|
170
|
-
except ValueError:
|
|
171
|
-
QMessageBox.warning(
|
|
172
|
-
self, "Warning", "Please enter valid numbers for dx, dy, dz."
|
|
173
|
-
)
|
|
174
|
-
return
|
|
175
|
-
|
|
176
|
-
if dx == 0 and dy == 0 and dz == 0:
|
|
177
|
-
return
|
|
178
|
-
|
|
179
|
-
translation_vec = np.array([dx, dy, dz])
|
|
180
|
-
|
|
181
|
-
# Update positions
|
|
182
|
-
positions = self.mol.GetConformer().GetPositions()
|
|
183
|
-
for atom_idx in self.selected_atoms:
|
|
184
|
-
positions[atom_idx] += translation_vec
|
|
185
|
-
|
|
186
|
-
# Write updated positions back using inherited helper
|
|
187
|
-
self._update_molecule_geometry(positions)
|
|
188
|
-
|
|
189
|
-
# Push Undo state AFTER modification
|
|
190
|
-
self._push_undo()
|
|
191
|
-
|
|
192
|
-
# Update labels
|
|
193
|
-
self.show_atom_labels()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/MoleditPy_linux.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/MoleditPy_linux.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/plugins/plugin_manager_window.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/align_plane_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/base_picking_dialog.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/bond_length_dialog.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/calculation_worker.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/color_settings_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/custom_qt_interactor.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/dialog_3d_picking_mixin.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/geometry_base_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/periodic_table_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/settings_tabs/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/sip_isdeleted_safe.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/template_preview_item.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/template_preview_view.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/utils/default_settings.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.0.1 → moleditpy_linux-3.0.3}/src/moleditpy_linux/utils/sip_isdeleted_safe.py
RENAMED
|
File without changes
|
|
File without changes
|