MoleditPy-linux 3.6.3__tar.gz → 3.6.5__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.6.3 → moleditpy_linux-3.6.5}/PKG-INFO +1 -1
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/pyproject.toml +1 -1
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/MoleditPy_linux.egg-info/PKG-INFO +1 -1
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/align_plane_dialog.py +6 -13
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/alignment_dialog.py +42 -49
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/io_logic.py +1 -1
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/translation_dialog.py +95 -24
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/LICENSE +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/README.md +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/setup.cfg +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/MoleditPy_linux.egg-info/SOURCES.txt +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/MoleditPy_linux.egg-info/dependency_links.txt +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/MoleditPy_linux.egg-info/entry_points.txt +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/MoleditPy_linux.egg-info/requires.txt +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/MoleditPy_linux.egg-info/top_level.txt +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/__init__.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/__main__.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/assets/file_icon.ico +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/assets/icon.icns +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/assets/icon.ico +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/assets/icon.png +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/core/__init__.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/core/mol_geometry.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/core/molecular_data.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/main.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/plugins/__init__.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/plugins/plugin_interface.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/plugins/plugin_manager.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/plugins/plugin_manager_window.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/__init__.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/about_dialog.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/analysis_window.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/angle_dialog.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/app_state.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/atom_item.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/atom_picking.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/base_picking_dialog.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/bond_item.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/bond_length_dialog.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/calculation_worker.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/color_settings_dialog.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/compute_logic.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/constrained_optimization_dialog.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/custom_interactor_style.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/custom_qt_interactor.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/dialog_3d_picking_mixin.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/dialog_logic.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/dihedral_dialog.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/edit_3d_logic.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/edit_actions_logic.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/export_logic.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/geometry_base_dialog.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/main_window.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/main_window_init.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/mirror_dialog.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/molecular_scene_handler.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/molecule_scene.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/move_group_dialog.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/move_selected_atoms_dialog.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/periodic_table_dialog.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/planarize_dialog.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/settings_dialog.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/settings_tabs/__init__.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/settings_tabs/settings_2d_tab.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/settings_tabs/settings_3d_tabs.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/settings_tabs/settings_other_tab.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/settings_tabs/settings_tab_base.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/string_importers.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/template_preview_item.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/template_preview_view.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/ui_manager.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/user_template_dialog.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/view_3d_logic.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/zoomable_view.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/utils/__init__.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/utils/constants.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/utils/default_settings.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/utils/sip_isdeleted_safe.py +0 -0
- {moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/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.6.
|
|
3
|
+
Version: 3.6.5
|
|
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.6.
|
|
3
|
+
Version: 3.6.5
|
|
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.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/align_plane_dialog.py
RENAMED
|
@@ -31,6 +31,11 @@ try:
|
|
|
31
31
|
except ImportError:
|
|
32
32
|
from moleditpy_linux.ui.base_picking_dialog import BasePickingDialog, SelectionList
|
|
33
33
|
|
|
34
|
+
try:
|
|
35
|
+
from ..core.mol_geometry import rodrigues_rotate
|
|
36
|
+
except ImportError:
|
|
37
|
+
from moleditpy_linux.core.mol_geometry import rodrigues_rotate
|
|
38
|
+
|
|
34
39
|
if TYPE_CHECKING:
|
|
35
40
|
from .main_window import MainWindow
|
|
36
41
|
|
|
@@ -242,18 +247,6 @@ class AlignPlaneDialog(BasePickingDialog):
|
|
|
242
247
|
rotation_axis = np.cross(normal_vector, target_normal)
|
|
243
248
|
rotation_axis_norm = np.linalg.norm(rotation_axis)
|
|
244
249
|
|
|
245
|
-
# Rodrigues' rotation formula
|
|
246
|
-
def rodrigues_rotation(
|
|
247
|
-
v: np.ndarray, axis: np.ndarray, angle: float
|
|
248
|
-
) -> np.ndarray:
|
|
249
|
-
cos_a = np.cos(angle)
|
|
250
|
-
sin_a = np.sin(angle)
|
|
251
|
-
return ( # type: ignore[no-any-return]
|
|
252
|
-
v * cos_a
|
|
253
|
-
+ np.cross(axis, v) * sin_a
|
|
254
|
-
+ axis * np.dot(axis, v) * (1 - cos_a)
|
|
255
|
-
)
|
|
256
|
-
|
|
257
250
|
# Calculate new positions (rotated, centered back by default)
|
|
258
251
|
conf = self.mol.GetConformer()
|
|
259
252
|
new_positions = np.empty_like(positions)
|
|
@@ -266,7 +259,7 @@ class AlignPlaneDialog(BasePickingDialog):
|
|
|
266
259
|
cos_angle = np.dot(normal_vector, target_normal)
|
|
267
260
|
cos_angle = np.clip(cos_angle, -1.0, 1.0)
|
|
268
261
|
rotation_angle = np.arccos(cos_angle)
|
|
269
|
-
rotated_pos =
|
|
262
|
+
rotated_pos = rodrigues_rotate(
|
|
270
263
|
centered_pos,
|
|
271
264
|
rotation_axis_normalized,
|
|
272
265
|
rotation_angle,
|
|
@@ -38,6 +38,11 @@ try:
|
|
|
38
38
|
except ImportError:
|
|
39
39
|
from moleditpy_linux.ui.base_picking_dialog import SelectionList
|
|
40
40
|
|
|
41
|
+
try:
|
|
42
|
+
from ..core.mol_geometry import rodrigues_rotate
|
|
43
|
+
except ImportError:
|
|
44
|
+
from moleditpy_linux.core.mol_geometry import rodrigues_rotate
|
|
45
|
+
|
|
41
46
|
if TYPE_CHECKING:
|
|
42
47
|
from .main_window import MainWindow
|
|
43
48
|
|
|
@@ -192,21 +197,16 @@ class AlignmentDialog(Dialog3DPickingMixin, QDialog):
|
|
|
192
197
|
|
|
193
198
|
conf = self.mol.GetConformer()
|
|
194
199
|
|
|
195
|
-
# Get
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
translation = -pos1
|
|
201
|
-
for i in range(self.mol.GetNumAtoms()):
|
|
202
|
-
current_pos = np.array(conf.GetAtomPosition(i))
|
|
203
|
-
new_pos = current_pos + translation
|
|
204
|
-
conf.SetAtomPosition(i, new_pos.tolist())
|
|
200
|
+
# Get original atom positions
|
|
201
|
+
positions = np.array(
|
|
202
|
+
[list(conf.GetAtomPosition(i)) for i in range(self.mol.GetNumAtoms())]
|
|
203
|
+
)
|
|
204
|
+
centroid = np.mean(positions, axis=0)
|
|
205
205
|
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
pos1 = positions[atom1_idx]
|
|
207
|
+
pos2 = positions[atom2_idx]
|
|
208
208
|
|
|
209
|
-
# Calculate rotation to align atom2 relative to the chosen axis
|
|
209
|
+
# Calculate rotation to align atom1 -> atom2 relative to the chosen axis
|
|
210
210
|
axis_vectors = {
|
|
211
211
|
"x": np.array([1.0, 0.0, 0.0]),
|
|
212
212
|
"y": np.array([0.0, 1.0, 0.0]),
|
|
@@ -214,10 +214,13 @@ class AlignmentDialog(Dialog3DPickingMixin, QDialog):
|
|
|
214
214
|
}
|
|
215
215
|
target_axis = axis_vectors[self.axis]
|
|
216
216
|
|
|
217
|
-
# Direction vector from
|
|
218
|
-
current_vector =
|
|
217
|
+
# Direction vector from atom1 to atom2
|
|
218
|
+
current_vector = pos2 - pos1
|
|
219
219
|
current_length = np.linalg.norm(current_vector)
|
|
220
220
|
|
|
221
|
+
# Keep track of rotated positions (initially original positions)
|
|
222
|
+
new_positions = np.copy(positions)
|
|
223
|
+
|
|
221
224
|
if current_length > 1e-10: # If not a zero vector
|
|
222
225
|
current_vector_normalized = current_vector / current_length
|
|
223
226
|
|
|
@@ -231,45 +234,35 @@ class AlignmentDialog(Dialog3DPickingMixin, QDialog):
|
|
|
231
234
|
cos_angle = np.clip(cos_angle, -1.0, 1.0)
|
|
232
235
|
rotation_angle = np.arccos(cos_angle)
|
|
233
236
|
|
|
234
|
-
#
|
|
235
|
-
def rodrigues_rotation(
|
|
236
|
-
v: np.ndarray, k: np.ndarray, theta: float
|
|
237
|
-
) -> np.ndarray:
|
|
238
|
-
cos_theta = np.cos(theta)
|
|
239
|
-
sin_theta = np.sin(theta)
|
|
240
|
-
return ( # type: ignore[no-any-return]
|
|
241
|
-
v * cos_theta
|
|
242
|
-
+ np.cross(k, v) * sin_theta
|
|
243
|
-
+ k * np.dot(k, v) * (1 - cos_theta)
|
|
244
|
-
)
|
|
245
|
-
|
|
246
|
-
# Apply rotation to all atoms
|
|
237
|
+
# Apply rotation to all atoms about the molecule's centroid
|
|
247
238
|
for i in range(self.mol.GetNumAtoms()):
|
|
248
|
-
|
|
249
|
-
rotated_pos =
|
|
250
|
-
|
|
251
|
-
)
|
|
252
|
-
conf.SetAtomPosition(
|
|
253
|
-
i,
|
|
254
|
-
Geometry.Point3D(
|
|
255
|
-
float(rotated_pos[0]),
|
|
256
|
-
float(rotated_pos[1]),
|
|
257
|
-
float(rotated_pos[2]),
|
|
258
|
-
),
|
|
239
|
+
rel_pos = positions[i] - centroid
|
|
240
|
+
rotated_pos = rodrigues_rotate(
|
|
241
|
+
rel_pos, rotation_axis, rotation_angle
|
|
259
242
|
)
|
|
243
|
+
new_positions[i] = rotated_pos + centroid
|
|
260
244
|
|
|
261
|
-
# If move_to_origin is
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
245
|
+
# If move_to_origin is True, translate entire molecule so atom1 ends up at origin
|
|
246
|
+
if (
|
|
247
|
+
hasattr(self, "move_to_origin_checkbox")
|
|
248
|
+
and self.move_to_origin_checkbox.isChecked()
|
|
249
|
+
):
|
|
250
|
+
new_pos1 = new_positions[atom1_idx]
|
|
251
|
+
new_positions = new_positions - new_pos1
|
|
252
|
+
|
|
253
|
+
# Update conformer positions
|
|
254
|
+
for i in range(self.mol.GetNumAtoms()):
|
|
255
|
+
conf.SetAtomPosition(
|
|
256
|
+
i,
|
|
257
|
+
Geometry.Point3D(
|
|
258
|
+
float(new_positions[i][0]),
|
|
259
|
+
float(new_positions[i][1]),
|
|
260
|
+
float(new_positions[i][2]),
|
|
261
|
+
),
|
|
262
|
+
)
|
|
268
263
|
|
|
269
264
|
# Update 3D positions
|
|
270
|
-
self.main_window.view_3d_manager.atom_positions_3d =
|
|
271
|
-
[list(conf.GetAtomPosition(i)) for i in range(self.mol.GetNumAtoms())]
|
|
272
|
-
)
|
|
265
|
+
self.main_window.view_3d_manager.atom_positions_3d = new_positions
|
|
273
266
|
|
|
274
267
|
# Update 3D visualization
|
|
275
268
|
self.main_window.view_3d_manager.draw_molecule_3d(self.mol)
|
|
@@ -942,7 +942,7 @@ class IOManager:
|
|
|
942
942
|
symbol = self.host.view_3d_manager.current_mol.GetAtomWithIdx(
|
|
943
943
|
i
|
|
944
944
|
).GetSymbol()
|
|
945
|
-
xyz_lines.append(f"{symbol}
|
|
945
|
+
xyz_lines.append(f" {symbol:<11}{pos.x:11.8f} {pos.y:11.8f} {pos.z:11.8f}")
|
|
946
946
|
|
|
947
947
|
with open(file_path, "w", encoding="utf-8") as f:
|
|
948
948
|
f.write("\n".join(xyz_lines) + "\n")
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/translation_dialog.py
RENAMED
|
@@ -10,6 +10,7 @@ Repo: https://github.com/HiroYokoyama/python_molecular_editor
|
|
|
10
10
|
DOI: 10.5281/zenodo.17268532
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
+
import logging
|
|
13
14
|
from typing import Any
|
|
14
15
|
|
|
15
16
|
import numpy as np
|
|
@@ -35,6 +36,8 @@ _TAB_DELTA = 1
|
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
class TranslationDialog(BasePickingDialog):
|
|
39
|
+
"""Dialog for translating selected atoms either absolutely or relatively."""
|
|
40
|
+
|
|
38
41
|
def __init__(
|
|
39
42
|
self,
|
|
40
43
|
mol: Any,
|
|
@@ -45,6 +48,19 @@ class TranslationDialog(BasePickingDialog):
|
|
|
45
48
|
super().__init__(mol, main_window, parent)
|
|
46
49
|
self.selected_atoms = set()
|
|
47
50
|
|
|
51
|
+
# Predefine widgets to satisfy pylint W0201
|
|
52
|
+
self.abs_selection_label = None
|
|
53
|
+
self.abs_x_input = None
|
|
54
|
+
self.abs_y_input = None
|
|
55
|
+
self.abs_z_input = None
|
|
56
|
+
self.move_mol_checkbox = None
|
|
57
|
+
self.abs_apply_btn = None
|
|
58
|
+
self.delta_selection_label = None
|
|
59
|
+
self.dx_input = None
|
|
60
|
+
self.dy_input = None
|
|
61
|
+
self.dz_input = None
|
|
62
|
+
self.apply_button = None
|
|
63
|
+
|
|
48
64
|
if preselected_atoms:
|
|
49
65
|
self.selected_atoms.update(preselected_atoms)
|
|
50
66
|
|
|
@@ -56,7 +72,7 @@ class TranslationDialog(BasePickingDialog):
|
|
|
56
72
|
self.tabs.blockSignals(True)
|
|
57
73
|
if len(self.selected_atoms) == 1:
|
|
58
74
|
self.tabs.setCurrentIndex(_TAB_ABSOLUTE)
|
|
59
|
-
self.
|
|
75
|
+
self._populate_abs_inputs_from_centroid()
|
|
60
76
|
else:
|
|
61
77
|
self.tabs.setCurrentIndex(_TAB_DELTA)
|
|
62
78
|
self.tabs.blockSignals(False)
|
|
@@ -68,6 +84,7 @@ class TranslationDialog(BasePickingDialog):
|
|
|
68
84
|
# ------------------------------------------------------------------
|
|
69
85
|
|
|
70
86
|
def init_ui(self) -> None:
|
|
87
|
+
"""Initialize and lay out all dialog widgets."""
|
|
71
88
|
self.setWindowTitle("Translate Atoms")
|
|
72
89
|
self.setModal(False)
|
|
73
90
|
layout = QVBoxLayout(self)
|
|
@@ -89,16 +106,18 @@ class TranslationDialog(BasePickingDialog):
|
|
|
89
106
|
self.enable_picking()
|
|
90
107
|
|
|
91
108
|
def _build_absolute_tab(self) -> QWidget:
|
|
109
|
+
"""Build the Absolute tab widget."""
|
|
92
110
|
widget = QWidget()
|
|
93
111
|
layout = QVBoxLayout(widget)
|
|
94
112
|
|
|
95
113
|
instr = QLabel(
|
|
96
|
-
"Click
|
|
114
|
+
"Click atoms to select them. The centroid of the selection "
|
|
115
|
+
"will be translated to the target absolute coordinates (Å)."
|
|
97
116
|
)
|
|
98
117
|
instr.setWordWrap(True)
|
|
99
118
|
layout.addWidget(instr)
|
|
100
119
|
|
|
101
|
-
self.abs_selection_label = QLabel("No
|
|
120
|
+
self.abs_selection_label = QLabel("No atoms selected")
|
|
102
121
|
layout.addWidget(self.abs_selection_label)
|
|
103
122
|
|
|
104
123
|
coord_row = QHBoxLayout()
|
|
@@ -122,10 +141,17 @@ class TranslationDialog(BasePickingDialog):
|
|
|
122
141
|
abs_clear_btn = QPushButton("Clear Selection")
|
|
123
142
|
abs_clear_btn.clicked.connect(self._abs_clear_selection)
|
|
124
143
|
btn_row.addWidget(abs_clear_btn)
|
|
144
|
+
|
|
145
|
+
abs_all_btn = QPushButton("Select All Atoms")
|
|
146
|
+
abs_all_btn.setToolTip("Select all atoms in the molecule")
|
|
147
|
+
abs_all_btn.clicked.connect(self._abs_select_all)
|
|
148
|
+
btn_row.addWidget(abs_all_btn)
|
|
149
|
+
|
|
125
150
|
origin_btn = QPushButton("Set to Origin")
|
|
126
151
|
origin_btn.setToolTip("Set target coordinates to the origin")
|
|
127
152
|
origin_btn.clicked.connect(self._set_origin)
|
|
128
153
|
btn_row.addWidget(origin_btn)
|
|
154
|
+
|
|
129
155
|
btn_row.addStretch()
|
|
130
156
|
self.abs_apply_btn = QPushButton("Move Molecule")
|
|
131
157
|
self.abs_apply_btn.clicked.connect(self.apply_absolute)
|
|
@@ -185,7 +211,8 @@ class TranslationDialog(BasePickingDialog):
|
|
|
185
211
|
# Tab switching
|
|
186
212
|
# ------------------------------------------------------------------
|
|
187
213
|
|
|
188
|
-
def _on_tab_changed(self,
|
|
214
|
+
def _on_tab_changed(self, _index: int) -> None:
|
|
215
|
+
"""Handle tab switching event to reset active selections."""
|
|
189
216
|
if hasattr(self, "_is_initializing") and self._is_initializing:
|
|
190
217
|
return
|
|
191
218
|
self.selected_atoms.clear()
|
|
@@ -203,9 +230,12 @@ class TranslationDialog(BasePickingDialog):
|
|
|
203
230
|
self._delta_on_atom_picked(atom_idx)
|
|
204
231
|
|
|
205
232
|
def _abs_on_atom_picked(self, atom_idx: int) -> None:
|
|
206
|
-
|
|
207
|
-
self.selected_atoms
|
|
208
|
-
|
|
233
|
+
"""Toggle atom in/out of the absolute-tab selection, then refresh centroid."""
|
|
234
|
+
if atom_idx in self.selected_atoms:
|
|
235
|
+
self.selected_atoms.discard(atom_idx)
|
|
236
|
+
else:
|
|
237
|
+
self.selected_atoms.add(atom_idx)
|
|
238
|
+
self._populate_abs_inputs_from_centroid()
|
|
209
239
|
self.update_display()
|
|
210
240
|
self.show_atom_labels()
|
|
211
241
|
|
|
@@ -221,19 +251,24 @@ class TranslationDialog(BasePickingDialog):
|
|
|
221
251
|
# Absolute tab helpers
|
|
222
252
|
# ------------------------------------------------------------------
|
|
223
253
|
|
|
224
|
-
def
|
|
254
|
+
def _populate_abs_inputs_from_centroid(self) -> None:
|
|
255
|
+
"""Populate X/Y/Z inputs with the centroid of all currently selected atoms."""
|
|
256
|
+
if not self.selected_atoms:
|
|
257
|
+
return
|
|
225
258
|
mol = self.main_window.view_3d_manager.current_mol
|
|
226
259
|
if mol is None:
|
|
227
260
|
return
|
|
228
261
|
conf = mol.GetConformer()
|
|
229
262
|
if conf is None:
|
|
230
263
|
return
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
self.
|
|
234
|
-
self.
|
|
264
|
+
positions = conf.GetPositions()
|
|
265
|
+
centroid = np.mean([positions[i] for i in self.selected_atoms], axis=0)
|
|
266
|
+
self.abs_x_input.setText(f"{centroid[0]:.4f}")
|
|
267
|
+
self.abs_y_input.setText(f"{centroid[1]:.4f}")
|
|
268
|
+
self.abs_z_input.setText(f"{centroid[2]:.4f}")
|
|
235
269
|
|
|
236
270
|
def _abs_clear_selection(self) -> None:
|
|
271
|
+
"""Clear the absolute-tab selection and reset coordinate inputs."""
|
|
237
272
|
self.selected_atoms.clear()
|
|
238
273
|
self.clear_atom_labels()
|
|
239
274
|
self.abs_x_input.setText("0.000")
|
|
@@ -241,19 +276,35 @@ class TranslationDialog(BasePickingDialog):
|
|
|
241
276
|
self.abs_z_input.setText("0.000")
|
|
242
277
|
self.update_display()
|
|
243
278
|
|
|
279
|
+
def _abs_select_all(self) -> None:
|
|
280
|
+
"""Select all atoms in the molecule for the absolute tab."""
|
|
281
|
+
try:
|
|
282
|
+
mol = self.main_window.view_3d_manager.current_mol
|
|
283
|
+
if mol is not None:
|
|
284
|
+
self.selected_atoms = set(range(mol.GetNumAtoms()))
|
|
285
|
+
self._populate_abs_inputs_from_centroid()
|
|
286
|
+
self.show_atom_labels()
|
|
287
|
+
self.update_display()
|
|
288
|
+
except (AttributeError, RuntimeError, TypeError) as exc:
|
|
289
|
+
logging.exception("Failed to select all atoms: %s", exc)
|
|
290
|
+
|
|
244
291
|
def _set_origin(self) -> None:
|
|
245
292
|
self.abs_x_input.setText("0.0000")
|
|
246
293
|
self.abs_y_input.setText("0.0000")
|
|
247
294
|
self.abs_z_input.setText("0.0000")
|
|
248
295
|
|
|
249
|
-
def _on_move_mol_toggled(self,
|
|
250
|
-
|
|
296
|
+
def _on_move_mol_toggled(self, _state: int) -> None:
|
|
297
|
+
"""Update Apply button label to reflect move-molecule vs move-selected mode."""
|
|
298
|
+
label = (
|
|
299
|
+
"Move Molecule" if self.move_mol_checkbox.isChecked() else "Move Selected"
|
|
300
|
+
)
|
|
251
301
|
self.abs_apply_btn.setText(label)
|
|
252
302
|
|
|
253
303
|
def apply_absolute(self) -> None:
|
|
304
|
+
"""Translate selected atoms so their centroid reaches the target coordinates."""
|
|
254
305
|
self.mol = self.main_window.view_3d_manager.current_mol
|
|
255
|
-
if
|
|
256
|
-
QMessageBox.warning(self, "Warning", "Please select
|
|
306
|
+
if not self.selected_atoms:
|
|
307
|
+
QMessageBox.warning(self, "Warning", "Please select at least one atom.")
|
|
257
308
|
return
|
|
258
309
|
|
|
259
310
|
try:
|
|
@@ -266,10 +317,9 @@ class TranslationDialog(BasePickingDialog):
|
|
|
266
317
|
)
|
|
267
318
|
return
|
|
268
319
|
|
|
269
|
-
atom_idx = next(iter(self.selected_atoms))
|
|
270
320
|
positions = self.mol.GetConformer().GetPositions()
|
|
271
|
-
|
|
272
|
-
delta = np.array([tx, ty, tz]) -
|
|
321
|
+
centroid = np.mean([positions[i] for i in self.selected_atoms], axis=0)
|
|
322
|
+
delta = np.array([tx, ty, tz]) - centroid
|
|
273
323
|
|
|
274
324
|
if np.allclose(delta, 0):
|
|
275
325
|
return
|
|
@@ -277,10 +327,12 @@ class TranslationDialog(BasePickingDialog):
|
|
|
277
327
|
if self.move_mol_checkbox.isChecked():
|
|
278
328
|
positions += delta
|
|
279
329
|
else:
|
|
280
|
-
|
|
330
|
+
for atom_idx in self.selected_atoms:
|
|
331
|
+
positions[atom_idx] += delta
|
|
281
332
|
|
|
282
333
|
self._update_molecule_geometry(positions)
|
|
283
334
|
self._push_undo()
|
|
335
|
+
self._populate_abs_inputs_from_centroid()
|
|
284
336
|
self.show_atom_labels()
|
|
285
337
|
|
|
286
338
|
# ------------------------------------------------------------------
|
|
@@ -288,11 +340,13 @@ class TranslationDialog(BasePickingDialog):
|
|
|
288
340
|
# ------------------------------------------------------------------
|
|
289
341
|
|
|
290
342
|
def clear_selection(self) -> None:
|
|
343
|
+
"""Clear active atom selection for the relative tab."""
|
|
291
344
|
self.selected_atoms.clear()
|
|
292
345
|
self.clear_atom_labels()
|
|
293
346
|
self.update_display()
|
|
294
347
|
|
|
295
348
|
def select_all_atoms(self) -> None:
|
|
349
|
+
"""Select all atoms in the molecule for relative translation."""
|
|
296
350
|
try:
|
|
297
351
|
if hasattr(self, "mol") and self.mol is not None:
|
|
298
352
|
self.selected_atoms = set(range(self.mol.GetNumAtoms()))
|
|
@@ -308,6 +362,7 @@ class TranslationDialog(BasePickingDialog):
|
|
|
308
362
|
QMessageBox.warning(self, "Warning", f"Failed to select all atoms: {e}")
|
|
309
363
|
|
|
310
364
|
def apply_translation(self) -> None:
|
|
365
|
+
"""Apply relative translation vector to selected atoms."""
|
|
311
366
|
self.mol = self.main_window.view_3d_manager.current_mol
|
|
312
367
|
if not self.selected_atoms:
|
|
313
368
|
QMessageBox.warning(self, "Warning", "Please select at least one atom.")
|
|
@@ -340,17 +395,32 @@ class TranslationDialog(BasePickingDialog):
|
|
|
340
395
|
# ------------------------------------------------------------------
|
|
341
396
|
|
|
342
397
|
def update_display(self) -> None:
|
|
398
|
+
"""Update label descriptions and button enablement states."""
|
|
343
399
|
tab = self.tabs.currentIndex()
|
|
344
400
|
count = len(self.selected_atoms)
|
|
345
401
|
|
|
346
402
|
if tab == _TAB_ABSOLUTE:
|
|
347
403
|
if count == 0:
|
|
348
|
-
self.abs_selection_label.setText("Click
|
|
404
|
+
self.abs_selection_label.setText("Click atoms to select them")
|
|
349
405
|
self.abs_apply_btn.setEnabled(False)
|
|
350
406
|
else:
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
407
|
+
mol = self.main_window.view_3d_manager.current_mol
|
|
408
|
+
if mol is not None and self.selected_atoms:
|
|
409
|
+
positions = mol.GetConformer().GetPositions()
|
|
410
|
+
centroid = np.mean(
|
|
411
|
+
[positions[i] for i in self.selected_atoms], axis=0
|
|
412
|
+
)
|
|
413
|
+
centroid_str = (
|
|
414
|
+
f"({centroid[0]:.3f}, {centroid[1]:.3f}, {centroid[2]:.3f})"
|
|
415
|
+
)
|
|
416
|
+
self.abs_selection_label.setText(
|
|
417
|
+
f"{count} atom{'s' if count != 1 else ''} selected "
|
|
418
|
+
f"— centroid: {centroid_str}"
|
|
419
|
+
)
|
|
420
|
+
else:
|
|
421
|
+
self.abs_selection_label.setText(
|
|
422
|
+
f"{count} atom{'s' if count != 1 else ''} selected"
|
|
423
|
+
)
|
|
354
424
|
self.abs_apply_btn.setEnabled(True)
|
|
355
425
|
else:
|
|
356
426
|
if count == 0:
|
|
@@ -365,6 +435,7 @@ class TranslationDialog(BasePickingDialog):
|
|
|
365
435
|
self.apply_button.setEnabled(True)
|
|
366
436
|
|
|
367
437
|
def show_atom_labels(self) -> None:
|
|
438
|
+
"""Redraw selection numeric tags in the active 3D viewport."""
|
|
368
439
|
if self.selected_atoms:
|
|
369
440
|
sorted_atoms = sorted(self.selected_atoms)
|
|
370
441
|
pairs = [(idx, str(i + 1)) for i, idx in enumerate(sorted_atoms)]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/MoleditPy_linux.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/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.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/plugins/plugin_interface.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/plugins/plugin_manager.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/plugins/plugin_manager_window.py
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
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/base_picking_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/bond_length_dialog.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/calculation_worker.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/color_settings_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/custom_interactor_style.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/custom_qt_interactor.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/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.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/edit_actions_logic.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/geometry_base_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/molecular_scene_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/move_selected_atoms_dialog.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/periodic_table_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/settings_tabs/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/template_preview_item.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/template_preview_view.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/ui/user_template_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/utils/default_settings.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.6.3 → moleditpy_linux-3.6.5}/src/moleditpy_linux/utils/sip_isdeleted_safe.py
RENAMED
|
File without changes
|
|
File without changes
|