MoleditPy 3.3.0__tar.gz → 3.3.1__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 (78) hide show
  1. {moleditpy-3.3.0 → moleditpy-3.3.1}/PKG-INFO +20 -2
  2. {moleditpy-3.3.0 → moleditpy-3.3.1}/README.md +19 -1
  3. {moleditpy-3.3.0 → moleditpy-3.3.1}/pyproject.toml +1 -1
  4. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/MoleditPy.egg-info/PKG-INFO +20 -2
  5. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/__init__.py +2 -0
  6. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/atom_picking.py +163 -163
  7. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/view_3d_logic.py +1 -1
  8. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/utils/constants.py +31 -1
  9. {moleditpy-3.3.0 → moleditpy-3.3.1}/LICENSE +0 -0
  10. {moleditpy-3.3.0 → moleditpy-3.3.1}/setup.cfg +0 -0
  11. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/MoleditPy.egg-info/SOURCES.txt +0 -0
  12. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/MoleditPy.egg-info/dependency_links.txt +0 -0
  13. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/MoleditPy.egg-info/entry_points.txt +0 -0
  14. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/MoleditPy.egg-info/requires.txt +0 -0
  15. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/MoleditPy.egg-info/top_level.txt +0 -0
  16. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/__main__.py +0 -0
  17. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/assets/file_icon.ico +0 -0
  18. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/assets/icon.icns +0 -0
  19. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/assets/icon.ico +0 -0
  20. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/assets/icon.png +0 -0
  21. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/core/__init__.py +0 -0
  22. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/core/mol_geometry.py +0 -0
  23. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/core/molecular_data.py +0 -0
  24. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/main.py +0 -0
  25. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/plugins/__init__.py +0 -0
  26. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/plugins/plugin_interface.py +0 -0
  27. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/plugins/plugin_manager.py +0 -0
  28. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/plugins/plugin_manager_window.py +0 -0
  29. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/__init__.py +0 -0
  30. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/about_dialog.py +0 -0
  31. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/align_plane_dialog.py +0 -0
  32. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/alignment_dialog.py +0 -0
  33. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/analysis_window.py +0 -0
  34. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/angle_dialog.py +0 -0
  35. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/app_state.py +0 -0
  36. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/atom_item.py +0 -0
  37. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/base_picking_dialog.py +0 -0
  38. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/bond_item.py +0 -0
  39. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/bond_length_dialog.py +0 -0
  40. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/calculation_worker.py +0 -0
  41. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/color_settings_dialog.py +0 -0
  42. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/compute_logic.py +0 -0
  43. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/constrained_optimization_dialog.py +0 -0
  44. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/custom_interactor_style.py +0 -0
  45. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/custom_qt_interactor.py +0 -0
  46. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/dialog_3d_picking_mixin.py +0 -0
  47. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/dialog_logic.py +0 -0
  48. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/dihedral_dialog.py +0 -0
  49. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/edit_3d_logic.py +0 -0
  50. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/edit_actions_logic.py +0 -0
  51. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/export_logic.py +0 -0
  52. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/geometry_base_dialog.py +0 -0
  53. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/io_logic.py +0 -0
  54. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/main_window.py +0 -0
  55. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/main_window_init.py +0 -0
  56. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/mirror_dialog.py +0 -0
  57. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/molecular_scene_handler.py +0 -0
  58. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/molecule_scene.py +0 -0
  59. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/move_group_dialog.py +0 -0
  60. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/periodic_table_dialog.py +0 -0
  61. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/planarize_dialog.py +0 -0
  62. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/settings_dialog.py +0 -0
  63. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/settings_tabs/__init__.py +0 -0
  64. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/settings_tabs/settings_2d_tab.py +0 -0
  65. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/settings_tabs/settings_3d_tabs.py +0 -0
  66. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/settings_tabs/settings_other_tab.py +0 -0
  67. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/settings_tabs/settings_tab_base.py +0 -0
  68. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/string_importers.py +0 -0
  69. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/template_preview_item.py +0 -0
  70. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/template_preview_view.py +0 -0
  71. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/translation_dialog.py +0 -0
  72. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/ui_manager.py +0 -0
  73. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/user_template_dialog.py +0 -0
  74. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/ui/zoomable_view.py +0 -0
  75. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/utils/__init__.py +0 -0
  76. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/utils/default_settings.py +0 -0
  77. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/utils/sip_isdeleted_safe.py +0 -0
  78. {moleditpy-3.3.0 → moleditpy-3.3.1}/src/moleditpy/utils/system_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MoleditPy
3
- Version: 3.3.0
3
+ Version: 3.3.1
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
@@ -713,8 +713,10 @@ Dynamic: license-file
713
713
  ![Core Logic Coverage](https://img.shields.io/badge/core_logic_coverage->80%25-green)
714
714
  ![Overall Coverage](https://img.shields.io/badge/coverage->75%25-green)
715
715
  ![GUI Status](https://img.shields.io/badge/GUI-Manually_Verified-blue)
716
- ![Pylint Score](https://img.shields.io/badge/pylint-9%2F10-brightgreen)
716
+ ![Pylint Score](https://img.shields.io/badge/pylint->9%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
+ [![PyPI Downloads](https://static.pepy.tech/personalized-badge/moleditpy?period=monthly&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=monthly+downloads)](https://pepy.tech/projects/moleditpy)
719
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/HiroYokoyama/python_molecular_editor)
718
720
 
719
721
  [🇯🇵 日本語 (Japanese)](#japanese)
720
722
 
@@ -840,6 +842,14 @@ moleditpy
840
842
 
841
843
  This project is licensed under the **GNU General Public License v3.0 (GPL-v3)**. See the `LICENSE` file for details.
842
844
 
845
+ ## Citation
846
+
847
+ If you use this software in your work, please cite it as follows:
848
+
849
+ ```
850
+ Yokoyama, H. (2026). MoleditPy — A Python-based molecular editing software. Zenodo. https://doi.org/10.5281/zenodo.17268532
851
+ ```
852
+
843
853
  -----
844
854
 
845
855
  <div id="japanese"></div>
@@ -963,3 +973,11 @@ moleditpy
963
973
  ## ライセンス
964
974
 
965
975
  このプロジェクトは **GNU General Public License v3.0 (GPL-v3)** のもとで公開されています。詳細は `LICENSE` ファイルを参照してください。
976
+
977
+ ## 引用
978
+
979
+ 本ソフトウェアを研究で使用される場合は、以下の通り引用を明記してください。
980
+
981
+ ```
982
+ Yokoyama, H. (2026). MoleditPy — A Python-based molecular editing software. Zenodo. https://doi.org/10.5281/zenodo.17268532
983
+ ```
@@ -9,8 +9,10 @@
9
9
  ![Core Logic Coverage](https://img.shields.io/badge/core_logic_coverage->80%25-green)
10
10
  ![Overall Coverage](https://img.shields.io/badge/coverage->75%25-green)
11
11
  ![GUI Status](https://img.shields.io/badge/GUI-Manually_Verified-blue)
12
- ![Pylint Score](https://img.shields.io/badge/pylint-9%2F10-brightgreen)
12
+ ![Pylint Score](https://img.shields.io/badge/pylint->9%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
+ [![PyPI Downloads](https://static.pepy.tech/personalized-badge/moleditpy?period=monthly&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=monthly+downloads)](https://pepy.tech/projects/moleditpy)
15
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/HiroYokoyama/python_molecular_editor)
14
16
 
15
17
  [🇯🇵 日本語 (Japanese)](#japanese)
16
18
 
@@ -136,6 +138,14 @@ moleditpy
136
138
 
137
139
  This project is licensed under the **GNU General Public License v3.0 (GPL-v3)**. See the `LICENSE` file for details.
138
140
 
141
+ ## Citation
142
+
143
+ If you use this software in your work, please cite it as follows:
144
+
145
+ ```
146
+ Yokoyama, H. (2026). MoleditPy — A Python-based molecular editing software. Zenodo. https://doi.org/10.5281/zenodo.17268532
147
+ ```
148
+
139
149
  -----
140
150
 
141
151
  <div id="japanese"></div>
@@ -259,3 +269,11 @@ moleditpy
259
269
  ## ライセンス
260
270
 
261
271
  このプロジェクトは **GNU General Public License v3.0 (GPL-v3)** のもとで公開されています。詳細は `LICENSE` ファイルを参照してください。
272
+
273
+ ## 引用
274
+
275
+ 本ソフトウェアを研究で使用される場合は、以下の通り引用を明記してください。
276
+
277
+ ```
278
+ Yokoyama, H. (2026). MoleditPy — A Python-based molecular editing software. Zenodo. https://doi.org/10.5281/zenodo.17268532
279
+ ```
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  name = "MoleditPy"
7
7
 
8
- version = "3.3.0"
8
+ version = "3.3.1"
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.3.0
3
+ Version: 3.3.1
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
@@ -713,8 +713,10 @@ Dynamic: license-file
713
713
  ![Core Logic Coverage](https://img.shields.io/badge/core_logic_coverage->80%25-green)
714
714
  ![Overall Coverage](https://img.shields.io/badge/coverage->75%25-green)
715
715
  ![GUI Status](https://img.shields.io/badge/GUI-Manually_Verified-blue)
716
- ![Pylint Score](https://img.shields.io/badge/pylint-9%2F10-brightgreen)
716
+ ![Pylint Score](https://img.shields.io/badge/pylint->9%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
+ [![PyPI Downloads](https://static.pepy.tech/personalized-badge/moleditpy?period=monthly&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=monthly+downloads)](https://pepy.tech/projects/moleditpy)
719
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/HiroYokoyama/python_molecular_editor)
718
720
 
719
721
  [🇯🇵 日本語 (Japanese)](#japanese)
720
722
 
@@ -840,6 +842,14 @@ moleditpy
840
842
 
841
843
  This project is licensed under the **GNU General Public License v3.0 (GPL-v3)**. See the `LICENSE` file for details.
842
844
 
845
+ ## Citation
846
+
847
+ If you use this software in your work, please cite it as follows:
848
+
849
+ ```
850
+ Yokoyama, H. (2026). MoleditPy — A Python-based molecular editing software. Zenodo. https://doi.org/10.5281/zenodo.17268532
851
+ ```
852
+
843
853
  -----
844
854
 
845
855
  <div id="japanese"></div>
@@ -963,3 +973,11 @@ moleditpy
963
973
  ## ライセンス
964
974
 
965
975
  このプロジェクトは **GNU General Public License v3.0 (GPL-v3)** のもとで公開されています。詳細は `LICENSE` ファイルを参照してください。
976
+
977
+ ## 引用
978
+
979
+ 本ソフトウェアを研究で使用される場合は、以下の通り引用を明記してください。
980
+
981
+ ```
982
+ Yokoyama, H. (2026). MoleditPy — A Python-based molecular editing software. Zenodo. https://doi.org/10.5281/zenodo.17268532
983
+ ```
@@ -18,3 +18,5 @@ try:
18
18
  OBABEL_AVAILABLE = importlib.util.find_spec("openbabel") is not None
19
19
  except ImportError:
20
20
  OBABEL_AVAILABLE = False
21
+
22
+ from .utils.constants import VERSION as __version__
@@ -1,163 +1,163 @@
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
- from __future__ import annotations
14
-
15
- from typing import Any, Optional
16
-
17
- import numpy as np
18
-
19
- try:
20
- from ..utils.constants import VDW_RADII, pt
21
- except ImportError:
22
- from moleditpy.utils.constants import VDW_RADII, pt
23
-
24
-
25
- def _world_to_display(renderer: Any, pos: Any) -> Optional[tuple[float, float, float]]:
26
- try:
27
- renderer.SetWorldPoint(float(pos[0]), float(pos[1]), float(pos[2]), 1.0)
28
- renderer.WorldToDisplay()
29
- display = renderer.GetDisplayPoint()
30
- return (float(display[0]), float(display[1]), float(display[2]))
31
- except (AttributeError, RuntimeError, TypeError, ValueError, IndexError):
32
- return None
33
-
34
-
35
- def _atom_world_radius(view_3d_manager: Any, mol: Any, atom_idx: int) -> float:
36
- try:
37
- atom = mol.GetAtomWithIdx(int(atom_idx))
38
- symbol = atom.GetSymbol()
39
- except (AttributeError, RuntimeError, TypeError, ValueError):
40
- symbol = "C"
41
-
42
- settings = {}
43
- try:
44
- settings = view_3d_manager.host.init_manager.settings
45
- except (AttributeError, RuntimeError, TypeError):
46
- pass
47
-
48
- style = str(getattr(view_3d_manager, "current_3d_style", "ball_and_stick"))
49
- style = style.lower().replace(" ", "_")
50
-
51
- if style == "cpk":
52
- scale = settings.get("cpk_atom_scale", 1.0)
53
- try:
54
- radius = pt.GetRvdw(pt.GetAtomicNumber(symbol))
55
- return float(radius if radius > 0.1 else 1.5) * float(scale)
56
- except (AttributeError, RuntimeError, TypeError, ValueError):
57
- return 1.5 * float(scale)
58
-
59
- if style == "stick":
60
- return float(settings.get("stick_bond_radius", 0.15))
61
-
62
- if style == "wireframe":
63
- return 0.01
64
-
65
- scale = settings.get("ball_stick_atom_scale", 1.0)
66
- return float(VDW_RADII.get(symbol, 0.4)) * float(scale)
67
-
68
-
69
- def _projected_radius_px(
70
- renderer: Any, center: Any, world_radius: float
71
- ) -> Optional[float]:
72
- center_display = _world_to_display(renderer, center)
73
- if center_display is None:
74
- return None
75
-
76
- offsets = (
77
- (world_radius, 0.0, 0.0),
78
- (0.0, world_radius, 0.0),
79
- (0.0, 0.0, world_radius),
80
- )
81
- radius_px = 0.0
82
- for offset in offsets:
83
- edge = (
84
- float(center[0]) + offset[0],
85
- float(center[1]) + offset[1],
86
- float(center[2]) + offset[2],
87
- )
88
- edge_display = _world_to_display(renderer, edge)
89
- if edge_display is None:
90
- continue
91
- radius_px = max(
92
- radius_px,
93
- float(
94
- np.hypot(
95
- edge_display[0] - center_display[0],
96
- edge_display[1] - center_display[1],
97
- )
98
- ),
99
- )
100
-
101
- return radius_px
102
-
103
-
104
- def pick_atom_index_from_screen(
105
- view_3d_manager: Any,
106
- click_pos: tuple[int, int],
107
- mol: Optional[Any] = None,
108
- padding_px: float = 8.0,
109
- min_radius_px: float = 14.0,
110
- max_radius_px: float = 96.0,
111
- ) -> Optional[int]:
112
- """Return the atom nearest a screen click without invoking VTK cell picking."""
113
- try:
114
- plotter = view_3d_manager.plotter
115
- renderer = plotter.renderer
116
- positions = view_3d_manager.atom_positions_3d
117
- except (AttributeError, RuntimeError, TypeError):
118
- return None
119
-
120
- if positions is None:
121
- return None
122
-
123
- try:
124
- positions_array = np.asarray(positions, dtype=float)
125
- except (TypeError, ValueError):
126
- return None
127
-
128
- if positions_array.ndim != 2 or positions_array.shape[1] < 3:
129
- return None
130
-
131
- if mol is None:
132
- mol = getattr(view_3d_manager, "current_mol", None)
133
-
134
- try:
135
- atom_count = int(mol.GetNumAtoms()) if mol is not None else len(positions_array)
136
- except (AttributeError, RuntimeError, TypeError, ValueError):
137
- atom_count = len(positions_array)
138
-
139
- best_idx: Optional[int] = None
140
- best_score: Optional[tuple[float, float]] = None
141
-
142
- for atom_idx in range(min(atom_count, len(positions_array))):
143
- center = positions_array[atom_idx]
144
- display = _world_to_display(renderer, center)
145
- if display is None or not np.all(np.isfinite(display[:2])):
146
- continue
147
-
148
- world_radius = _atom_world_radius(view_3d_manager, mol, atom_idx)
149
- projected_radius = _projected_radius_px(renderer, center, world_radius)
150
- hit_radius = max(
151
- float(min_radius_px),
152
- min(float(max_radius_px), float(projected_radius or 0.0) + padding_px),
153
- )
154
- distance = float(np.hypot(display[0] - click_pos[0], display[1] - click_pos[1]))
155
- if distance > hit_radius:
156
- continue
157
-
158
- score = (distance / hit_radius, distance)
159
- if best_score is None or score < best_score:
160
- best_idx = atom_idx
161
- best_score = score
162
-
163
- return best_idx
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
+ from __future__ import annotations
14
+
15
+ from typing import Any, Optional
16
+
17
+ import numpy as np
18
+
19
+ try:
20
+ from ..utils.constants import VDW_RADII, pt
21
+ except ImportError:
22
+ from moleditpy.utils.constants import VDW_RADII, pt
23
+
24
+
25
+ def _world_to_display(renderer: Any, pos: Any) -> Optional[tuple[float, float, float]]:
26
+ try:
27
+ renderer.SetWorldPoint(float(pos[0]), float(pos[1]), float(pos[2]), 1.0)
28
+ renderer.WorldToDisplay()
29
+ display = renderer.GetDisplayPoint()
30
+ return (float(display[0]), float(display[1]), float(display[2]))
31
+ except (AttributeError, RuntimeError, TypeError, ValueError, IndexError):
32
+ return None
33
+
34
+
35
+ def _atom_world_radius(view_3d_manager: Any, mol: Any, atom_idx: int) -> float:
36
+ try:
37
+ atom = mol.GetAtomWithIdx(int(atom_idx))
38
+ symbol = atom.GetSymbol()
39
+ except (AttributeError, RuntimeError, TypeError, ValueError):
40
+ symbol = "C"
41
+
42
+ settings = {}
43
+ try:
44
+ settings = view_3d_manager.host.init_manager.settings
45
+ except (AttributeError, RuntimeError, TypeError):
46
+ pass
47
+
48
+ style = str(getattr(view_3d_manager, "current_3d_style", "ball_and_stick"))
49
+ style = style.lower().replace(" ", "_")
50
+
51
+ if style == "cpk":
52
+ scale = settings.get("cpk_atom_scale", 1.0)
53
+ try:
54
+ radius = pt.GetRvdw(pt.GetAtomicNumber(symbol))
55
+ return float(radius if radius > 0.1 else 1.5) * float(scale)
56
+ except (AttributeError, RuntimeError, TypeError, ValueError):
57
+ return 1.5 * float(scale)
58
+
59
+ if style == "stick":
60
+ return float(settings.get("stick_bond_radius", 0.15))
61
+
62
+ if style == "wireframe":
63
+ return 0.01
64
+
65
+ scale = settings.get("ball_stick_atom_scale", 1.0)
66
+ return float(VDW_RADII.get(symbol, 0.4)) * float(scale)
67
+
68
+
69
+ def _projected_radius_px(
70
+ renderer: Any, center: Any, world_radius: float
71
+ ) -> Optional[float]:
72
+ center_display = _world_to_display(renderer, center)
73
+ if center_display is None:
74
+ return None
75
+
76
+ offsets = (
77
+ (world_radius, 0.0, 0.0),
78
+ (0.0, world_radius, 0.0),
79
+ (0.0, 0.0, world_radius),
80
+ )
81
+ radius_px = 0.0
82
+ for offset in offsets:
83
+ edge = (
84
+ float(center[0]) + offset[0],
85
+ float(center[1]) + offset[1],
86
+ float(center[2]) + offset[2],
87
+ )
88
+ edge_display = _world_to_display(renderer, edge)
89
+ if edge_display is None:
90
+ continue
91
+ radius_px = max(
92
+ radius_px,
93
+ float(
94
+ np.hypot(
95
+ edge_display[0] - center_display[0],
96
+ edge_display[1] - center_display[1],
97
+ )
98
+ ),
99
+ )
100
+
101
+ return radius_px
102
+
103
+
104
+ def pick_atom_index_from_screen(
105
+ view_3d_manager: Any,
106
+ click_pos: tuple[int, int],
107
+ mol: Optional[Any] = None,
108
+ padding_px: float = 8.0,
109
+ min_radius_px: float = 14.0,
110
+ max_radius_px: float = 96.0,
111
+ ) -> Optional[int]:
112
+ """Return the atom nearest a screen click without invoking VTK cell picking."""
113
+ try:
114
+ plotter = view_3d_manager.plotter
115
+ renderer = plotter.renderer
116
+ positions = view_3d_manager.atom_positions_3d
117
+ except (AttributeError, RuntimeError, TypeError):
118
+ return None
119
+
120
+ if positions is None:
121
+ return None
122
+
123
+ try:
124
+ positions_array = np.asarray(positions, dtype=float)
125
+ except (TypeError, ValueError):
126
+ return None
127
+
128
+ if positions_array.ndim != 2 or positions_array.shape[1] < 3:
129
+ return None
130
+
131
+ if mol is None:
132
+ mol = getattr(view_3d_manager, "current_mol", None)
133
+
134
+ try:
135
+ atom_count = int(mol.GetNumAtoms()) if mol is not None else len(positions_array)
136
+ except (AttributeError, RuntimeError, TypeError, ValueError):
137
+ atom_count = len(positions_array)
138
+
139
+ best_idx: Optional[int] = None
140
+ best_score: Optional[tuple[float, float]] = None
141
+
142
+ for atom_idx in range(min(atom_count, len(positions_array))):
143
+ center = positions_array[atom_idx]
144
+ display = _world_to_display(renderer, center)
145
+ if display is None or not np.all(np.isfinite(display[:2])):
146
+ continue
147
+
148
+ world_radius = _atom_world_radius(view_3d_manager, mol, atom_idx)
149
+ projected_radius = _projected_radius_px(renderer, center, world_radius)
150
+ hit_radius = max(
151
+ float(min_radius_px),
152
+ min(float(max_radius_px), float(projected_radius or 0.0) + padding_px),
153
+ )
154
+ distance = float(np.hypot(display[0] - click_pos[0], display[1] - click_pos[1]))
155
+ if distance > hit_radius:
156
+ continue
157
+
158
+ score = (distance / hit_radius, distance)
159
+ if best_score is None or score < best_score:
160
+ best_idx = atom_idx
161
+ best_score = score
162
+
163
+ return best_idx
@@ -1632,7 +1632,7 @@ class View3DManager:
1632
1632
 
1633
1633
  # Color definitions (dark blue/green/red)
1634
1634
  rdkit_color = "#003366" # Dark blue
1635
- id_color = "#006400" # Dark green
1635
+ id_color = "#009000" # Green
1636
1636
  xyz_color = "#8B0000" # Dark red
1637
1637
  other_color = "black"
1638
1638
 
@@ -12,11 +12,41 @@ DOI: 10.5281/zenodo.17268532
12
12
 
13
13
  # --- Constants ---
14
14
 
15
+ import os
15
16
  from PyQt6.QtGui import QColor, QFont
16
17
  from rdkit import Chem
17
18
 
19
+
20
+ def _get_version():
21
+ try:
22
+ from importlib.metadata import version, PackageNotFoundError
23
+
24
+ try:
25
+ return version("MoleditPy")
26
+ except PackageNotFoundError:
27
+ pass
28
+ except ImportError:
29
+ pass
30
+
31
+ try:
32
+ # Fallback: Parse pyproject.toml directly
33
+ current_dir = os.path.abspath(os.path.dirname(__file__))
34
+ for _ in range(5):
35
+ pyproject_path = os.path.join(current_dir, "pyproject.toml")
36
+ if os.path.exists(pyproject_path):
37
+ with open(pyproject_path, "r", encoding="utf-8") as f:
38
+ for line in f:
39
+ if line.strip().startswith("version ="):
40
+ return line.split("=")[1].strip().strip('"').strip("'")
41
+ current_dir = os.path.dirname(current_dir)
42
+ except Exception:
43
+ pass
44
+
45
+ return "Unknown"
46
+
47
+
18
48
  # Version
19
- VERSION = "3.3.0"
49
+ VERSION = _get_version()
20
50
 
21
51
  ATOM_RADIUS = 18
22
52
  BOND_OFFSET = 3.5
File without changes
File without changes