MoleditPy 3.0.0a1__tar.gz → 3.0.0a2__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.
Files changed (69) hide show
  1. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/PKG-INFO +4 -4
  2. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/README.md +3 -3
  3. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/pyproject.toml +1 -1
  4. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/MoleditPy.egg-info/PKG-INFO +4 -4
  5. moleditpy-3.0.0a2/src/moleditpy/main.py +60 -0
  6. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/__init__.py +2 -0
  7. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/about_dialog.py +115 -110
  8. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/align_plane_dialog.py +11 -3
  9. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/alignment_dialog.py +10 -2
  10. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/angle_dialog.py +515 -480
  11. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/atom_item.py +6 -5
  12. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/bond_item.py +11 -7
  13. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/bond_length_dialog.py +455 -423
  14. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/calculation_worker.py +510 -184
  15. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/color_settings_dialog.py +155 -27
  16. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/constants.py +2 -2
  17. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/constrained_optimization_dialog.py +23 -11
  18. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/custom_interactor_style.py +821 -784
  19. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/dialog_3d_picking_mixin.py +1 -1
  20. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/dihedral_dialog.py +50 -19
  21. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/main_window.py +19 -10
  22. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/main_window_app_state.py +213 -210
  23. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/main_window_compute.py +129 -70
  24. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/main_window_dialog_manager.py +4 -4
  25. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/main_window_edit_3d.py +20 -8
  26. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/main_window_edit_actions.py +24 -9
  27. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/main_window_export.py +42 -35
  28. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/main_window_main_init.py +1919 -1704
  29. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/main_window_molecular_parsers.py +109 -49
  30. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/main_window_project_io.py +393 -384
  31. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/main_window_string_importers.py +1 -6
  32. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/main_window_ui_manager.py +29 -12
  33. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/main_window_view_3d.py +103 -53
  34. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/main_window_view_loaders.py +48 -17
  35. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/mirror_dialog.py +3 -2
  36. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/mol_geometry.py +12 -12
  37. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/molecular_data.py +427 -427
  38. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/molecular_scene_handler.py +319 -288
  39. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/molecule_scene.py +113 -85
  40. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/move_group_dialog.py +705 -653
  41. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/periodic_table_dialog.py +1 -1
  42. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/planarize_dialog.py +8 -2
  43. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/plugin_manager.py +73 -16
  44. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/plugin_manager_window.py +1 -1
  45. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/settings_dialog.py +1705 -1729
  46. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/system_utils.py +3 -2
  47. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/translation_dialog.py +18 -8
  48. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/user_template_dialog.py +1 -1
  49. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/zoomable_view.py +3 -1
  50. moleditpy-3.0.0a1/src/moleditpy/main.py +0 -34
  51. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/LICENSE +0 -0
  52. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/setup.cfg +0 -0
  53. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/MoleditPy.egg-info/SOURCES.txt +0 -0
  54. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/MoleditPy.egg-info/dependency_links.txt +0 -0
  55. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/MoleditPy.egg-info/entry_points.txt +0 -0
  56. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/MoleditPy.egg-info/requires.txt +0 -0
  57. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/MoleditPy.egg-info/top_level.txt +0 -0
  58. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/__init__.py +0 -0
  59. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/__main__.py +0 -0
  60. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/assets/file_icon.ico +0 -0
  61. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/assets/icon.icns +0 -0
  62. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/assets/icon.ico +0 -0
  63. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/assets/icon.png +0 -0
  64. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/analysis_window.py +0 -0
  65. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/custom_qt_interactor.py +0 -0
  66. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/plugin_interface.py +0 -0
  67. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/sip_isdeleted_safe.py +0 -0
  68. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/template_preview_item.py +0 -0
  69. {moleditpy-3.0.0a1 → moleditpy-3.0.0a2}/src/moleditpy/modules/template_preview_view.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MoleditPy
3
- Version: 3.0.0a1
3
+ Version: 3.0.0a2
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
@@ -710,10 +710,10 @@ Dynamic: license-file
710
710
  [![Python Versions](https://img.shields.io/pypi/pyversions/MoleditPy.svg)](https://pypi.org/project/MoleditPy/)
711
711
  [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
712
712
  [![Build Status](https://github.com/HiroYokoyama/python_molecular_editor/actions/workflows/tests.yml/badge.svg)](https://github.com/HiroYokoyama/python_molecular_editor/actions)
713
- ![Core Logic Coverage](https://img.shields.io/badge/core_logic_coverage-72%25-green)
714
- ![Overall Coverage](https://img.shields.io/badge/coverage-50%25-orange)
713
+ ![Core Logic Coverage](https://img.shields.io/badge/core_logic_coverage-80%25-green)
714
+ ![Overall Coverage](https://img.shields.io/badge/coverage-63.83%25-green)
715
715
  ![GUI Status](https://img.shields.io/badge/GUI-Manually_Verified-blue)
716
- ![Pylint Score](https://img.shields.io/badge/pylint-8.64%2F10-brightgreen)
716
+ ![Pylint Score](https://img.shields.io/badge/pylint-8.93%2F10-brightgreen)
717
717
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/moleditpy?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/moleditpy)
718
718
 
719
719
  [🇯🇵 日本語 (Japanese)](#japanese)
@@ -6,10 +6,10 @@
6
6
  [![Python Versions](https://img.shields.io/pypi/pyversions/MoleditPy.svg)](https://pypi.org/project/MoleditPy/)
7
7
  [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
8
8
  [![Build Status](https://github.com/HiroYokoyama/python_molecular_editor/actions/workflows/tests.yml/badge.svg)](https://github.com/HiroYokoyama/python_molecular_editor/actions)
9
- ![Core Logic Coverage](https://img.shields.io/badge/core_logic_coverage-72%25-green)
10
- ![Overall Coverage](https://img.shields.io/badge/coverage-50%25-orange)
9
+ ![Core Logic Coverage](https://img.shields.io/badge/core_logic_coverage-80%25-green)
10
+ ![Overall Coverage](https://img.shields.io/badge/coverage-63.83%25-green)
11
11
  ![GUI Status](https://img.shields.io/badge/GUI-Manually_Verified-blue)
12
- ![Pylint Score](https://img.shields.io/badge/pylint-8.64%2F10-brightgreen)
12
+ ![Pylint Score](https://img.shields.io/badge/pylint-8.93%2F10-brightgreen)
13
13
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/moleditpy?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/moleditpy)
14
14
 
15
15
  [🇯🇵 日本語 (Japanese)](#japanese)
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  name = "MoleditPy"
7
7
 
8
- version = "3.0.0a1"
8
+ version = "3.0.0a2"
9
9
 
10
10
  license = {file = "LICENSE"}
11
11
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MoleditPy
3
- Version: 3.0.0a1
3
+ Version: 3.0.0a2
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
@@ -710,10 +710,10 @@ Dynamic: license-file
710
710
  [![Python Versions](https://img.shields.io/pypi/pyversions/MoleditPy.svg)](https://pypi.org/project/MoleditPy/)
711
711
  [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
712
712
  [![Build Status](https://github.com/HiroYokoyama/python_molecular_editor/actions/workflows/tests.yml/badge.svg)](https://github.com/HiroYokoyama/python_molecular_editor/actions)
713
- ![Core Logic Coverage](https://img.shields.io/badge/core_logic_coverage-72%25-green)
714
- ![Overall Coverage](https://img.shields.io/badge/coverage-50%25-orange)
713
+ ![Core Logic Coverage](https://img.shields.io/badge/core_logic_coverage-80%25-green)
714
+ ![Overall Coverage](https://img.shields.io/badge/coverage-63.83%25-green)
715
715
  ![GUI Status](https://img.shields.io/badge/GUI-Manually_Verified-blue)
716
- ![Pylint Score](https://img.shields.io/badge/pylint-8.64%2F10-brightgreen)
716
+ ![Pylint Score](https://img.shields.io/badge/pylint-8.93%2F10-brightgreen)
717
717
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/moleditpy?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/moleditpy)
718
718
 
719
719
  [🇯🇵 日本語 (Japanese)](#japanese)
@@ -0,0 +1,60 @@
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 ctypes
14
+ import sys
15
+ import argparse
16
+ import logging
17
+
18
+ from PyQt6.QtWidgets import QApplication
19
+
20
+ try:
21
+ from .modules.main_window import MainWindow
22
+ except ImportError:
23
+ from modules.main_window import MainWindow
24
+
25
+
26
+ def setup_logging():
27
+ """Configure global logging to standard output."""
28
+ logging.basicConfig(
29
+ level=logging.INFO,
30
+ format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
31
+ stream=sys.stdout,
32
+ )
33
+
34
+
35
+ def main():
36
+ # Setup logging as early as possible
37
+ setup_logging()
38
+
39
+ # --- Additional handling for Windows taskbar icon ---
40
+ if sys.platform == "win32":
41
+ myappid = "hyoko.moleditpy.1.0" # Application-specific ID (arbitrary)
42
+ ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
43
+
44
+ parser = argparse.ArgumentParser(
45
+ prog="moleditpy", description="MoleditPy molecular editor"
46
+ )
47
+ parser.add_argument("file", nargs="?", default=None, help="File to open on startup")
48
+ parser.add_argument(
49
+ "--safe",
50
+ action="store_true",
51
+ default=False,
52
+ help="Start in safe mode: skip loading all plugins",
53
+ )
54
+ # parse_known_args so Qt's own argv flags (e.g. -platform) are passed through
55
+ args, remaining = parser.parse_known_args()
56
+
57
+ app = QApplication([sys.argv[0]] + remaining)
58
+ window = MainWindow(initial_file=args.file, safe_mode=args.safe)
59
+ window.show()
60
+ sys.exit(app.exec())
@@ -12,12 +12,14 @@ DOI: 10.5281/zenodo.17268532
12
12
 
13
13
  try:
14
14
  import importlib.util
15
+
15
16
  OBABEL_AVAILABLE = importlib.util.find_spec("openbabel") is not None
16
17
  except ImportError:
17
18
  OBABEL_AVAILABLE = False
18
19
 
19
20
  try:
20
21
  from PyQt6 import sip as _sip # type: ignore
22
+
21
23
  _sip_isdeleted = getattr(_sip, "isdeleted", None)
22
24
  except ImportError:
23
25
  _sip = None
@@ -1,110 +1,115 @@
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 os
14
-
15
- from PyQt6.QtCore import Qt
16
- from PyQt6.QtGui import QCursor, QPainter, QPen, QPixmap
17
- from PyQt6.QtWidgets import QDialog, QHBoxLayout, QLabel, QPushButton, QVBoxLayout
18
-
19
- try:
20
- from .constants import VERSION
21
- except ImportError:
22
- from modules.constants import VERSION
23
-
24
-
25
- class AboutDialog(QDialog):
26
- def __init__(self, main_window, parent=None):
27
- super().__init__(parent)
28
- self.main_window = main_window
29
- self.setWindowTitle("About MoleditPy")
30
- self.setFixedSize(250, 300)
31
- self.init_ui()
32
-
33
- def init_ui(self):
34
- layout = QVBoxLayout(self)
35
-
36
- # Create a clickable image label
37
- self.image_label = QLabel()
38
- self.image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
39
-
40
- # Load the original icon image
41
- icon_path = os.path.join(os.path.dirname(__file__), "..", "assets", "icon.png")
42
- if os.path.exists(icon_path):
43
- original_pixmap = QPixmap(icon_path)
44
- # Scale to 2x size (160x160)
45
- pixmap = original_pixmap.scaled(
46
- 160,
47
- 160,
48
- Qt.AspectRatioMode.KeepAspectRatio,
49
- Qt.TransformationMode.SmoothTransformation,
50
- )
51
- else:
52
- # Fallback: create a simple placeholder if icon.png not found
53
- pixmap = QPixmap(160, 160)
54
- pixmap.fill(Qt.GlobalColor.lightGray)
55
- painter = QPainter(pixmap)
56
- painter.setPen(QPen(Qt.GlobalColor.black, 2))
57
- painter.drawText(pixmap.rect(), Qt.AlignmentFlag.AlignCenter, "MoleditPy")
58
- painter.end()
59
-
60
- self.image_label.setPixmap(pixmap)
61
- try:
62
- self.image_label.setCursor(QCursor(Qt.CursorShape.ArrowCursor))
63
- except (AttributeError, RuntimeError, ValueError, TypeError):
64
- pass # Suppress cursor setting errors on about image
65
-
66
- self.image_label.mousePressEvent = self.image_mouse_press_event
67
-
68
- layout.addWidget(self.image_label)
69
-
70
- # Add text information
71
- info_text = f"MoleditPy Ver. {VERSION}\nAuthor: Hiromichi Yokoyama\nLicense: GPL-3.0 license"
72
- info_label = QLabel(info_text)
73
- info_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
74
- layout.addWidget(info_label)
75
-
76
- # Add OK button
77
- ok_button = QPushButton("OK")
78
- ok_button.setFixedSize(80, 30) # Fixed small size
79
- ok_button.clicked.connect(self.accept)
80
-
81
- # Center the button
82
- button_layout = QHBoxLayout()
83
- button_layout.addStretch()
84
- button_layout.addWidget(ok_button)
85
- button_layout.addStretch()
86
- layout.addLayout(button_layout)
87
-
88
- def image_clicked(self, event):
89
- """Easter egg: Clear all and load bipyrimidine from SMILES"""
90
- # Clear the current scene
91
- self.main_window.clear_all()
92
-
93
- bipyrimidine_smiles = "C1=CN=C(N=C1)C2=NC=CC=N2"
94
- self.main_window.load_from_smiles(bipyrimidine_smiles)
95
-
96
- # Close the dialog
97
- self.accept()
98
-
99
- def image_mouse_press_event(self, event):
100
- """Handle mouse press on the image: trigger easter egg only for right-click."""
101
- try:
102
- if event.button() == Qt.MouseButton.RightButton:
103
- self.image_clicked(event)
104
- else:
105
- event.ignore()
106
- except (AttributeError, RuntimeError, ValueError, TypeError):
107
- try:
108
- event.ignore()
109
- except (AttributeError, RuntimeError, ValueError, TypeError):
110
- pass # Suppress nested event errors
1
+ import logging
2
+ #!/usr/bin/env python3
3
+ # -*- coding: utf-8 -*-
4
+
5
+ """
6
+ MoleditPy — A Python-based molecular editing software
7
+
8
+ Author: Hiromichi Yokoyama
9
+ License: GPL-3.0 license
10
+ Repo: https://github.com/HiroYokoyama/python_molecular_editor
11
+ DOI: 10.5281/zenodo.17268532
12
+ """
13
+
14
+ import os
15
+
16
+ from PyQt6.QtCore import Qt
17
+ from PyQt6.QtGui import QCursor, QPainter, QPen, QPixmap
18
+ from PyQt6.QtWidgets import QDialog, QHBoxLayout, QLabel, QPushButton, QVBoxLayout
19
+
20
+ try:
21
+ from .constants import VERSION
22
+ except ImportError:
23
+ from modules.constants import VERSION
24
+
25
+
26
+ class AboutDialog(QDialog):
27
+ def __init__(self, main_window, parent=None):
28
+ super().__init__(parent)
29
+ self.main_window = main_window
30
+ self.setWindowTitle("About MoleditPy")
31
+ self.setFixedSize(250, 300)
32
+ self.init_ui()
33
+
34
+ def init_ui(self):
35
+ layout = QVBoxLayout(self)
36
+
37
+ # Create a clickable image label
38
+ self.image_label = QLabel()
39
+ self.image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
40
+
41
+ # Load the original icon image
42
+ icon_path = os.path.join(os.path.dirname(__file__), "..", "assets", "icon.png")
43
+ if os.path.exists(icon_path):
44
+ original_pixmap = QPixmap(icon_path)
45
+ # Scale to 2x size (160x160)
46
+ pixmap = original_pixmap.scaled(
47
+ 160,
48
+ 160,
49
+ Qt.AspectRatioMode.KeepAspectRatio,
50
+ Qt.TransformationMode.SmoothTransformation,
51
+ )
52
+ else:
53
+ # Fallback: create a simple placeholder if icon.png not found
54
+ pixmap = QPixmap(160, 160)
55
+ pixmap.fill(Qt.GlobalColor.lightGray)
56
+ painter = QPainter(pixmap)
57
+ painter.setPen(QPen(Qt.GlobalColor.black, 2))
58
+ painter.drawText(pixmap.rect(), Qt.AlignmentFlag.AlignCenter, "MoleditPy")
59
+ painter.end()
60
+
61
+ self.image_label.setPixmap(pixmap)
62
+ try:
63
+ self.image_label.setCursor(QCursor(Qt.CursorShape.ArrowCursor))
64
+ except (AttributeError, RuntimeError, ValueError, TypeError) as e:
65
+ logging.debug(
66
+ f"Suppressed exception: {e}"
67
+ ) # Suppress cursor setting errors on about image
68
+
69
+ self.image_label.mousePressEvent = self.image_mouse_press_event
70
+
71
+ layout.addWidget(self.image_label)
72
+
73
+ # Add text information
74
+ info_text = f"MoleditPy Ver. {VERSION}\nAuthor: Hiromichi Yokoyama\nLicense: GPL-3.0 license"
75
+ info_label = QLabel(info_text)
76
+ info_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
77
+ layout.addWidget(info_label)
78
+
79
+ # Add OK button
80
+ ok_button = QPushButton("OK")
81
+ ok_button.setFixedSize(80, 30) # Fixed small size
82
+ ok_button.clicked.connect(self.accept)
83
+
84
+ # Center the button
85
+ button_layout = QHBoxLayout()
86
+ button_layout.addStretch()
87
+ button_layout.addWidget(ok_button)
88
+ button_layout.addStretch()
89
+ layout.addLayout(button_layout)
90
+
91
+ def image_clicked(self, event):
92
+ """Easter egg: Clear all and load bipyrimidine from SMILES"""
93
+ # Clear the current scene
94
+ self.main_window.clear_all()
95
+
96
+ bipyrimidine_smiles = "C1=CN=C(N=C1)C2=NC=CC=N2"
97
+ self.main_window.load_from_smiles(bipyrimidine_smiles)
98
+
99
+ # Close the dialog
100
+ self.accept()
101
+
102
+ def image_mouse_press_event(self, event):
103
+ """Handle mouse press on the image: trigger easter egg only for right-click."""
104
+ try:
105
+ if event.button() == Qt.MouseButton.RightButton:
106
+ self.image_clicked(event)
107
+ else:
108
+ event.ignore()
109
+ except (AttributeError, RuntimeError, ValueError, TypeError):
110
+ try:
111
+ event.ignore()
112
+ except (AttributeError, RuntimeError, ValueError, TypeError) as e:
113
+ logging.debug(
114
+ f"Suppressed exception: {e}"
115
+ ) # Suppress nested event errors
@@ -20,6 +20,7 @@ from PyQt6.QtWidgets import (
20
20
  QPushButton,
21
21
  QVBoxLayout,
22
22
  )
23
+ from rdkit import Geometry
23
24
 
24
25
  try:
25
26
  from .dialog_3d_picking_mixin import Dialog3DPickingMixin
@@ -27,7 +28,7 @@ except ImportError:
27
28
  from modules.dialog_3d_picking_mixin import Dialog3DPickingMixin
28
29
 
29
30
 
30
- class AlignPlaneDialog(Dialog3DPickingMixin, QDialog):
31
+ class AlignPlaneDialog(Dialog3DPickingMixin, QDialog):
31
32
  def __init__(self, mol, main_window, plane, preselected_atoms=None, parent=None):
32
33
  QDialog.__init__(self, parent)
33
34
  Dialog3DPickingMixin.__init__(self)
@@ -218,7 +219,9 @@ class AlignPlaneDialog(Dialog3DPickingMixin, QDialog):
218
219
  eigenvalues, eigenvectors = np.linalg.eigh(cov_matrix)
219
220
 
220
221
  # Normal vector of the plane corresponds to the smallest eigenvalue
221
- normal_vector = eigenvectors[:, 0] # Normal vector of the plane corresponds to the smallest eigenvalue
222
+ normal_vector = eigenvectors[
223
+ :, 0
224
+ ] # Normal vector of the plane corresponds to the smallest eigenvalue
222
225
 
223
226
  # Define target plane normal vector
224
227
  if self.plane == "xy":
@@ -264,7 +267,12 @@ class AlignPlaneDialog(Dialog3DPickingMixin, QDialog):
264
267
  centered_pos, rotation_axis, rotation_angle
265
268
  )
266
269
  new_pos = rotated_pos + centroid
267
- conf.SetAtomPosition(i, new_pos.tolist())
270
+ conf.SetAtomPosition(
271
+ i,
272
+ Geometry.Point3D(
273
+ float(new_pos[0]), float(new_pos[1]), float(new_pos[2])
274
+ ),
275
+ )
268
276
  self.main_window.atom_positions_3d[i] = new_pos
269
277
 
270
278
  # Update 3D visualization
@@ -19,6 +19,7 @@ from PyQt6.QtWidgets import (
19
19
  QPushButton,
20
20
  QVBoxLayout,
21
21
  )
22
+ from rdkit import Geometry
22
23
 
23
24
  try:
24
25
  from .dialog_3d_picking_mixin import Dialog3DPickingMixin
@@ -26,7 +27,7 @@ except ImportError:
26
27
  from modules.dialog_3d_picking_mixin import Dialog3DPickingMixin
27
28
 
28
29
 
29
- class AlignmentDialog(Dialog3DPickingMixin, QDialog):
30
+ class AlignmentDialog(Dialog3DPickingMixin, QDialog):
30
31
  def __init__(self, mol, main_window, axis, preselected_atoms=None, parent=None):
31
32
  QDialog.__init__(self, parent)
32
33
  Dialog3DPickingMixin.__init__(self)
@@ -221,7 +222,14 @@ class AlignmentDialog(Dialog3DPickingMixin, QDialog):
221
222
  rotated_pos = rodrigues_rotation(
222
223
  current_pos, rotation_axis, rotation_angle
223
224
  )
224
- conf.SetAtomPosition(i, rotated_pos.tolist())
225
+ conf.SetAtomPosition(
226
+ i,
227
+ Geometry.Point3D(
228
+ float(rotated_pos[0]),
229
+ float(rotated_pos[1]),
230
+ float(rotated_pos[2]),
231
+ ),
232
+ )
225
233
 
226
234
  # Update 3D positions
227
235
  self.main_window.atom_positions_3d = np.array(