MoleditPy-linux 4.3.1__tar.gz → 4.3.2__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-4.3.1 → moleditpy_linux-4.3.2}/PKG-INFO +1 -1
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/pyproject.toml +1 -1
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/MoleditPy_linux.egg-info/PKG-INFO +1 -1
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/custom_interactor_style.py +54 -6
- moleditpy_linux-4.3.2/src/moleditpy_linux/ui/custom_qt_interactor.py +75 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/dialog_3d_picking_mixin.py +2 -3
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/move_selected_atoms_dialog.py +6 -2
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/ui_manager.py +43 -1
- moleditpy_linux-4.3.1/src/moleditpy_linux/ui/custom_qt_interactor.py +0 -105
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/LICENSE +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/README.md +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/setup.cfg +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/MoleditPy_linux.egg-info/SOURCES.txt +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/MoleditPy_linux.egg-info/dependency_links.txt +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/MoleditPy_linux.egg-info/entry_points.txt +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/MoleditPy_linux.egg-info/requires.txt +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/MoleditPy_linux.egg-info/top_level.txt +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/__init__.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/__main__.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/assets/file_icon.ico +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/assets/icon.icns +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/assets/icon.ico +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/assets/icon.png +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/core/__init__.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/core/mol_geometry.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/core/molecular_data.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/main.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/plugins/__init__.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/plugins/plugin_interface.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/plugins/plugin_manager.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/plugins/plugin_manager_window.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/__init__.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/about_dialog.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/align_plane_dialog.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/alignment_dialog.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/analysis_window.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/angle_dialog.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/app_state.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/atom_item.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/atom_picking.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/base_picking_dialog.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/bond_item.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/bond_length_dialog.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/calculation_worker.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/color_settings_dialog.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/compute_logic.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/constrained_optimization_dialog.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/dialog_logic.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/dihedral_dialog.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/edit_3d_logic.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/edit_actions_logic.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/export_logic.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/geometry_base_dialog.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/io_logic.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/main_window.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/main_window_init.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/mirror_dialog.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/molecular_scene_handler.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/molecule_scene.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/move_group_dialog.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/periodic_table_dialog.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/planarize_dialog.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/plugin_menu_manager.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/settings_dialog.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/settings_tabs/__init__.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/settings_tabs/settings_2d_tab.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/settings_tabs/settings_3d_tabs.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/settings_tabs/settings_other_tab.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/settings_tabs/settings_tab_base.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/string_importers.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/template_preview_item.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/template_preview_view.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/translation_dialog.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/user_template_dialog.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/view_3d_logic.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/zoomable_view.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/utils/__init__.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/utils/constants.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/utils/default_settings.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/utils/sip_isdeleted_safe.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/utils/suppress_log.py +0 -0
- {moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/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: 4.3.
|
|
3
|
+
Version: 4.3.2
|
|
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: 4.3.
|
|
3
|
+
Version: 4.3.2
|
|
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-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/custom_interactor_style.py
RENAMED
|
@@ -159,9 +159,8 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
|
|
|
159
159
|
try:
|
|
160
160
|
move_group_dialog.on_atom_picked(clicked_atom_idx)
|
|
161
161
|
except (AttributeError, RuntimeError):
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
"Suppressed non-critical error", exc_info=True
|
|
162
|
+
logging.warning(
|
|
163
|
+
"Move-dialog atom toggle failed", exc_info=True
|
|
165
164
|
)
|
|
166
165
|
|
|
167
166
|
QTimer.singleShot(0, _deferred_toggle)
|
|
@@ -273,9 +272,8 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
|
|
|
273
272
|
closest_atom_idx
|
|
274
273
|
)
|
|
275
274
|
except (AttributeError, RuntimeError):
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
"Suppressed non-critical error", exc_info=True
|
|
275
|
+
logging.warning(
|
|
276
|
+
"Measurement selection failed", exc_info=True
|
|
279
277
|
)
|
|
280
278
|
|
|
281
279
|
QTimer.singleShot(0, _deferred_measure)
|
|
@@ -383,6 +381,54 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
|
|
|
383
381
|
# Standard right-click
|
|
384
382
|
super().OnRightButtonDown()
|
|
385
383
|
|
|
384
|
+
def _heal_stuck_pointer_state(self, move_group_dialog: Any) -> None:
|
|
385
|
+
"""Self-heal drag/rotate state whose release event was lost.
|
|
386
|
+
|
|
387
|
+
A release can be lost mid-gesture (e.g. pyvista temporarily swapping
|
|
388
|
+
the interactor style, a dialog grabbing events), leaving this style or
|
|
389
|
+
a Move Group dialog in a permanent drag/rotate state that blocks
|
|
390
|
+
interaction. Verify against the real button state on every move and
|
|
391
|
+
reset when the button is not actually held.
|
|
392
|
+
"""
|
|
393
|
+
try:
|
|
394
|
+
buttons = QApplication.mouseButtons()
|
|
395
|
+
left_held = bool(buttons & Qt.MouseButton.LeftButton)
|
|
396
|
+
right_held = bool(buttons & Qt.MouseButton.RightButton)
|
|
397
|
+
any_held = buttons != Qt.MouseButton.NoButton
|
|
398
|
+
except (AttributeError, RuntimeError, TypeError):
|
|
399
|
+
return
|
|
400
|
+
|
|
401
|
+
if self._is_dragging_atom and not left_held:
|
|
402
|
+
self.reset_interactor_state()
|
|
403
|
+
|
|
404
|
+
if not any_held:
|
|
405
|
+
try:
|
|
406
|
+
if self.GetState() != 0: # stuck ROTATE/PAN/etc. without a button
|
|
407
|
+
self.reset_interactor_state()
|
|
408
|
+
except (AttributeError, RuntimeError, TypeError):
|
|
409
|
+
# Safe defensive fallback catching AttributeError, RuntimeError, TypeError
|
|
410
|
+
logging.debug("Suppressed non-critical error", exc_info=True)
|
|
411
|
+
|
|
412
|
+
if move_group_dialog is not None:
|
|
413
|
+
try:
|
|
414
|
+
if (
|
|
415
|
+
getattr(move_group_dialog, "is_dragging_group_vtk", False)
|
|
416
|
+
and not left_held
|
|
417
|
+
):
|
|
418
|
+
move_group_dialog.is_dragging_group_vtk = False
|
|
419
|
+
move_group_dialog.drag_start_pos_vtk = None
|
|
420
|
+
move_group_dialog.mouse_moved_vtk = False
|
|
421
|
+
if (
|
|
422
|
+
getattr(move_group_dialog, "is_rotating_group_vtk", False)
|
|
423
|
+
and not right_held
|
|
424
|
+
):
|
|
425
|
+
move_group_dialog.is_rotating_group_vtk = False
|
|
426
|
+
move_group_dialog.rotation_start_pos = None
|
|
427
|
+
move_group_dialog.rotation_mouse_moved = False
|
|
428
|
+
except (AttributeError, RuntimeError, TypeError):
|
|
429
|
+
# Safe defensive fallback catching AttributeError, RuntimeError, TypeError
|
|
430
|
+
logging.debug("Suppressed non-critical error", exc_info=True)
|
|
431
|
+
|
|
386
432
|
def on_mouse_move(self, obj: Any, event: Any) -> None:
|
|
387
433
|
"""
|
|
388
434
|
Handle mouse move (drag vs camera/hover).
|
|
@@ -402,6 +448,8 @@ class CustomInteractorStyle(vtkInteractorStyleTrackballCamera):
|
|
|
402
448
|
break
|
|
403
449
|
except (AttributeError, RuntimeError, TypeError):
|
|
404
450
|
logging.warning("Caught exception in " + __file__, exc_info=True)
|
|
451
|
+
|
|
452
|
+
self._heal_stuck_pointer_state(move_group_dialog)
|
|
405
453
|
if move_group_dialog and getattr(
|
|
406
454
|
move_group_dialog, "is_dragging_group_vtk", False
|
|
407
455
|
):
|
|
@@ -0,0 +1,75 @@
|
|
|
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 logging
|
|
14
|
+
from typing import Any, Optional
|
|
15
|
+
|
|
16
|
+
from PyQt6.QtCore import QEvent
|
|
17
|
+
from PyQt6.QtGui import QMouseEvent
|
|
18
|
+
from pyvistaqt import QtInteractor
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CustomQtInteractor(QtInteractor):
|
|
22
|
+
"""PyVista QtInteractor subclass that exposes the main window for event handling."""
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
parent: Optional[Any] = None,
|
|
27
|
+
main_window: Optional[Any] = None,
|
|
28
|
+
**kwargs: Any,
|
|
29
|
+
) -> None:
|
|
30
|
+
super().__init__(parent, **kwargs)
|
|
31
|
+
self.main_window = main_window
|
|
32
|
+
|
|
33
|
+
def wheelEvent(self, event: Any) -> None:
|
|
34
|
+
"""
|
|
35
|
+
Override the mouse wheel event.
|
|
36
|
+
"""
|
|
37
|
+
# First call the parent class event to perform normal zoom processing
|
|
38
|
+
super().wheelEvent(event)
|
|
39
|
+
|
|
40
|
+
# Force focus back to the 2D view after zoom processing
|
|
41
|
+
if self.main_window and hasattr(self.main_window.init_manager, "view_2d"):
|
|
42
|
+
self.main_window.init_manager.view_2d.setFocus()
|
|
43
|
+
|
|
44
|
+
def mouseReleaseEvent(self, event: Any) -> None:
|
|
45
|
+
"""
|
|
46
|
+
Override the Qt mouse release event to return focus to the 2D view after
|
|
47
|
+
all 3D view operations.
|
|
48
|
+
"""
|
|
49
|
+
super().mouseReleaseEvent(event) # Process parent class event first
|
|
50
|
+
if self.main_window and hasattr(self.main_window.init_manager, "view_2d"):
|
|
51
|
+
self.main_window.init_manager.view_2d.setFocus()
|
|
52
|
+
|
|
53
|
+
def mouseDoubleClickEvent(self, event: Any) -> None:
|
|
54
|
+
"""Re-dispatch double-clicks as plain presses so fast clicking works.
|
|
55
|
+
|
|
56
|
+
Qt turns every second fast click into a double-click event. Forwarding
|
|
57
|
+
it unchanged would reach VTK with a repeat count and be dropped by the
|
|
58
|
+
interactor style, so the click would be lost (e.g. when rapidly
|
|
59
|
+
selecting atoms in measurement mode). Synthesizing a normal press keeps
|
|
60
|
+
press/release pairing intact and makes each fast click act as a click.
|
|
61
|
+
"""
|
|
62
|
+
try:
|
|
63
|
+
synthetic_press = QMouseEvent(
|
|
64
|
+
QEvent.Type.MouseButtonPress,
|
|
65
|
+
event.position(),
|
|
66
|
+
event.globalPosition(),
|
|
67
|
+
event.button(),
|
|
68
|
+
event.buttons(),
|
|
69
|
+
event.modifiers(),
|
|
70
|
+
)
|
|
71
|
+
super().mousePressEvent(synthetic_press)
|
|
72
|
+
event.accept()
|
|
73
|
+
except (AttributeError, RuntimeError, TypeError):
|
|
74
|
+
# Safe defensive fallback catching AttributeError, RuntimeError, TypeError
|
|
75
|
+
logging.debug("Suppressed non-critical error", exc_info=True)
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/dialog_3d_picking_mixin.py
RENAMED
|
@@ -82,9 +82,8 @@ class Dialog3DPickingMixin:
|
|
|
82
82
|
try:
|
|
83
83
|
self.on_atom_picked(int(closest_atom_idx))
|
|
84
84
|
except (AttributeError, RuntimeError):
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
"Suppressed non-critical error", exc_info=True
|
|
85
|
+
logging.warning(
|
|
86
|
+
"Dialog atom pick failed", exc_info=True
|
|
88
87
|
)
|
|
89
88
|
|
|
90
89
|
from PyQt6.QtCore import QTimer
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/move_selected_atoms_dialog.py
RENAMED
|
@@ -696,9 +696,13 @@ class MoveSelectedAtomsDialog(BasePickingDialog):
|
|
|
696
696
|
else:
|
|
697
697
|
btn.setText("Box Selection: OFF")
|
|
698
698
|
plotter.disable_picking()
|
|
699
|
-
# Restore original style
|
|
699
|
+
# Restore original style via pyvista's bookkeeping so its
|
|
700
|
+
# update_style() re-asserts ours instead of RubberBandPick
|
|
700
701
|
if self.original_style is not None:
|
|
701
|
-
|
|
702
|
+
try:
|
|
703
|
+
plotter.iren.style = self.original_style
|
|
704
|
+
except AttributeError:
|
|
705
|
+
plotter.interactor.SetInteractorStyle(self.original_style)
|
|
702
706
|
|
|
703
707
|
def on_rectangle_picked(self, selection: Any) -> None:
|
|
704
708
|
"""Handle PyVista rectangle picking callback."""
|
|
@@ -322,9 +322,51 @@ class UIManager(QObject):
|
|
|
322
322
|
style = CustomInteractorStyle(self.host)
|
|
323
323
|
|
|
324
324
|
# Set interactor style
|
|
325
|
-
self.
|
|
325
|
+
self._install_interactor_style(style)
|
|
326
326
|
self.host.view_3d_manager.plotter.interactor.Initialize()
|
|
327
327
|
|
|
328
|
+
# Watchdog: if anything silently replaces the custom style (observed as
|
|
329
|
+
# "rotation works but pick/select is dead"), report it and reinstall.
|
|
330
|
+
self._expected_style = style
|
|
331
|
+
self._style_watchdog = QTimer(self)
|
|
332
|
+
self._style_watchdog.setInterval(2000)
|
|
333
|
+
self._style_watchdog.timeout.connect(self._check_interactor_style)
|
|
334
|
+
self._style_watchdog.start()
|
|
335
|
+
|
|
336
|
+
def _install_interactor_style(self, style: Any) -> None:
|
|
337
|
+
"""Install *style* through pyvista's bookkeeping, not raw VTK.
|
|
338
|
+
|
|
339
|
+
pyvista's RenderWindowInteractor re-asserts its own ``_style_class``
|
|
340
|
+
via ``update_style()`` whenever its built-in double-click chart
|
|
341
|
+
handler fires (fast clicks!). Installing through the ``iren.style``
|
|
342
|
+
property keeps that bookkeeping pointing at our style so the
|
|
343
|
+
re-assert is a no-op instead of an eviction.
|
|
344
|
+
"""
|
|
345
|
+
plotter = self.host.view_3d_manager.plotter
|
|
346
|
+
try:
|
|
347
|
+
plotter.iren.style = style
|
|
348
|
+
except AttributeError:
|
|
349
|
+
plotter.interactor.SetInteractorStyle(style)
|
|
350
|
+
|
|
351
|
+
def _check_interactor_style(self) -> None:
|
|
352
|
+
"""Reinstall CustomInteractorStyle if it was silently replaced."""
|
|
353
|
+
try:
|
|
354
|
+
interactor = self.host.view_3d_manager.plotter.interactor
|
|
355
|
+
current = interactor.GetInteractorStyle()
|
|
356
|
+
if current is self._expected_style:
|
|
357
|
+
return
|
|
358
|
+
name = type(current).__name__
|
|
359
|
+
if "RubberBand" in name: # legitimate temporary box-selection style
|
|
360
|
+
return
|
|
361
|
+
logging.debug(
|
|
362
|
+
"3D interactor style was replaced by %s — reinstalling "
|
|
363
|
+
"CustomInteractorStyle",
|
|
364
|
+
name,
|
|
365
|
+
)
|
|
366
|
+
self._install_interactor_style(self._expected_style)
|
|
367
|
+
except (AttributeError, RuntimeError):
|
|
368
|
+
logging.debug("Style watchdog check failed", exc_info=True)
|
|
369
|
+
|
|
328
370
|
def handle_drag_enter_event(self, event: QDragEnterEvent) -> None:
|
|
329
371
|
"""Internal handler for drag enter event (bypasses PyQt type checks in tests)."""
|
|
330
372
|
mime_data = event.mimeData()
|
|
@@ -1,105 +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 time
|
|
14
|
-
from typing import Any, Optional
|
|
15
|
-
|
|
16
|
-
from pyvistaqt import QtInteractor
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class CustomQtInteractor(QtInteractor):
|
|
20
|
-
"""PyVista QtInteractor subclass that exposes the main window for event handling."""
|
|
21
|
-
|
|
22
|
-
def __init__(
|
|
23
|
-
self,
|
|
24
|
-
parent: Optional[Any] = None,
|
|
25
|
-
main_window: Optional[Any] = None,
|
|
26
|
-
**kwargs: Any,
|
|
27
|
-
) -> None:
|
|
28
|
-
super().__init__(parent, **kwargs)
|
|
29
|
-
self.main_window = main_window
|
|
30
|
-
self._last_click_time = 0.0
|
|
31
|
-
self._click_count = 0
|
|
32
|
-
self._ignore_next_release = False
|
|
33
|
-
|
|
34
|
-
def wheelEvent(self, event: Any) -> None:
|
|
35
|
-
"""
|
|
36
|
-
Override the mouse wheel event.
|
|
37
|
-
"""
|
|
38
|
-
# First call the parent class event to perform normal zoom processing
|
|
39
|
-
super().wheelEvent(event)
|
|
40
|
-
|
|
41
|
-
# Force focus back to the 2D view after zoom processing
|
|
42
|
-
if self.main_window and hasattr(self.main_window.init_manager, "view_2d"):
|
|
43
|
-
self.main_window.init_manager.view_2d.setFocus()
|
|
44
|
-
|
|
45
|
-
def mouseReleaseEvent(self, event: Any) -> None:
|
|
46
|
-
"""
|
|
47
|
-
Override the Qt mouse release event to return focus to the 2D view after
|
|
48
|
-
all 3D view operations. Also filters out "Ghost Release" (release without
|
|
49
|
-
a corresponding press).
|
|
50
|
-
"""
|
|
51
|
-
if self._ignore_next_release:
|
|
52
|
-
self._ignore_next_release = False
|
|
53
|
-
event.accept()
|
|
54
|
-
return
|
|
55
|
-
|
|
56
|
-
super().mouseReleaseEvent(event) # Process parent class event first
|
|
57
|
-
if self.main_window and hasattr(self.main_window.init_manager, "view_2d"):
|
|
58
|
-
self.main_window.init_manager.view_2d.setFocus()
|
|
59
|
-
|
|
60
|
-
def mousePressEvent(self, event: Any) -> None:
|
|
61
|
-
"""
|
|
62
|
-
Custom mouse press handling to track accumulated clicks and filter out
|
|
63
|
-
triple-clicks.
|
|
64
|
-
"""
|
|
65
|
-
current_time = time.time()
|
|
66
|
-
# Reset count if too much time has passed (0.5s is standard double-click time)
|
|
67
|
-
if current_time - self._last_click_time > 0.5:
|
|
68
|
-
self._click_count = 0
|
|
69
|
-
|
|
70
|
-
self._click_count += 1
|
|
71
|
-
self._last_click_time = current_time
|
|
72
|
-
|
|
73
|
-
# If this is the 3rd click (or more), swallow it to prevent
|
|
74
|
-
# the internal state desync that happens with rapid clicking sequences.
|
|
75
|
-
if self._click_count >= 3:
|
|
76
|
-
self._ignore_next_release = True
|
|
77
|
-
event.accept()
|
|
78
|
-
return
|
|
79
|
-
|
|
80
|
-
super().mousePressEvent(event)
|
|
81
|
-
|
|
82
|
-
def mouseDoubleClickEvent(self, event: Any) -> None:
|
|
83
|
-
"""Ignore mouse double-clicks on the 3D widget to avoid accidental actions.
|
|
84
|
-
|
|
85
|
-
Swallow the double-click event so it doesn't trigger selection, editing,
|
|
86
|
-
or camera jumps. We intentionally do not call the superclass handler.
|
|
87
|
-
Crucially, we also flag the NEXT release event to be swallowed, preventing
|
|
88
|
-
a "Ghost Release" (Release without Press) from reaching VTK.
|
|
89
|
-
"""
|
|
90
|
-
current_time = time.time()
|
|
91
|
-
self._last_click_time = current_time
|
|
92
|
-
# Set to 2 to ensure the next click counts as 3rd
|
|
93
|
-
if current_time - self._last_click_time < 0.5:
|
|
94
|
-
self._click_count = 2
|
|
95
|
-
else:
|
|
96
|
-
self._click_count = 2 # Force sync
|
|
97
|
-
|
|
98
|
-
self._ignore_next_release = True
|
|
99
|
-
|
|
100
|
-
try:
|
|
101
|
-
# Accept the event to mark it handled and prevent further processing.
|
|
102
|
-
event.accept()
|
|
103
|
-
except (AttributeError, RuntimeError, TypeError):
|
|
104
|
-
# If event doesn't support accept for some reason, just return.
|
|
105
|
-
return
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/MoleditPy_linux.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/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-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/plugins/plugin_interface.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/plugins/plugin_manager.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/plugins/plugin_manager_window.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/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
|
|
File without changes
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/base_picking_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/bond_length_dialog.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/calculation_worker.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/color_settings_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/edit_actions_logic.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/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-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/molecular_scene_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/periodic_table_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/plugin_menu_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/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-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/template_preview_item.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/template_preview_view.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/ui/translation_dialog.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/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-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/utils/default_settings.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.3.1 → moleditpy_linux-4.3.2}/src/moleditpy_linux/utils/sip_isdeleted_safe.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|