MoleditPy-linux 2.2.4__py3-none-any.whl → 2.2.6__py3-none-any.whl
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/modules/align_plane_dialog.py +0 -1
- moleditpy_linux/modules/alignment_dialog.py +0 -1
- moleditpy_linux/modules/angle_dialog.py +1 -1
- moleditpy_linux/modules/assets/file_icon.ico +0 -0
- moleditpy_linux/modules/bond_length_dialog.py +1 -1
- moleditpy_linux/modules/constants.py +1 -1
- moleditpy_linux/modules/constrained_optimization_dialog.py +0 -1
- moleditpy_linux/modules/custom_interactor_style.py +56 -56
- moleditpy_linux/modules/custom_qt_interactor.py +43 -0
- moleditpy_linux/modules/dialog3_d_picking_mixin.py +42 -9
- moleditpy_linux/modules/dihedral_dialog.py +1 -1
- moleditpy_linux/modules/main_window_app_state.py +13 -6
- moleditpy_linux/modules/main_window_dialog_manager.py +9 -9
- moleditpy_linux/modules/main_window_edit_actions.py +4 -1
- moleditpy_linux/modules/main_window_main_init.py +11 -0
- moleditpy_linux/modules/main_window_view_3d.py +9 -1
- moleditpy_linux/modules/move_group_dialog.py +7 -5
- moleditpy_linux/modules/planarize_dialog.py +0 -1
- moleditpy_linux/modules/plugin_interface.py +18 -0
- moleditpy_linux/modules/translation_dialog.py +0 -1
- {moleditpy_linux-2.2.4.dist-info → moleditpy_linux-2.2.6.dist-info}/METADATA +24 -6
- {moleditpy_linux-2.2.4.dist-info → moleditpy_linux-2.2.6.dist-info}/RECORD +26 -25
- {moleditpy_linux-2.2.4.dist-info → moleditpy_linux-2.2.6.dist-info}/WHEEL +0 -0
- {moleditpy_linux-2.2.4.dist-info → moleditpy_linux-2.2.6.dist-info}/entry_points.txt +0 -0
- {moleditpy_linux-2.2.4.dist-info → moleditpy_linux-2.2.6.dist-info}/licenses/LICENSE +0 -0
- {moleditpy_linux-2.2.4.dist-info → moleditpy_linux-2.2.6.dist-info}/top_level.txt +0 -0
|
@@ -45,7 +45,6 @@ class AlignPlaneDialog(Dialog3DPickingMixin, QDialog):
|
|
|
45
45
|
plane_names = {'xy': 'XY', 'xz': 'XZ', 'yz': 'YZ'}
|
|
46
46
|
self.setWindowTitle(f"Align to {plane_names[self.plane]} Plane")
|
|
47
47
|
self.setModal(False) # モードレスにしてクリックを阻害しない
|
|
48
|
-
self.setWindowFlags(Qt.WindowType.Window | Qt.WindowType.WindowStaysOnTopHint) # 常に前面表示
|
|
49
48
|
layout = QVBoxLayout(self)
|
|
50
49
|
|
|
51
50
|
# Instructions
|
|
@@ -47,7 +47,6 @@ class AlignmentDialog(Dialog3DPickingMixin, QDialog):
|
|
|
47
47
|
axis_names = {'x': 'X-axis', 'y': 'Y-axis', 'z': 'Z-axis'}
|
|
48
48
|
self.setWindowTitle(f"Align to {axis_names[self.axis]}")
|
|
49
49
|
self.setModal(False) # モードレスにしてクリックを阻害しない
|
|
50
|
-
self.setWindowFlags(Qt.WindowType.Window | Qt.WindowType.WindowStaysOnTopHint) # 常に前面表示
|
|
51
50
|
layout = QVBoxLayout(self)
|
|
52
51
|
|
|
53
52
|
# Instructions
|
|
@@ -45,7 +45,7 @@ class AngleDialog(Dialog3DPickingMixin, QDialog):
|
|
|
45
45
|
def init_ui(self):
|
|
46
46
|
self.setWindowTitle("Adjust Angle")
|
|
47
47
|
self.setModal(False) # モードレスにしてクリックを阻害しない
|
|
48
|
-
|
|
48
|
+
# 常に前面表示
|
|
49
49
|
layout = QVBoxLayout(self)
|
|
50
50
|
|
|
51
51
|
# Instructions
|
|
Binary file
|
|
@@ -38,7 +38,7 @@ class BondLengthDialog(Dialog3DPickingMixin, QDialog):
|
|
|
38
38
|
def init_ui(self):
|
|
39
39
|
self.setWindowTitle("Adjust Bond Length")
|
|
40
40
|
self.setModal(False) # モードレスにしてクリックを阻害しない
|
|
41
|
-
|
|
41
|
+
# 常に前面表示
|
|
42
42
|
layout = QVBoxLayout(self)
|
|
43
43
|
|
|
44
44
|
# Instructions
|
|
@@ -123,7 +123,6 @@ class ConstrainedOptimizationDialog(Dialog3DPickingMixin, QDialog):
|
|
|
123
123
|
self.setWindowTitle("Constrained Optimization")
|
|
124
124
|
self.setModal(False)
|
|
125
125
|
self.resize(450, 500)
|
|
126
|
-
self.setWindowFlags(Qt.WindowType.Window | Qt.WindowType.WindowStaysOnTopHint)
|
|
127
126
|
layout = QVBoxLayout(self)
|
|
128
127
|
|
|
129
128
|
# 1. 説明
|
|
@@ -42,6 +42,7 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
|
|
|
42
42
|
self._mouse_press_pos = None
|
|
43
43
|
|
|
44
44
|
self.AddObserver("LeftButtonPressEvent", self.on_left_button_down)
|
|
45
|
+
#self.AddObserver("LeftButtonDoubleClickEvent", self.on_left_button_down)
|
|
45
46
|
self.AddObserver("RightButtonPressEvent", self.on_right_button_down)
|
|
46
47
|
self.AddObserver("MouseMoveEvent", self.on_mouse_move)
|
|
47
48
|
self.AddObserver("LeftButtonReleaseEvent", self.on_left_button_up)
|
|
@@ -85,8 +86,12 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
|
|
|
85
86
|
if 0 <= closest_atom_idx < mw.current_mol.GetNumAtoms():
|
|
86
87
|
atom = mw.current_mol.GetAtomWithIdx(int(closest_atom_idx))
|
|
87
88
|
if atom:
|
|
88
|
-
|
|
89
|
-
|
|
89
|
+
try:
|
|
90
|
+
atomic_num = atom.GetAtomicNum()
|
|
91
|
+
vdw_radius = pt.GetRvdw(atomic_num)
|
|
92
|
+
if vdw_radius < 0.1: vdw_radius = 1.5
|
|
93
|
+
except Exception:
|
|
94
|
+
vdw_radius = 1.5
|
|
90
95
|
click_threshold = vdw_radius * 1.5
|
|
91
96
|
|
|
92
97
|
if distances[closest_atom_idx] < click_threshold:
|
|
@@ -148,11 +153,11 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
|
|
|
148
153
|
move_group_dialog.update_display()
|
|
149
154
|
return
|
|
150
155
|
else:
|
|
151
|
-
# 原子以外をクリック
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
+
# 原子以外をクリック
|
|
157
|
+
# 即座に解除せず、マウスイベントを追跡して回転かクリックかを判定する
|
|
158
|
+
self._mouse_press_pos = self.GetInteractor().GetEventPosition()
|
|
159
|
+
self._mouse_moved_during_drag = False
|
|
160
|
+
|
|
156
161
|
# カメラ回転を許可
|
|
157
162
|
super(CustomInteractorStyle, self).OnLeftButtonDown()
|
|
158
163
|
return
|
|
@@ -166,8 +171,9 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
|
|
|
166
171
|
# 測定モードが有効な場合の処理
|
|
167
172
|
if mw.measurement_mode and mw.current_mol:
|
|
168
173
|
click_pos = self.GetInteractor().GetEventPosition()
|
|
169
|
-
|
|
170
|
-
|
|
174
|
+
# Note: We do NOT set _mouse_press_pos here initially.
|
|
175
|
+
# We only set it if we confirm it's a background click (see below).
|
|
176
|
+
self._mouse_moved_during_drag = False # Reset drag flag
|
|
171
177
|
|
|
172
178
|
picker = mw.plotter.picker
|
|
173
179
|
|
|
@@ -185,17 +191,24 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
|
|
|
185
191
|
# クリック閾値チェック
|
|
186
192
|
atom = mw.current_mol.GetAtomWithIdx(int(closest_atom_idx))
|
|
187
193
|
if atom:
|
|
188
|
-
|
|
189
|
-
|
|
194
|
+
try:
|
|
195
|
+
atomic_num = atom.GetAtomicNum()
|
|
196
|
+
vdw_radius = pt.GetRvdw(atomic_num)
|
|
197
|
+
if vdw_radius < 0.1: vdw_radius = 1.5
|
|
198
|
+
except Exception:
|
|
199
|
+
vdw_radius = 1.5
|
|
190
200
|
click_threshold = vdw_radius * 1.5
|
|
191
201
|
|
|
192
202
|
if distances[closest_atom_idx] < click_threshold:
|
|
193
203
|
mw.handle_measurement_atom_selection(int(closest_atom_idx))
|
|
194
204
|
return # 原子選択処理完了、カメラ回転は無効
|
|
195
205
|
|
|
206
|
+
|
|
196
207
|
# 測定モードで原子以外をクリックした場合は計測選択をクリア
|
|
197
|
-
#
|
|
208
|
+
# ただし、回転操作(ドラッグ)の場合はクリアしないため、
|
|
209
|
+
# ここで _mouse_press_pos を記録し、Upイベントで判定する。
|
|
198
210
|
self._is_dragging_atom = False
|
|
211
|
+
self._mouse_press_pos = click_pos
|
|
199
212
|
super().OnLeftButtonDown()
|
|
200
213
|
return
|
|
201
214
|
|
|
@@ -219,8 +232,12 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
|
|
|
219
232
|
# RDKitのMolオブジェクトから原子を安全に取得
|
|
220
233
|
atom = mw.current_mol.GetAtomWithIdx(int(closest_atom_idx))
|
|
221
234
|
if atom:
|
|
222
|
-
|
|
223
|
-
|
|
235
|
+
try:
|
|
236
|
+
atomic_num = atom.GetAtomicNum()
|
|
237
|
+
vdw_radius = pt.GetRvdw(atomic_num)
|
|
238
|
+
if vdw_radius < 0.1: vdw_radius = 1.5
|
|
239
|
+
except Exception:
|
|
240
|
+
vdw_radius = 1.5
|
|
224
241
|
click_threshold = vdw_radius * 1.5
|
|
225
242
|
|
|
226
243
|
if distances[closest_atom_idx] < click_threshold:
|
|
@@ -265,8 +282,12 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
|
|
|
265
282
|
if 0 <= closest_atom_idx < mw.current_mol.GetNumAtoms():
|
|
266
283
|
atom = mw.current_mol.GetAtomWithIdx(int(closest_atom_idx))
|
|
267
284
|
if atom:
|
|
268
|
-
|
|
269
|
-
|
|
285
|
+
try:
|
|
286
|
+
atomic_num = atom.GetAtomicNum()
|
|
287
|
+
vdw_radius = pt.GetRvdw(atomic_num)
|
|
288
|
+
if vdw_radius < 0.1: vdw_radius = 1.5
|
|
289
|
+
except Exception:
|
|
290
|
+
vdw_radius = 1.5
|
|
270
291
|
click_threshold = vdw_radius * 1.5
|
|
271
292
|
|
|
272
293
|
if distances[closest_atom_idx] < click_threshold:
|
|
@@ -458,37 +479,23 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
|
|
|
458
479
|
move_group_dialog.on_atom_picked(clicked_atom)
|
|
459
480
|
except Exception as e:
|
|
460
481
|
print(f"Error in toggle: {e}")
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
if
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
self._is_dragging_atom = False
|
|
473
|
-
self.is_dragging = False
|
|
474
|
-
self._mouse_moved_during_drag = False
|
|
475
|
-
self._mouse_press_pos = None
|
|
476
|
-
|
|
477
|
-
try:
|
|
478
|
-
mw.plotter.setCursor(Qt.CursorShape.ArrowCursor)
|
|
479
|
-
except Exception:
|
|
480
|
-
pass
|
|
481
|
-
return
|
|
482
|
+
|
|
483
|
+
# Move Groupモードでの背景クリック判定(選択解除)
|
|
484
|
+
# グループドラッグでなく、マウス移動もなかった(=回転操作でない)場合
|
|
485
|
+
# かつ、mouse_press_pos が記録されている(背景クリックで開始した)場合
|
|
486
|
+
if move_group_dialog and not getattr(move_group_dialog, '_is_dragging_group_vtk', False):
|
|
487
|
+
if not self._mouse_moved_during_drag and self._mouse_press_pos is not None:
|
|
488
|
+
# 背景クリック -> 選択解除
|
|
489
|
+
move_group_dialog.group_atoms.clear()
|
|
490
|
+
move_group_dialog.selected_atoms.clear()
|
|
491
|
+
move_group_dialog.clear_atom_labels()
|
|
492
|
+
move_group_dialog.update_display()
|
|
482
493
|
|
|
483
494
|
# 計測モードで、マウスが動いていない場合(つまりクリック)の処理
|
|
495
|
+
# _mouse_press_pos が None でない = 背景をクリックしたことを意味する(Downイベントでそう設定したため)
|
|
484
496
|
if mw.measurement_mode and not self._mouse_moved_during_drag and self._mouse_press_pos is not None:
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
picker.Pick(click_pos[0], click_pos[1], 0, mw.plotter.renderer)
|
|
488
|
-
|
|
489
|
-
# 原子がクリックされていない場合は測定選択をクリア
|
|
490
|
-
if picker.GetActor() is not mw.atom_actor:
|
|
491
|
-
mw.clear_measurement_selection()
|
|
497
|
+
# 背景クリック -> 測定選択をクリア
|
|
498
|
+
mw.clear_measurement_selection()
|
|
492
499
|
|
|
493
500
|
if self._is_dragging_atom:
|
|
494
501
|
# カスタムドラッグの後始末
|
|
@@ -594,7 +601,7 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
|
|
|
594
601
|
# カメラ回転の後始末を親クラスに任せます
|
|
595
602
|
super().OnLeftButtonUp()
|
|
596
603
|
|
|
597
|
-
# 状態をリセット(完全なクリーンアップ)
|
|
604
|
+
# 状態をリセット(完全なクリーンアップ) - すべてのチェックの後に実行
|
|
598
605
|
self._is_dragging_atom = False
|
|
599
606
|
self.is_dragging = False
|
|
600
607
|
self._mouse_press_pos = None
|
|
@@ -602,12 +609,6 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
|
|
|
602
609
|
|
|
603
610
|
# Move Group関連の状態もクリア
|
|
604
611
|
try:
|
|
605
|
-
move_group_dialog = None
|
|
606
|
-
for widget in QApplication.topLevelWidgets():
|
|
607
|
-
if isinstance(widget, MoveGroupDialog) and widget.isVisible():
|
|
608
|
-
move_group_dialog = widget
|
|
609
|
-
break
|
|
610
|
-
|
|
611
612
|
if move_group_dialog:
|
|
612
613
|
move_group_dialog._is_dragging_group_vtk = False
|
|
613
614
|
move_group_dialog._drag_start_pos = None
|
|
@@ -619,12 +620,11 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
|
|
|
619
620
|
except Exception:
|
|
620
621
|
pass
|
|
621
622
|
|
|
622
|
-
# ピックリセットは測定モードで実際に問題が発生した場合のみ行う
|
|
623
|
-
# (通常のドラッグ回転では行わない)
|
|
624
|
-
|
|
625
623
|
# ボタンを離した後のカーソル表示を最新の状態に更新
|
|
626
|
-
|
|
627
|
-
|
|
624
|
+
try:
|
|
625
|
+
mw.plotter.setCursor(Qt.CursorShape.ArrowCursor)
|
|
626
|
+
except Exception:
|
|
627
|
+
pass
|
|
628
628
|
# 2Dビューにフォーカスを戻し、ショートカットキーなどが使えるようにする
|
|
629
629
|
if mw and mw.view_2d:
|
|
630
630
|
mw.view_2d.setFocus()
|
|
@@ -11,6 +11,7 @@ DOI: 10.5281/zenodo.17268532
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
from pyvistaqt import QtInteractor
|
|
14
|
+
import time
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class CustomQtInteractor(QtInteractor):
|
|
@@ -24,6 +25,7 @@ class CustomQtInteractor(QtInteractor):
|
|
|
24
25
|
# the VTK interactor and causing unexpected behaviour in the 3D view.
|
|
25
26
|
self._last_click_time = 0.0
|
|
26
27
|
self._click_count = 0
|
|
28
|
+
self._ignore_next_release = False
|
|
27
29
|
|
|
28
30
|
def wheelEvent(self, event):
|
|
29
31
|
"""
|
|
@@ -40,17 +42,58 @@ class CustomQtInteractor(QtInteractor):
|
|
|
40
42
|
"""
|
|
41
43
|
Qtのマウスリリースイベントをオーバーライドし、
|
|
42
44
|
3Dビューでの全ての操作完了後に2Dビューへフォーカスを戻す。
|
|
45
|
+
また、Ghost Release(対応するPressがないRelease)をフィルタリングする。
|
|
43
46
|
"""
|
|
47
|
+
if self._ignore_next_release:
|
|
48
|
+
self._ignore_next_release = False
|
|
49
|
+
event.accept()
|
|
50
|
+
return
|
|
51
|
+
|
|
44
52
|
super().mouseReleaseEvent(event) # 親クラスのイベントを先に処理
|
|
45
53
|
if self.main_window and hasattr(self.main_window, 'view_2d'):
|
|
46
54
|
self.main_window.view_2d.setFocus()
|
|
47
55
|
|
|
56
|
+
def mousePressEvent(self, event):
|
|
57
|
+
"""
|
|
58
|
+
Custom mouse press handling to track accumulated clicks and filter out
|
|
59
|
+
triple-clicks.
|
|
60
|
+
"""
|
|
61
|
+
current_time = time.time()
|
|
62
|
+
# Reset count if too much time has passed (0.5s is standard double-click time)
|
|
63
|
+
if current_time - self._last_click_time > 0.5:
|
|
64
|
+
self._click_count = 0
|
|
65
|
+
|
|
66
|
+
self._click_count += 1
|
|
67
|
+
self._last_click_time = current_time
|
|
68
|
+
|
|
69
|
+
# If this is the 3rd click (or more), swallow it to prevent
|
|
70
|
+
# the internal state desync that happens with rapid clicking sequences.
|
|
71
|
+
if self._click_count >= 3:
|
|
72
|
+
self._ignore_next_release = True
|
|
73
|
+
event.accept()
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
super().mousePressEvent(event)
|
|
77
|
+
|
|
48
78
|
def mouseDoubleClickEvent(self, event):
|
|
49
79
|
"""Ignore mouse double-clicks on the 3D widget to avoid accidental actions.
|
|
50
80
|
|
|
51
81
|
Swallow the double-click event so it doesn't trigger selection, editing,
|
|
52
82
|
or camera jumps. We intentionally do not call the superclass handler.
|
|
83
|
+
Crucially, we also flag the NEXT release event to be swallowed, preventing
|
|
84
|
+
a "Ghost Release" (Release without Press) from reaching VTK.
|
|
53
85
|
"""
|
|
86
|
+
current_time = time.time()
|
|
87
|
+
self._last_click_time = current_time
|
|
88
|
+
# Set to 2 to ensure the next click counts as 3rd
|
|
89
|
+
if current_time - self._last_click_time < 0.5:
|
|
90
|
+
self._click_count = 2
|
|
91
|
+
else:
|
|
92
|
+
self._click_count = 2 # Force sync
|
|
93
|
+
|
|
94
|
+
# We must ignore the release event that follows this double-click event
|
|
95
|
+
self._ignore_next_release = True
|
|
96
|
+
|
|
54
97
|
try:
|
|
55
98
|
# Accept the event to mark it handled and prevent further processing.
|
|
56
99
|
event.accept()
|
|
@@ -31,6 +31,11 @@ class Dialog3DPickingMixin:
|
|
|
31
31
|
event.type() == QEvent.Type.MouseButtonPress and
|
|
32
32
|
event.button() == Qt.MouseButton.LeftButton):
|
|
33
33
|
|
|
34
|
+
# Start tracking for smart selection (click vs drag)
|
|
35
|
+
self._mouse_press_pos = event.pos()
|
|
36
|
+
self._mouse_moved = False
|
|
37
|
+
|
|
38
|
+
|
|
34
39
|
try:
|
|
35
40
|
# VTKイベント座標を取得(元のロジックと同じ)
|
|
36
41
|
interactor = self.main_window.plotter.interactor
|
|
@@ -48,8 +53,12 @@ class Dialog3DPickingMixin:
|
|
|
48
53
|
# クリック閾値チェック(元のロジックと同じ)
|
|
49
54
|
atom = self.mol.GetAtomWithIdx(int(closest_atom_idx))
|
|
50
55
|
if atom:
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
try:
|
|
57
|
+
atomic_num = atom.GetAtomicNum()
|
|
58
|
+
vdw_radius = pt.GetRvdw(atomic_num)
|
|
59
|
+
if vdw_radius < 0.1: vdw_radius = 1.5
|
|
60
|
+
except Exception:
|
|
61
|
+
vdw_radius = 1.5
|
|
53
62
|
click_threshold = vdw_radius * 1.5
|
|
54
63
|
|
|
55
64
|
if distances[closest_atom_idx] < click_threshold:
|
|
@@ -63,15 +72,14 @@ class Dialog3DPickingMixin:
|
|
|
63
72
|
except Exception:
|
|
64
73
|
pass
|
|
65
74
|
self.on_atom_picked(int(closest_atom_idx))
|
|
75
|
+
|
|
76
|
+
# We picked an atom, so stop tracking for background click
|
|
77
|
+
self._mouse_press_pos = None
|
|
66
78
|
return True
|
|
67
79
|
|
|
68
|
-
#
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
# We did not actually pick an atom; do NOT consume the event here so
|
|
72
|
-
# the interactor and CustomInteractorStyle can handle camera rotation
|
|
73
|
-
# and other behaviors. Returning False (or calling the base
|
|
74
|
-
# implementation) allows normal processing to continue.
|
|
80
|
+
# 原子以外をクリックした場合
|
|
81
|
+
# 即時には解除せず、回転操作(ドラッグ)を許可する。
|
|
82
|
+
# 実際の解除は MouseButtonRelease イベントで行う。
|
|
75
83
|
return False
|
|
76
84
|
|
|
77
85
|
except Exception as e:
|
|
@@ -80,6 +88,31 @@ class Dialog3DPickingMixin:
|
|
|
80
88
|
# event pipeline continue so the UI remains responsive.
|
|
81
89
|
return False
|
|
82
90
|
|
|
91
|
+
# Add movement tracking for smart selection
|
|
92
|
+
elif (obj == self.main_window.plotter.interactor and
|
|
93
|
+
event.type() == QEvent.Type.MouseMove):
|
|
94
|
+
if hasattr(self, '_mouse_press_pos') and self._mouse_press_pos is not None:
|
|
95
|
+
# Check if moved significantly
|
|
96
|
+
diff = event.pos() - self._mouse_press_pos
|
|
97
|
+
if diff.manhattanLength() > 3:
|
|
98
|
+
self._mouse_moved = True
|
|
99
|
+
|
|
100
|
+
# Add release handling for smart selection
|
|
101
|
+
elif (obj == self.main_window.plotter.interactor and
|
|
102
|
+
event.type() == QEvent.Type.MouseButtonRelease and
|
|
103
|
+
event.button() == Qt.MouseButton.LeftButton):
|
|
104
|
+
|
|
105
|
+
if hasattr(self, '_mouse_press_pos') and self._mouse_press_pos is not None:
|
|
106
|
+
if not getattr(self, '_mouse_moved', False):
|
|
107
|
+
# Pure click (no drag) on background -> Clear selection
|
|
108
|
+
if hasattr(self, 'clear_selection'):
|
|
109
|
+
self.clear_selection()
|
|
110
|
+
|
|
111
|
+
# Reset state
|
|
112
|
+
self._mouse_press_pos = None
|
|
113
|
+
self._mouse_moved = False
|
|
114
|
+
|
|
115
|
+
|
|
83
116
|
return super().eventFilter(obj, event)
|
|
84
117
|
|
|
85
118
|
def enable_picking(self):
|
|
@@ -47,7 +47,7 @@ class DihedralDialog(Dialog3DPickingMixin, QDialog):
|
|
|
47
47
|
def init_ui(self):
|
|
48
48
|
self.setWindowTitle("Adjust Dihedral Angle")
|
|
49
49
|
self.setModal(False) # モードレスにしてクリックを阻害しない
|
|
50
|
-
|
|
50
|
+
# 常に前面表示
|
|
51
51
|
layout = QVBoxLayout(self)
|
|
52
52
|
|
|
53
53
|
# Instructions
|
|
@@ -596,8 +596,10 @@ class MainWindowAppState(object):
|
|
|
596
596
|
json_data["last_successful_optimization_method"] = None
|
|
597
597
|
|
|
598
598
|
# Plugin State Persistence (Phase 3)
|
|
599
|
+
# Start with preserved data from missing plugins
|
|
600
|
+
plugin_data = self._preserved_plugin_data.copy() if self._preserved_plugin_data else {}
|
|
601
|
+
|
|
599
602
|
if self.plugin_manager and self.plugin_manager.save_handlers:
|
|
600
|
-
plugin_data = {}
|
|
601
603
|
for name, callback in self.plugin_manager.save_handlers.items():
|
|
602
604
|
try:
|
|
603
605
|
p_state = callback()
|
|
@@ -606,8 +608,8 @@ class MainWindowAppState(object):
|
|
|
606
608
|
except Exception as e:
|
|
607
609
|
print(f"Error saving state for plugin {name}: {e}")
|
|
608
610
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
+
if plugin_data:
|
|
612
|
+
json_data['plugins'] = plugin_data
|
|
611
613
|
|
|
612
614
|
return json_data
|
|
613
615
|
|
|
@@ -629,14 +631,19 @@ class MainWindowAppState(object):
|
|
|
629
631
|
self.last_successful_optimization_method = None
|
|
630
632
|
|
|
631
633
|
# Plugin State Restoration (Phase 3)
|
|
632
|
-
|
|
634
|
+
self._preserved_plugin_data = {} # Reset preserved data on new load
|
|
635
|
+
if "plugins" in json_data:
|
|
633
636
|
plugin_data = json_data["plugins"]
|
|
634
637
|
for name, p_state in plugin_data.items():
|
|
635
|
-
if name in self.plugin_manager.load_handlers:
|
|
638
|
+
if self.plugin_manager and name in self.plugin_manager.load_handlers:
|
|
636
639
|
try:
|
|
637
640
|
self.plugin_manager.load_handlers[name](p_state)
|
|
638
641
|
except Exception as e:
|
|
639
642
|
print(f"Error loading state for plugin {name}: {e}")
|
|
643
|
+
else:
|
|
644
|
+
# No handler found (plugin disabled or missing)
|
|
645
|
+
# Preserve data so it's not lost on next save
|
|
646
|
+
self._preserved_plugin_data[name] = p_state
|
|
640
647
|
|
|
641
648
|
|
|
642
649
|
# 2D構造データの復元
|
|
@@ -697,7 +704,7 @@ class MainWindowAppState(object):
|
|
|
697
704
|
for atom in self.data.atoms.values():
|
|
698
705
|
atom['item'].update_style()
|
|
699
706
|
# 3D構造データの復元
|
|
700
|
-
if "3d_structure" in json_data:
|
|
707
|
+
if "3d_structure" in json_data and json_data["3d_structure"] is not None:
|
|
701
708
|
structure_3d = json_data["3d_structure"]
|
|
702
709
|
|
|
703
710
|
# 制約データの復元 (JSONはタプルをリストとして保存するので、タプルに再変換)
|
|
@@ -263,7 +263,7 @@ class MainWindowDialogManager(object):
|
|
|
263
263
|
self.measurement_action.setChecked(False)
|
|
264
264
|
self.toggle_measurement_mode(False)
|
|
265
265
|
|
|
266
|
-
dialog = TranslationDialog(self.current_mol, self)
|
|
266
|
+
dialog = TranslationDialog(self.current_mol, self, parent=self)
|
|
267
267
|
self.active_3d_dialogs.append(dialog) # 参照を保持
|
|
268
268
|
dialog.show() # execではなくshowを使用してモードレス表示
|
|
269
269
|
dialog.accepted.connect(lambda: self.statusBar().showMessage("Translation applied."))
|
|
@@ -279,7 +279,7 @@ class MainWindowDialogManager(object):
|
|
|
279
279
|
self.measurement_action.setChecked(False)
|
|
280
280
|
self.toggle_measurement_mode(False)
|
|
281
281
|
|
|
282
|
-
dialog = MoveGroupDialog(self.current_mol, self)
|
|
282
|
+
dialog = MoveGroupDialog(self.current_mol, self, parent=self)
|
|
283
283
|
self.active_3d_dialogs.append(dialog)
|
|
284
284
|
dialog.show()
|
|
285
285
|
dialog.accepted.connect(lambda: self.statusBar().showMessage("Group transformation applied."))
|
|
@@ -302,7 +302,7 @@ class MainWindowDialogManager(object):
|
|
|
302
302
|
self.measurement_action.setChecked(False)
|
|
303
303
|
self.toggle_measurement_mode(False)
|
|
304
304
|
|
|
305
|
-
dialog = AlignPlaneDialog(self.current_mol, self, plane, preselected_atoms)
|
|
305
|
+
dialog = AlignPlaneDialog(self.current_mol, self, plane, preselected_atoms, parent=self)
|
|
306
306
|
self.active_3d_dialogs.append(dialog) # 参照を保持
|
|
307
307
|
dialog.show() # execではなくshowを使用してモードレス表示
|
|
308
308
|
dialog.accepted.connect(lambda: self.statusBar().showMessage(f"Atoms alignd to {plane.upper()} plane."))
|
|
@@ -325,7 +325,7 @@ class MainWindowDialogManager(object):
|
|
|
325
325
|
self.measurement_action.setChecked(False)
|
|
326
326
|
self.toggle_measurement_mode(False)
|
|
327
327
|
|
|
328
|
-
dialog = PlanarizeDialog(self.current_mol, self, preselected_atoms)
|
|
328
|
+
dialog = PlanarizeDialog(self.current_mol, self, preselected_atoms, parent=self)
|
|
329
329
|
self.active_3d_dialogs.append(dialog)
|
|
330
330
|
dialog.show()
|
|
331
331
|
dialog.accepted.connect(lambda: self.statusBar().showMessage("Selection planarized to best-fit plane."))
|
|
@@ -348,7 +348,7 @@ class MainWindowDialogManager(object):
|
|
|
348
348
|
self.measurement_action.setChecked(False)
|
|
349
349
|
self.toggle_measurement_mode(False)
|
|
350
350
|
|
|
351
|
-
dialog = AlignmentDialog(self.current_mol, self, axis, preselected_atoms)
|
|
351
|
+
dialog = AlignmentDialog(self.current_mol, self, axis, preselected_atoms, parent=self)
|
|
352
352
|
self.active_3d_dialogs.append(dialog) # 参照を保持
|
|
353
353
|
dialog.show() # execではなくshowを使用してモードレス表示
|
|
354
354
|
dialog.accepted.connect(lambda: self.statusBar().showMessage(f"Atoms aligned to {axis.upper()}-axis."))
|
|
@@ -371,7 +371,7 @@ class MainWindowDialogManager(object):
|
|
|
371
371
|
self.measurement_action.setChecked(False)
|
|
372
372
|
self.toggle_measurement_mode(False)
|
|
373
373
|
|
|
374
|
-
dialog = BondLengthDialog(self.current_mol, self, preselected_atoms)
|
|
374
|
+
dialog = BondLengthDialog(self.current_mol, self, preselected_atoms, parent=self)
|
|
375
375
|
self.active_3d_dialogs.append(dialog) # 参照を保持
|
|
376
376
|
dialog.show() # execではなくshowを使用してモードレス表示
|
|
377
377
|
dialog.accepted.connect(lambda: self.statusBar().showMessage("Bond length adjusted."))
|
|
@@ -394,7 +394,7 @@ class MainWindowDialogManager(object):
|
|
|
394
394
|
self.measurement_action.setChecked(False)
|
|
395
395
|
self.toggle_measurement_mode(False)
|
|
396
396
|
|
|
397
|
-
dialog = AngleDialog(self.current_mol, self, preselected_atoms)
|
|
397
|
+
dialog = AngleDialog(self.current_mol, self, preselected_atoms, parent=self)
|
|
398
398
|
self.active_3d_dialogs.append(dialog) # 参照を保持
|
|
399
399
|
dialog.show() # execではなくshowを使用してモードレス表示
|
|
400
400
|
dialog.accepted.connect(lambda: self.statusBar().showMessage("Angle adjusted."))
|
|
@@ -417,7 +417,7 @@ class MainWindowDialogManager(object):
|
|
|
417
417
|
self.measurement_action.setChecked(False)
|
|
418
418
|
self.toggle_measurement_mode(False)
|
|
419
419
|
|
|
420
|
-
dialog = DihedralDialog(self.current_mol, self, preselected_atoms)
|
|
420
|
+
dialog = DihedralDialog(self.current_mol, self, preselected_atoms, parent=self)
|
|
421
421
|
self.active_3d_dialogs.append(dialog) # 参照を保持
|
|
422
422
|
dialog.show() # execではなくshowを使用してモードレス表示
|
|
423
423
|
dialog.accepted.connect(lambda: self.statusBar().showMessage("Dihedral angle adjusted."))
|
|
@@ -453,7 +453,7 @@ class MainWindowDialogManager(object):
|
|
|
453
453
|
self.measurement_action.setChecked(False)
|
|
454
454
|
self.toggle_measurement_mode(False)
|
|
455
455
|
|
|
456
|
-
dialog = ConstrainedOptimizationDialog(self.current_mol, self)
|
|
456
|
+
dialog = ConstrainedOptimizationDialog(self.current_mol, self, parent=self)
|
|
457
457
|
self.active_3d_dialogs.append(dialog) # 参照を保持
|
|
458
458
|
dialog.show() # モードレス表示
|
|
459
459
|
dialog.finished.connect(lambda: self.remove_dialog_from_list(dialog))
|
|
@@ -1211,7 +1211,10 @@ class MainWindowEditActions(object):
|
|
|
1211
1211
|
|
|
1212
1212
|
atom = mol.GetAtomWithIdx(idx)
|
|
1213
1213
|
# GetRvdw() はファンデルワールス半径を返す
|
|
1214
|
-
|
|
1214
|
+
try:
|
|
1215
|
+
vdw_radii.append(pt.GetRvdw(atom.GetAtomicNum()))
|
|
1216
|
+
except RuntimeError:
|
|
1217
|
+
vdw_radii.append(1.5)
|
|
1215
1218
|
|
|
1216
1219
|
positions_np = np.array(positions)
|
|
1217
1220
|
vdw_radii_np = np.array(vdw_radii)
|
|
@@ -264,6 +264,9 @@ class MainWindowMainInit(object):
|
|
|
264
264
|
except Exception as e:
|
|
265
265
|
print(f"Failed to initialize PluginManager: {e}")
|
|
266
266
|
self.plugin_manager = None
|
|
267
|
+
|
|
268
|
+
# ロードされていないプラグインのデータを保持する辞書
|
|
269
|
+
self._preserved_plugin_data = {}
|
|
267
270
|
|
|
268
271
|
self.init_ui()
|
|
269
272
|
self.init_worker_thread()
|
|
@@ -1832,6 +1835,14 @@ class MainWindowMainInit(object):
|
|
|
1832
1835
|
if not found_sub:
|
|
1833
1836
|
current_menu = current_menu.addMenu(part)
|
|
1834
1837
|
|
|
1838
|
+
# If last action was NOT from a plugin, insert a separator
|
|
1839
|
+
actions = current_menu.actions()
|
|
1840
|
+
if actions:
|
|
1841
|
+
last_action = actions[-1]
|
|
1842
|
+
if not last_action.isSeparator() and last_action.data() != PLUGIN_ACTION_TAG:
|
|
1843
|
+
sep = current_menu.addSeparator()
|
|
1844
|
+
sep.setData(PLUGIN_ACTION_TAG)
|
|
1845
|
+
|
|
1835
1846
|
# Add action
|
|
1836
1847
|
action_text = text if text else parts[-1]
|
|
1837
1848
|
action = QAction(action_text, self)
|
|
@@ -219,7 +219,15 @@ class MainWindowView3d(object):
|
|
|
219
219
|
if current_style == 'cpk':
|
|
220
220
|
atom_scale = self.settings.get('cpk_atom_scale', 1.0)
|
|
221
221
|
resolution = self.settings.get('cpk_resolution', 32)
|
|
222
|
-
|
|
222
|
+
# Safe VDW lookup to handle custom elements like 'Bq'
|
|
223
|
+
def get_safe_rvdw(s):
|
|
224
|
+
try:
|
|
225
|
+
r = pt.GetRvdw(pt.GetAtomicNumber(s))
|
|
226
|
+
return r if r > 0.1 else 1.5
|
|
227
|
+
except Exception:
|
|
228
|
+
return 1.5
|
|
229
|
+
|
|
230
|
+
rad = np.array([get_safe_rvdw(s) * atom_scale for s in sym])
|
|
223
231
|
elif current_style == 'wireframe':
|
|
224
232
|
# Wireframeでは原子を描画しないので、この設定は実際には使用されない
|
|
225
233
|
resolution = self.settings.get('wireframe_resolution', 6)
|
|
@@ -42,7 +42,6 @@ class MoveGroupDialog(Dialog3DPickingMixin, QDialog):
|
|
|
42
42
|
def init_ui(self):
|
|
43
43
|
self.setWindowTitle("Move Group")
|
|
44
44
|
self.setModal(False)
|
|
45
|
-
self.setWindowFlags(Qt.WindowType.Window | Qt.WindowType.WindowStaysOnTopHint)
|
|
46
45
|
self.resize(300,400) # ウィンドウサイズを設定
|
|
47
46
|
layout = QVBoxLayout(self)
|
|
48
47
|
|
|
@@ -195,9 +194,12 @@ class MoveGroupDialog(Dialog3DPickingMixin, QDialog):
|
|
|
195
194
|
if 0 <= closest_atom_idx < self.mol.GetNumAtoms():
|
|
196
195
|
atom = self.mol.GetAtomWithIdx(int(closest_atom_idx))
|
|
197
196
|
if atom:
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
197
|
+
try:
|
|
198
|
+
atomic_num = atom.GetAtomicNum()
|
|
199
|
+
vdw_radius = pt.GetRvdw(atomic_num)
|
|
200
|
+
if vdw_radius < 0.1: vdw_radius = 1.5
|
|
201
|
+
except Exception:
|
|
202
|
+
vdw_radius = 1.5
|
|
201
203
|
click_threshold = vdw_radius * 1.5
|
|
202
204
|
|
|
203
205
|
if distances[closest_atom_idx] < click_threshold:
|
|
@@ -349,7 +351,7 @@ class MoveGroupDialog(Dialog3DPickingMixin, QDialog):
|
|
|
349
351
|
return False
|
|
350
352
|
|
|
351
353
|
# その他のイベントは親クラスに渡す
|
|
352
|
-
return
|
|
354
|
+
return super().eventFilter(obj, event)
|
|
353
355
|
|
|
354
356
|
def on_atom_picked(self, atom_idx):
|
|
355
357
|
"""原子がピックされたときに、その原子が属する連結成分全体を選択(複数グループ対応)"""
|
|
@@ -46,7 +46,6 @@ class PlanarizeDialog(Dialog3DPickingMixin, QDialog):
|
|
|
46
46
|
def init_ui(self):
|
|
47
47
|
self.setWindowTitle("Planarize")
|
|
48
48
|
self.setModal(False)
|
|
49
|
-
self.setWindowFlags(Qt.WindowType.Window | Qt.WindowType.WindowStaysOnTopHint)
|
|
50
49
|
layout = QVBoxLayout(self)
|
|
51
50
|
|
|
52
51
|
instruction_label = QLabel("Click atoms in the 3D view to select them for planarization (minimum 3 required).")
|
|
@@ -65,6 +65,24 @@ class PluginContext:
|
|
|
65
65
|
"""
|
|
66
66
|
return self._manager.get_main_window()
|
|
67
67
|
|
|
68
|
+
@property
|
|
69
|
+
def current_molecule(self) -> Any:
|
|
70
|
+
"""
|
|
71
|
+
Get or set the current molecule (RDKit Mol object).
|
|
72
|
+
"""
|
|
73
|
+
mw = self._manager.get_main_window()
|
|
74
|
+
if mw:
|
|
75
|
+
return mw.current_mol
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
@current_molecule.setter
|
|
79
|
+
def current_molecule(self, mol: Any):
|
|
80
|
+
mw = self._manager.get_main_window()
|
|
81
|
+
if mw:
|
|
82
|
+
mw.current_mol = mol
|
|
83
|
+
if hasattr(mw, 'draw_molecule_3d'):
|
|
84
|
+
mw.draw_molecule_3d(mol)
|
|
85
|
+
|
|
68
86
|
def add_export_action(self, label: str, callback: Callable):
|
|
69
87
|
"""
|
|
70
88
|
Register a custom export action.
|
|
@@ -31,7 +31,6 @@ class TranslationDialog(Dialog3DPickingMixin, QDialog):
|
|
|
31
31
|
def init_ui(self):
|
|
32
32
|
self.setWindowTitle("Translation")
|
|
33
33
|
self.setModal(False) # モードレスにしてクリックを阻害しない
|
|
34
|
-
self.setWindowFlags(Qt.WindowType.Window | Qt.WindowType.WindowStaysOnTopHint) # 常に前面表示
|
|
35
34
|
layout = QVBoxLayout(self)
|
|
36
35
|
|
|
37
36
|
# Instructions
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: MoleditPy-linux
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.6
|
|
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
|
|
@@ -706,7 +706,9 @@ This is the Linux version of MoleditPy. The Open Babel fallback is disabled due
|
|
|
706
706
|
[](https://doi.org/10.5281/zenodo.17268532)
|
|
707
707
|
[](https://www.rdkit.org/)
|
|
708
708
|
|
|
709
|
-
|
|
709
|
+
[🇯🇵 日本語 (Japanese)](#japanese)
|
|
710
|
+
|
|
711
|
+
**MoleditPy** is a **programmable** and cross-platform molecular editor built in Python. It streamlines the workflow from 2D drawing to 3D visualization, making it an **ideal tool for rapidly preparing input files for DFT calculations**. Designed as an **open platform**, it also allows users to freely extend its capabilities, writing custom Python scripts to manipulate molecular data, automate tasks, or integrate new cheminformatics algorithms seamlessly.
|
|
710
712
|
|
|
711
713
|
**Author**: HiroYokoyama
|
|
712
714
|
**License**: GPL-v3
|
|
@@ -715,7 +717,7 @@ This is the Linux version of MoleditPy. The Open Babel fallback is disabled due
|
|
|
715
717
|
-----
|
|
716
718
|

|
|
717
719
|

|
|
718
|
-
|
|
720
|
+
|
|
719
721
|
|
|
720
722
|
## Overview
|
|
721
723
|
|
|
@@ -760,7 +762,14 @@ This application combines a modern GUI built with **PyQt6**, powerful cheminform
|
|
|
760
762
|
* Import structures from **MOL/SDF** files or **SMILES** strings.
|
|
761
763
|
* Export 3D structures to **MOL** or **XYZ** formats, which are compatible with most DFT calculation software.
|
|
762
764
|
* Export 2D and 3D views as high-resolution PNG images.
|
|
763
|
-
|
|
765
|
+
* Export 2D and 3D views as high-resolution PNG images.
|
|
766
|
+
|
|
767
|
+
### 4. Programmable & Extensible
|
|
768
|
+
|
|
769
|
+
* **Python Plugin System:** Drop your Python scripts into the plugin folder, and they instantly become part of the application menu.
|
|
770
|
+
* **Downloadable Plugins:** Explore and download specialized plugins from the [Plugin Explorer](https://hiroyokoyama.github.io/moleditpy-plugins/explorer/).
|
|
771
|
+
* **Full API Access:** Plugins have direct access to the `MainWindow`, `RDKit` molecule objects, and `PyGraphics` items, allowing for limitless customization.
|
|
772
|
+
* **Rapid Prototyping:** Ideal for researchers who need to test new algorithms or workflow automations on the fly.
|
|
764
773
|
|
|
765
774
|
## Installation and Execution
|
|
766
775
|
|
|
@@ -822,9 +831,11 @@ This project is licensed under the **GNU General Public License v3.0 (GPL-v3)**.
|
|
|
822
831
|
|
|
823
832
|
-----
|
|
824
833
|
|
|
834
|
+
<div id="japanese"></div>
|
|
835
|
+
|
|
825
836
|
# MoleditPy — A Python Molecular Editor
|
|
826
837
|
|
|
827
|
-
**MoleditPy**は、Python
|
|
838
|
+
**MoleditPy**は、Pythonで構築された**機能拡張が自由自在な**分子エディタープラットフォームです。2D描画から3Dへの変換により、**DFT計算用インプットの迅速な作成に最適なツール**であると同時に、**Pythonスクリプトを用いて必要な機能をユーザー自身が手軽に追加・開発できる**柔軟な環境を提供します。
|
|
828
839
|
|
|
829
840
|
**作者**: HiroYokoyama
|
|
830
841
|
**ライセンス**: GPL-v3
|
|
@@ -875,7 +886,14 @@ This project is licensed under the **GNU General Public License v3.0 (GPL-v3)**.
|
|
|
875
886
|
* **MOL/SDF**ファイルや**SMILES**文字列から構造をインポートできます。
|
|
876
887
|
* 3D構造を**MOL**または**XYZ**形式でエクスポートでき、これらは多くのDFT計算ソフトウェアと互換性があります。
|
|
877
888
|
* 2Dおよび3Dビューを高解像度のPNG画像としてエクスポートできます。
|
|
878
|
-
|
|
889
|
+
* 2Dおよび3Dビューを高解像度のPNG画像としてエクスポートできます。
|
|
890
|
+
|
|
891
|
+
### 4. プログラマブルで拡張可能
|
|
892
|
+
|
|
893
|
+
* **Pythonプラグインシステム:** Pythonスクリプトをプラグインフォルダに入れるだけで、即座にアプリケーションメニューの一部として機能します。
|
|
894
|
+
* **プラグインのダウンロード:** [Plugin Explorer](https://hiroyokoyama.github.io/moleditpy-plugins/explorer/) から特化したプラグインを探索・ダウンロードできます。
|
|
895
|
+
* **フルAPIアクセス:** プラグインは `MainWindow`、`RDKit` 分子オブジェクト、`PyGraphics` アイテムに直接アクセスでき、無限のカスタマイズが可能です。
|
|
896
|
+
* **迅速なプロトタイピング:** 新しいアルゴリズムやワークフローの自動化をその場でテストしたい研究者に最適です。
|
|
879
897
|
|
|
880
898
|
## インストールと実行
|
|
881
899
|
|
|
@@ -3,56 +3,57 @@ moleditpy_linux/__main__.py,sha256=bYu_h7lhc6PIl1-I_VI4_Q5MdBHmVrMiO4DYsy9jbiY,8
|
|
|
3
3
|
moleditpy_linux/main.py,sha256=0D8_CXe4xdgqlu2TWoT2pi35K9y2b4ilFj6LWD9Qk7w,1178
|
|
4
4
|
moleditpy_linux/modules/__init__.py,sha256=PFwvuEKXsgQlHTYL8oT5sTeo22gR0XmQcySp7kOTtqk,1396
|
|
5
5
|
moleditpy_linux/modules/about_dialog.py,sha256=zkR-w0vtqLH1Y0baQaoTb0tYmVamT4ztEMBdIANKKvc,3693
|
|
6
|
-
moleditpy_linux/modules/align_plane_dialog.py,sha256=
|
|
7
|
-
moleditpy_linux/modules/alignment_dialog.py,sha256=
|
|
6
|
+
moleditpy_linux/modules/align_plane_dialog.py,sha256=SEC32l8z2x1W1Sf1Gu001OQEtinuvqGuCGHRELmtU5s,11834
|
|
7
|
+
moleditpy_linux/modules/alignment_dialog.py,sha256=CPubSJI52mNSXmWt7oKQ2un5lXnN8_K4Z-LMM7wWTxQ,11309
|
|
8
8
|
moleditpy_linux/modules/analysis_window.py,sha256=zjP5ipSTpKw8oLr1eKdoxW8Bk1SslGlPqsVucD-x_5w,9403
|
|
9
|
-
moleditpy_linux/modules/angle_dialog.py,sha256=
|
|
9
|
+
moleditpy_linux/modules/angle_dialog.py,sha256=uc2WbvSfRe892xoEirqpZ78pf2Smwzkinkso6zLWr0Y,17751
|
|
10
10
|
moleditpy_linux/modules/atom_item.py,sha256=u8ge6B1M9sOGobfzg3tp1-EGXtEUmvdee7Fx6msg8Wk,15566
|
|
11
11
|
moleditpy_linux/modules/bond_item.py,sha256=eVkEeKvM4igYI67DYxpey3FllqDyt_iWDo4VPYMhaPk,19137
|
|
12
|
-
moleditpy_linux/modules/bond_length_dialog.py,sha256=
|
|
12
|
+
moleditpy_linux/modules/bond_length_dialog.py,sha256=6bFPGssnqlgINuqpxLv-OhjMH3_hspnaH8QtorAyu2M,14782
|
|
13
13
|
moleditpy_linux/modules/calculation_worker.py,sha256=KiGQY7i-QCQofEoE0r65KoQgpEGFcbhmxWv6egfkUdc,42324
|
|
14
14
|
moleditpy_linux/modules/color_settings_dialog.py,sha256=Ow44BhCOLo0AFb6klO001k6B4drOgKX9DeNBQhZLp5o,15474
|
|
15
|
-
moleditpy_linux/modules/constants.py,sha256=
|
|
16
|
-
moleditpy_linux/modules/constrained_optimization_dialog.py,sha256=
|
|
17
|
-
moleditpy_linux/modules/custom_interactor_style.py,sha256=
|
|
18
|
-
moleditpy_linux/modules/custom_qt_interactor.py,sha256=
|
|
19
|
-
moleditpy_linux/modules/dialog3_d_picking_mixin.py,sha256=
|
|
20
|
-
moleditpy_linux/modules/dihedral_dialog.py,sha256=
|
|
15
|
+
moleditpy_linux/modules/constants.py,sha256=_LJDuqJFfR_t_84zzl3p5dhK7C2LB-ZyFuBE4K0dUE4,4702
|
|
16
|
+
moleditpy_linux/modules/constrained_optimization_dialog.py,sha256=REsk4ePsqNmAGPMTS_jckeM7jexrU3krwun8sKqKUCs,30062
|
|
17
|
+
moleditpy_linux/modules/custom_interactor_style.py,sha256=LDNODMJoNHGe1AUSrvqv6PdeJm-hpPmSpWINppnJLt0,38942
|
|
18
|
+
moleditpy_linux/modules/custom_qt_interactor.py,sha256=vCZsDfRO-FtphD5cTP7Ps-5rpHZMIGloaoe6EaKzrsw,4139
|
|
19
|
+
moleditpy_linux/modules/dialog3_d_picking_mixin.py,sha256=z4udbkiX9PYmIGazPXsbftkk_oRRwZhcvlCqbyJzr24,6493
|
|
20
|
+
moleditpy_linux/modules/dihedral_dialog.py,sha256=bOTDO6-b74vEDn_z6OyuBr5cRz3RnRj83PiaEBUyWJA,18002
|
|
21
21
|
moleditpy_linux/modules/main_window.py,sha256=IL8dH3qPx2TkPgO7amuDgjlFoadh5J59xYUEVhlNZqA,36338
|
|
22
|
-
moleditpy_linux/modules/main_window_app_state.py,sha256=
|
|
22
|
+
moleditpy_linux/modules/main_window_app_state.py,sha256=8YDcGNCSpLTO1NGL9tEvNkXpUcS7JW-uK7TdUGvEqnk,35189
|
|
23
23
|
moleditpy_linux/modules/main_window_compute.py,sha256=ipIkhH_DONXDnPzh7xeym9X-Yfx8EhsvXYOdyxsAj4c,53347
|
|
24
|
-
moleditpy_linux/modules/main_window_dialog_manager.py,sha256=
|
|
24
|
+
moleditpy_linux/modules/main_window_dialog_manager.py,sha256=QR96LqHAPSOShXbc9cK-Ffq8a16JrXAoMKB0pHjESrQ,20072
|
|
25
25
|
moleditpy_linux/modules/main_window_edit_3d.py,sha256=CUArB5wcsgq1C7LygAEC6URlbnn4RhRYDa5n-Y-etWI,19731
|
|
26
|
-
moleditpy_linux/modules/main_window_edit_actions.py,sha256
|
|
26
|
+
moleditpy_linux/modules/main_window_edit_actions.py,sha256=yEc0Nw-VpN0P4e4neUu7pDuUHPGEcu6eFmwWFrSBIQ8,64815
|
|
27
27
|
moleditpy_linux/modules/main_window_export.py,sha256=dSVfylsybDDboDuXU9Inotf6YkrKJwgBTqGYSfq1lRE,38241
|
|
28
|
-
moleditpy_linux/modules/main_window_main_init.py,sha256=
|
|
28
|
+
moleditpy_linux/modules/main_window_main_init.py,sha256=Shxs8_vVa7PhfhVZdHYtPniItLHKJ3i9wcamcVofvso,91172
|
|
29
29
|
moleditpy_linux/modules/main_window_molecular_parsers.py,sha256=KR6vzuqc3nutOcorpYr0QOyX3MFBcxTwDhZX96VgJ9Q,48291
|
|
30
30
|
moleditpy_linux/modules/main_window_project_io.py,sha256=TWwtuKDuvgcvPZ9IGmW8r1EJJOrgxrIJRnxe_f4C1oM,17149
|
|
31
31
|
moleditpy_linux/modules/main_window_string_importers.py,sha256=v47wOd4RtjKYcF-aLP-mogGGdYTpTEo3dDyAu79_5MM,10782
|
|
32
32
|
moleditpy_linux/modules/main_window_ui_manager.py,sha256=HofI6T9EvcSSzPbsdPqkYEEDoB6Hui1Uj2Ll-wwczGA,24016
|
|
33
|
-
moleditpy_linux/modules/main_window_view_3d.py,sha256=
|
|
33
|
+
moleditpy_linux/modules/main_window_view_3d.py,sha256=CxZxyJHl2isF7KtyVWSI9f8LVbvdZM5H9Gnhm_8ovBM,74227
|
|
34
34
|
moleditpy_linux/modules/main_window_view_loaders.py,sha256=gklTMo27QnyJ8Gd0ampPdbm9d0Gi-oHWkIqQuGADHmI,14352
|
|
35
35
|
moleditpy_linux/modules/mirror_dialog.py,sha256=c3v4qY6R4FAljzk4EPaDjL9ZdZMjLQSFLqDMXz2fBUk,4696
|
|
36
36
|
moleditpy_linux/modules/molecular_data.py,sha256=8gE9ByYg3kSBfb1zANsyad_BVBTm6WOLF7NsZIYuG2E,13250
|
|
37
37
|
moleditpy_linux/modules/molecule_scene.py,sha256=khdt7h9Mk_D1cMbYeHGtq7P9aFXo0xG-hcShU_H2Y-Q,95911
|
|
38
|
-
moleditpy_linux/modules/move_group_dialog.py,sha256=
|
|
38
|
+
moleditpy_linux/modules/move_group_dialog.py,sha256=Fyuy3Uq1KsFsk9qR96r_FxPbAM_-zSfW2dsMQGv7btc,27276
|
|
39
39
|
moleditpy_linux/modules/periodic_table_dialog.py,sha256=ItEZUts1XCietz9paY-spvbzxh6SXak3GnikwqkHZCw,4006
|
|
40
|
-
moleditpy_linux/modules/planarize_dialog.py,sha256=
|
|
41
|
-
moleditpy_linux/modules/plugin_interface.py,sha256=
|
|
40
|
+
moleditpy_linux/modules/planarize_dialog.py,sha256=eaqI1MpF35e-VUMpJATt-EtGG5FhcSUlbAenUaFGabY,8593
|
|
41
|
+
moleditpy_linux/modules/plugin_interface.py,sha256=srzPZ3a_aRTx28NAvWNKRVUDYQNfQOTcjzx-5YW2Pb4,8164
|
|
42
42
|
moleditpy_linux/modules/plugin_manager.py,sha256=cxbqIE7Rb_KeBd-cG1vF2ySY2qNx8IJVRXjVPyQMFDc,13457
|
|
43
43
|
moleditpy_linux/modules/plugin_manager_window.py,sha256=UeoPQWTxmckoFIQAuv9jVBzok_9gEKEm9c7JsKRX0P4,10135
|
|
44
44
|
moleditpy_linux/modules/settings_dialog.py,sha256=Nr7yE8UmYRi3VObWvRlrnv0DnjSjmYXbvqryZ02O12k,65348
|
|
45
45
|
moleditpy_linux/modules/template_preview_item.py,sha256=djdq3tz73d_fJGOvai3E-V9Hk9q9ZW7skx7BV59mooA,6556
|
|
46
46
|
moleditpy_linux/modules/template_preview_view.py,sha256=4OCHZDO51BvJpKdfrBWJ4_4WfLfFSKxsVIyf7I-Kj2E,3350
|
|
47
|
-
moleditpy_linux/modules/translation_dialog.py,sha256=
|
|
47
|
+
moleditpy_linux/modules/translation_dialog.py,sha256=x_GJsbVk-cj4aN2KgmYWDRUDInFlXezAoYoTvX-OT30,14553
|
|
48
48
|
moleditpy_linux/modules/user_template_dialog.py,sha256=2hARO04DaILgdExx5ubL0GPsxK95VvVRqy7fNfudD_M,30843
|
|
49
49
|
moleditpy_linux/modules/zoomable_view.py,sha256=hjwljui13QpvjvxJHY4Evot4jMQvxRBQUNH5HUlyFOk,5966
|
|
50
|
+
moleditpy_linux/modules/assets/file_icon.ico,sha256=yyVj084A7HuMNbV073cE_Ag3Ne405qgOP3Mia1ZqLpE,101632
|
|
50
51
|
moleditpy_linux/modules/assets/icon.icns,sha256=wD5R6-Vw7K662tVKhu2E1ImN0oUuyAP4youesEQsn9c,139863
|
|
51
52
|
moleditpy_linux/modules/assets/icon.ico,sha256=RfgFcx7-dHY_2STdsOQCQziY5SNhDr3gPnjO6jzEDPI,147975
|
|
52
53
|
moleditpy_linux/modules/assets/icon.png,sha256=kCFN1WacYIdy0GN6SFEbNA00ef39pCczBnFdkkBI8Bs,147110
|
|
53
|
-
moleditpy_linux-2.2.
|
|
54
|
-
moleditpy_linux-2.2.
|
|
55
|
-
moleditpy_linux-2.2.
|
|
56
|
-
moleditpy_linux-2.2.
|
|
57
|
-
moleditpy_linux-2.2.
|
|
58
|
-
moleditpy_linux-2.2.
|
|
54
|
+
moleditpy_linux-2.2.6.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
55
|
+
moleditpy_linux-2.2.6.dist-info/METADATA,sha256=DRK5ZQ7Mw9fPPxg2JOD0qvNRSTXcKTnBWP8Krl0Q4tM,60708
|
|
56
|
+
moleditpy_linux-2.2.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
57
|
+
moleditpy_linux-2.2.6.dist-info/entry_points.txt,sha256=-OzipSi__yVwlimNtu3eiRP5t5UMg55Cs0udyhXYiyw,60
|
|
58
|
+
moleditpy_linux-2.2.6.dist-info/top_level.txt,sha256=qyqe-hDYL6CXyin9E5Me5rVl3PG84VqiOjf9bQvfJLs,16
|
|
59
|
+
moleditpy_linux-2.2.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|