MoleditPy-linux 3.0.2__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.2 → moleditpy_linux-3.0.3}/PKG-INFO +1 -1
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/pyproject.toml +1 -1
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/MoleditPy_linux.egg-info/PKG-INFO +1 -1
- moleditpy_linux-3.0.3/src/moleditpy_linux/ui/translation_dialog.py +340 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/utils/constants.py +1 -1
- moleditpy_linux-3.0.2/src/moleditpy_linux/ui/translation_dialog.py +0 -193
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/LICENSE +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/README.md +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/setup.cfg +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/MoleditPy_linux.egg-info/SOURCES.txt +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/MoleditPy_linux.egg-info/dependency_links.txt +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/MoleditPy_linux.egg-info/entry_points.txt +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/MoleditPy_linux.egg-info/requires.txt +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/MoleditPy_linux.egg-info/top_level.txt +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/__init__.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/__main__.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/assets/file_icon.ico +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/assets/icon.icns +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/assets/icon.ico +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/assets/icon.png +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/core/__init__.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/core/mol_geometry.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/core/molecular_data.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/main.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/plugins/__init__.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/plugins/plugin_interface.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/plugins/plugin_manager.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/plugins/plugin_manager_window.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/__init__.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/about_dialog.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/align_plane_dialog.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/alignment_dialog.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/analysis_window.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/angle_dialog.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/app_state.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/atom_item.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/base_picking_dialog.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/bond_item.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/bond_length_dialog.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/calculation_worker.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/color_settings_dialog.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/compute_logic.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/constrained_optimization_dialog.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/custom_interactor_style.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/custom_qt_interactor.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/dialog_3d_picking_mixin.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/dialog_logic.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/dihedral_dialog.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/edit_3d_logic.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/edit_actions_logic.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/export_logic.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/geometry_base_dialog.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/io_logic.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/main_window.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/main_window_init.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/mirror_dialog.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/molecular_scene_handler.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/molecule_scene.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/move_group_dialog.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/periodic_table_dialog.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/planarize_dialog.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/settings_dialog.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/settings_tabs/__init__.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/settings_tabs/settings_2d_tab.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/settings_tabs/settings_3d_tabs.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/settings_tabs/settings_other_tab.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/settings_tabs/settings_tab_base.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/sip_isdeleted_safe.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/string_importers.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/template_preview_item.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/template_preview_view.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/ui_manager.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/user_template_dialog.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/view_3d_logic.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/zoomable_view.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/utils/__init__.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/utils/default_settings.py +0 -0
- {moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/utils/sip_isdeleted_safe.py +0 -0
- {moleditpy_linux-3.0.2 → 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
|
|
@@ -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()
|
|
@@ -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.2 → moleditpy_linux-3.0.3}/src/MoleditPy_linux.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{moleditpy_linux-3.0.2 → 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.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/plugins/plugin_interface.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/plugins/plugin_manager.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.0.2 → 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.2 → 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
|
|
File without changes
|
{moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/base_picking_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/bond_length_dialog.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/calculation_worker.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.0.2 → 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.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/custom_interactor_style.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/custom_qt_interactor.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.0.2 → 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.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/edit_actions_logic.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.0.2 → 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
|
|
File without changes
|
{moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/molecular_scene_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/periodic_table_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.0.2 → 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.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/sip_isdeleted_safe.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/template_preview_item.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/template_preview_view.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/ui/user_template_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/utils/default_settings.py
RENAMED
|
File without changes
|
{moleditpy_linux-3.0.2 → moleditpy_linux-3.0.3}/src/moleditpy_linux/utils/sip_isdeleted_safe.py
RENAMED
|
File without changes
|
|
File without changes
|