MoleditPy-linux 4.1.1__tar.gz → 4.1.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.1.1 → moleditpy_linux-4.1.2}/PKG-INFO +1 -1
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/pyproject.toml +1 -1
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/MoleditPy_linux.egg-info/PKG-INFO +1 -1
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/plugins/plugin_interface.py +11 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/io_logic.py +184 -111
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/utils/constants.py +5 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/LICENSE +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/README.md +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/setup.cfg +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/MoleditPy_linux.egg-info/SOURCES.txt +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/MoleditPy_linux.egg-info/dependency_links.txt +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/MoleditPy_linux.egg-info/entry_points.txt +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/MoleditPy_linux.egg-info/requires.txt +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/MoleditPy_linux.egg-info/top_level.txt +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/__init__.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/__main__.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/assets/file_icon.ico +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/assets/icon.icns +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/assets/icon.ico +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/assets/icon.png +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/core/__init__.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/core/mol_geometry.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/core/molecular_data.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/main.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/plugins/__init__.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/plugins/plugin_manager.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/plugins/plugin_manager_window.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/__init__.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/about_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/align_plane_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/alignment_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/analysis_window.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/angle_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/app_state.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/atom_item.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/atom_picking.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/base_picking_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/bond_item.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/bond_length_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/calculation_worker.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/color_settings_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/compute_logic.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/constrained_optimization_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/custom_interactor_style.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/custom_qt_interactor.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/dialog_3d_picking_mixin.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/dialog_logic.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/dihedral_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/edit_3d_logic.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/edit_actions_logic.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/export_logic.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/geometry_base_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/main_window.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/main_window_init.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/mirror_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/molecular_scene_handler.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/molecule_scene.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/move_group_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/move_selected_atoms_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/periodic_table_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/planarize_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/plugin_menu_manager.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/settings_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/settings_tabs/__init__.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/settings_tabs/settings_2d_tab.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/settings_tabs/settings_3d_tabs.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/settings_tabs/settings_other_tab.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/settings_tabs/settings_tab_base.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/string_importers.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/template_preview_item.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/template_preview_view.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/translation_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/ui_manager.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/user_template_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/view_3d_logic.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/zoomable_view.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/utils/__init__.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/utils/default_settings.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/utils/sip_isdeleted_safe.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.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.1.
|
|
3
|
+
Version: 4.1.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.1.
|
|
3
|
+
Version: 4.1.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.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/plugins/plugin_interface.py
RENAMED
|
@@ -499,6 +499,17 @@ class PluginContext:
|
|
|
499
499
|
if mw and hasattr(mw, "string_importer_manager"):
|
|
500
500
|
mw.string_importer_manager.load_from_smiles(smiles)
|
|
501
501
|
|
|
502
|
+
def show_xyz_data(
|
|
503
|
+
self, xyz_text: str, source_name: str = "XYZ data"
|
|
504
|
+
) -> Optional[Any]:
|
|
505
|
+
"""Display XYZ text in the 3D viewer and return the loaded RDKit Mol."""
|
|
506
|
+
mw = self.get_main_window()
|
|
507
|
+
if mw and hasattr(mw, "io_manager"):
|
|
508
|
+
show = getattr(mw.io_manager, "show_xyz_data", None)
|
|
509
|
+
if show is not None:
|
|
510
|
+
return show(xyz_text, source_name=source_name)
|
|
511
|
+
return None
|
|
512
|
+
|
|
502
513
|
def to_xyz_block(self) -> Optional[str]:
|
|
503
514
|
"""Return the current 3D structure as an XYZ block (only element x y z lines)."""
|
|
504
515
|
mol = self.current_mol
|
|
@@ -34,7 +34,7 @@ from PyQt6.QtWidgets import (
|
|
|
34
34
|
from rdkit import Chem
|
|
35
35
|
from rdkit.Chem import AllChem, rdGeometry, rdMolTransforms, Descriptors
|
|
36
36
|
|
|
37
|
-
from ..utils.constants import COVALENT_RADII, VERSION
|
|
37
|
+
from ..utils.constants import COVALENT_RADII, DUMMY_XYZ_SYMBOLS, VERSION
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
class IOManager:
|
|
@@ -84,139 +84,210 @@ class IOManager:
|
|
|
84
84
|
lines[3] = self.fix_mol_counts_line(lines[3])
|
|
85
85
|
return "\n".join(lines)
|
|
86
86
|
|
|
87
|
-
def
|
|
88
|
-
"""
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
def _normalize_xyz_symbol(self, raw_symbol: str) -> Tuple[str, bool]:
|
|
88
|
+
"""Return the RDKit symbol for an XYZ atom and whether it is a dummy."""
|
|
89
|
+
stripped = raw_symbol.strip()
|
|
90
|
+
if ":" in stripped:
|
|
91
|
+
return "*", True
|
|
92
|
+
if stripped.upper() in DUMMY_XYZ_SYMBOLS:
|
|
93
|
+
return "*", True
|
|
94
|
+
symbol = stripped.capitalize()
|
|
95
|
+
try:
|
|
96
|
+
atomic_num = Chem.GetPeriodicTable().GetAtomicNumber(symbol)
|
|
97
|
+
except (RuntimeError, ValueError, TypeError):
|
|
98
|
+
return "*", True
|
|
99
|
+
if atomic_num <= 0:
|
|
100
|
+
return "*", True
|
|
101
|
+
return symbol, False
|
|
102
|
+
|
|
103
|
+
def _mol_from_xyz_lines(self, raw_lines: list[str]) -> Any:
|
|
104
|
+
"""Create an RDKit molecule from XYZ text lines."""
|
|
105
|
+
lines = [ln.strip() for ln in raw_lines if not ln.strip().startswith("#")]
|
|
106
|
+
while lines and not lines[0]:
|
|
107
|
+
lines.pop(0)
|
|
108
|
+
|
|
109
|
+
if not lines:
|
|
110
|
+
raise ValueError("XYZ file format error: too few lines")
|
|
111
|
+
|
|
112
|
+
atom_start = 2
|
|
92
113
|
try:
|
|
93
|
-
with open(file_path, "r", encoding="utf-8") as f:
|
|
94
|
-
raw_lines = f.readlines()
|
|
95
|
-
|
|
96
|
-
lines = [ln.strip() for ln in raw_lines if not ln.strip().startswith("#")]
|
|
97
|
-
while lines and not lines[0]:
|
|
98
|
-
lines.pop(0)
|
|
99
|
-
|
|
100
114
|
if len(lines) < 2:
|
|
101
115
|
raise ValueError("XYZ file format error: too few lines")
|
|
102
|
-
|
|
103
116
|
num_atoms = int(lines[0])
|
|
104
117
|
if num_atoms == 0:
|
|
105
118
|
raise ValueError("XYZ file has zero atoms")
|
|
119
|
+
except ValueError as exc:
|
|
120
|
+
# Not a standard headed XYZ — treat all lines as atom rows
|
|
121
|
+
if "zero atoms" in str(exc) or "too few" in str(exc):
|
|
122
|
+
raise
|
|
123
|
+
num_atoms = len(lines)
|
|
124
|
+
atom_start = 0
|
|
125
|
+
|
|
126
|
+
atoms_data = []
|
|
127
|
+
has_dummy_atoms = False
|
|
128
|
+
atom_lines = lines[atom_start : atom_start + num_atoms]
|
|
129
|
+
if len(atom_lines) < num_atoms:
|
|
130
|
+
raise ValueError("XYZ file format error: fewer atom rows than expected")
|
|
131
|
+
|
|
132
|
+
for i, line in enumerate(atom_lines):
|
|
133
|
+
parts = line.split()
|
|
134
|
+
if len(parts) < 4:
|
|
135
|
+
raise ValueError(f"Invalid atom data at line {atom_start + i + 1}")
|
|
136
|
+
raw_symbol = parts[0]
|
|
137
|
+
symbol, is_dummy = self._normalize_xyz_symbol(raw_symbol)
|
|
138
|
+
has_dummy_atoms = has_dummy_atoms or is_dummy
|
|
139
|
+
atoms_data.append(
|
|
140
|
+
(symbol, float(parts[1]), float(parts[2]), float(parts[3]))
|
|
141
|
+
)
|
|
106
142
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
parts = line.split()
|
|
110
|
-
if len(parts) < 4:
|
|
111
|
-
raise ValueError(f"Invalid atom data at line {i + 3}")
|
|
112
|
-
symbol = parts[0].capitalize()
|
|
113
|
-
try:
|
|
114
|
-
Chem.Atom(symbol)
|
|
115
|
-
except (RuntimeError, ValueError):
|
|
116
|
-
settings = self.host.init_manager.settings
|
|
117
|
-
if settings.get("skip_chemistry_checks", False):
|
|
118
|
-
symbol = "C"
|
|
119
|
-
else:
|
|
120
|
-
raise ValueError(f"Unrecognized element symbol: {parts[0]}")
|
|
121
|
-
atoms_data.append(
|
|
122
|
-
(symbol, float(parts[1]), float(parts[2]), float(parts[3]))
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
if not atoms_data:
|
|
126
|
-
raise ValueError("No valid atoms found in XYZ file")
|
|
143
|
+
if not atoms_data:
|
|
144
|
+
raise ValueError("No valid atoms found in XYZ file")
|
|
127
145
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
mol.
|
|
146
|
+
mol = Chem.RWMol()
|
|
147
|
+
conf = Chem.Conformer(len(atoms_data))
|
|
148
|
+
for i, (symbol, x, y, z) in enumerate(atoms_data):
|
|
149
|
+
atom = Chem.Atom(symbol)
|
|
150
|
+
atom.SetIntProp("xyz_unique_id", i)
|
|
151
|
+
if atom.GetAtomicNum() == 0:
|
|
152
|
+
atom.SetProp("xyz_original_symbol", atom_lines[i].split()[0])
|
|
153
|
+
mol.AddAtom(atom)
|
|
154
|
+
conf.SetAtomPosition(i, rdGeometry.Point3D(x, y, z))
|
|
155
|
+
mol.AddConformer(conf)
|
|
136
156
|
|
|
137
|
-
|
|
138
|
-
|
|
157
|
+
settings = self.host.init_manager.settings
|
|
158
|
+
skip_checks = bool(settings.get("skip_chemistry_checks", False))
|
|
139
159
|
|
|
140
|
-
|
|
160
|
+
def _set_prop(m: Chem.Mol, key: str, val: Any) -> None:
|
|
161
|
+
try:
|
|
162
|
+
if isinstance(val, int):
|
|
163
|
+
m.SetIntProp(key, val)
|
|
164
|
+
elif isinstance(val, float):
|
|
165
|
+
m.SetDoubleProp(key, val)
|
|
166
|
+
except (RuntimeError, TypeError, ValueError):
|
|
167
|
+
# Safe defensive fallback catching RuntimeError, TypeError, ValueError
|
|
168
|
+
pass
|
|
169
|
+
|
|
170
|
+
def _process(charge_val: int, use_rd_determine: bool = True) -> Any:
|
|
171
|
+
if use_rd_determine:
|
|
141
172
|
try:
|
|
142
|
-
|
|
143
|
-
m.SetIntProp(key, val)
|
|
144
|
-
elif isinstance(val, float):
|
|
145
|
-
m.SetDoubleProp(key, val)
|
|
146
|
-
except (RuntimeError, TypeError, ValueError):
|
|
147
|
-
# Safe defensive fallback catching RuntimeError, TypeError, ValueError
|
|
148
|
-
pass
|
|
149
|
-
|
|
150
|
-
def _process(charge_val: int, use_rd_determine: bool = True) -> Any:
|
|
151
|
-
if use_rd_determine:
|
|
152
|
-
try:
|
|
153
|
-
from rdkit.Chem import rdDetermineBonds
|
|
173
|
+
from rdkit.Chem import rdDetermineBonds
|
|
154
174
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
_set_prop(candidate, "_xyz_charge", charge_val)
|
|
159
|
-
return candidate
|
|
160
|
-
except (RuntimeError, ValueError, TypeError) as e:
|
|
161
|
-
raise e
|
|
162
|
-
else:
|
|
163
|
-
self.estimate_bonds_from_distances(mol)
|
|
164
|
-
candidate = mol.GetMol()
|
|
175
|
+
mol_copy = Chem.RWMol(mol)
|
|
176
|
+
rdDetermineBonds.DetermineBonds(mol_copy, charge=charge_val)
|
|
177
|
+
candidate = mol_copy.GetMol()
|
|
165
178
|
_set_prop(candidate, "_xyz_charge", charge_val)
|
|
166
179
|
return candidate
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
final_mol = _process(0, use_rd_determine=False)
|
|
170
|
-
_set_prop(final_mol, "_xyz_skip_checks", 1)
|
|
180
|
+
except (RuntimeError, ValueError, TypeError):
|
|
181
|
+
raise
|
|
171
182
|
else:
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
183
|
+
self.estimate_bonds_from_distances(mol)
|
|
184
|
+
candidate = mol.GetMol()
|
|
185
|
+
_set_prop(candidate, "_xyz_charge", charge_val)
|
|
186
|
+
return candidate
|
|
187
|
+
|
|
188
|
+
if skip_checks or has_dummy_atoms:
|
|
189
|
+
final_mol = _process(0, use_rd_determine=False)
|
|
190
|
+
_set_prop(final_mol, "_xyz_skip_checks", 1)
|
|
191
|
+
else:
|
|
192
|
+
final_mol = None
|
|
193
|
+
# First try with charge 0 (per user's 'first try with 0 then ask' requirement)
|
|
194
|
+
# but only if "Always ask" is not explicitly enabled in settings.
|
|
195
|
+
if not settings.get("always_ask_charge", False):
|
|
196
|
+
try:
|
|
197
|
+
final_mol = _process(0, use_rd_determine=True)
|
|
198
|
+
except (RuntimeError, ValueError, TypeError):
|
|
199
|
+
final_mol = None
|
|
200
|
+
|
|
201
|
+
# If still no final_mol (because always_ask is True, or charge 0 failed)
|
|
202
|
+
if final_mol is None:
|
|
203
|
+
while True:
|
|
204
|
+
prompt_fn = getattr(self, "prompt_for_charge", None)
|
|
205
|
+
if callable(prompt_fn):
|
|
206
|
+
result = prompt_fn()
|
|
207
|
+
if isinstance(result, tuple) and len(result) == 3:
|
|
208
|
+
charge_val, ok, skip_flag = result
|
|
192
209
|
else:
|
|
193
210
|
charge_val, ok, skip_flag = 0, True, False
|
|
211
|
+
else:
|
|
212
|
+
charge_val, ok, skip_flag = 0, True, False
|
|
213
|
+
|
|
214
|
+
if not ok:
|
|
215
|
+
return None
|
|
216
|
+
if skip_flag:
|
|
217
|
+
final_mol = _process(0, use_rd_determine=False)
|
|
218
|
+
_set_prop(final_mol, "_xyz_skip_checks", 1)
|
|
219
|
+
break
|
|
220
|
+
try:
|
|
221
|
+
final_mol = _process(charge_val, use_rd_determine=True)
|
|
222
|
+
break
|
|
223
|
+
except (RuntimeError, ValueError, TypeError) as e:
|
|
224
|
+
if self.host.statusBar():
|
|
225
|
+
self.host.statusBar().showMessage(
|
|
226
|
+
f"Chemistry failed for charge {charge_val}: {e}. Try a different charge or skip."
|
|
227
|
+
)
|
|
228
|
+
if not callable(prompt_fn):
|
|
229
|
+
raise e
|
|
194
230
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
break
|
|
204
|
-
except (RuntimeError, ValueError, TypeError) as e:
|
|
205
|
-
if self.host.statusBar():
|
|
206
|
-
self.host.statusBar().showMessage(
|
|
207
|
-
f"Chemistry failed for charge {charge_val}: {e}. Try a different charge or skip."
|
|
208
|
-
)
|
|
209
|
-
if not callable(prompt_fn):
|
|
210
|
-
raise e
|
|
211
|
-
|
|
212
|
-
if final_mol:
|
|
213
|
-
final_mol.xyz_atom_data = atoms_data
|
|
214
|
-
return final_mol
|
|
231
|
+
if final_mol:
|
|
232
|
+
final_mol.xyz_atom_data = atoms_data
|
|
233
|
+
return final_mol
|
|
234
|
+
|
|
235
|
+
def load_xyz_file(self, file_path: str) -> Optional[Any]:
|
|
236
|
+
"""Load XYZ file and create RDKit Mol with charge prompt and bond determination."""
|
|
237
|
+
if not self.host.state_manager.check_unsaved_changes():
|
|
238
|
+
return None
|
|
215
239
|
|
|
240
|
+
try:
|
|
241
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
242
|
+
return self._mol_from_xyz_lines(f.readlines())
|
|
216
243
|
except (RuntimeError, TypeError, ValueError, UnicodeDecodeError) as e:
|
|
217
244
|
self.host.statusBar().showMessage(f"Error parsing XYZ file: {e}")
|
|
218
245
|
return None
|
|
219
246
|
|
|
247
|
+
def load_xyz_block(self, xyz_text: str) -> Optional[Any]:
|
|
248
|
+
"""Load XYZ text and create an RDKit Mol without opening a file dialog."""
|
|
249
|
+
try:
|
|
250
|
+
return self._mol_from_xyz_lines(xyz_text.splitlines())
|
|
251
|
+
except (RuntimeError, TypeError, ValueError, UnicodeDecodeError) as e:
|
|
252
|
+
self.host.statusBar().showMessage(f"Error parsing XYZ data: {e}")
|
|
253
|
+
return None
|
|
254
|
+
|
|
255
|
+
def show_xyz_data(
|
|
256
|
+
self, xyz_text: str, source_name: str = "XYZ data"
|
|
257
|
+
) -> Optional[Any]:
|
|
258
|
+
"""Load XYZ text, set it as the current molecule, and draw it in 3D."""
|
|
259
|
+
try:
|
|
260
|
+
mol = self.load_xyz_block(xyz_text)
|
|
261
|
+
if mol is None:
|
|
262
|
+
return None
|
|
263
|
+
|
|
264
|
+
self.host.edit_actions_manager.clear_all(skip_check=True)
|
|
265
|
+
self.host.set_current_molecule(mol)
|
|
266
|
+
self.host.set_atom_id_to_rdkit_idx_map({})
|
|
267
|
+
|
|
268
|
+
skip_flag = False
|
|
269
|
+
if mol.HasProp("_xyz_skip_checks"):
|
|
270
|
+
skip_flag = bool(mol.GetIntProp("_xyz_skip_checks"))
|
|
271
|
+
self.host.is_xyz_derived = skip_flag or (mol.GetNumBonds() == 0)
|
|
272
|
+
|
|
273
|
+
self.host.view_3d_manager.draw_molecule_3d(mol)
|
|
274
|
+
self.host.ui_manager.enter_3d_viewer_mode()
|
|
275
|
+
self.host.ui_manager.enable_3d_features(True)
|
|
276
|
+
self.host.view_3d_manager.update_atom_id_menu_text()
|
|
277
|
+
self.host.view_3d_manager.update_atom_id_menu_state()
|
|
278
|
+
|
|
279
|
+
if self.host.statusBar():
|
|
280
|
+
self.host.statusBar().showMessage(
|
|
281
|
+
f"3D Viewer Mode: Loaded {source_name}"
|
|
282
|
+
)
|
|
283
|
+
self.host.set_has_unsaved_changes(False)
|
|
284
|
+
self.host.state_manager.update_window_title()
|
|
285
|
+
return mol
|
|
286
|
+
except (RuntimeError, TypeError, ValueError, AttributeError) as e:
|
|
287
|
+
if self.host.statusBar():
|
|
288
|
+
self.host.statusBar().showMessage(f"XYZ display failed: {e}")
|
|
289
|
+
return None
|
|
290
|
+
|
|
220
291
|
def prompt_for_charge(self) -> Tuple[Optional[int], bool, bool]:
|
|
221
292
|
"""Show dialog to prompt user for molecular charge when loading XYZ files."""
|
|
222
293
|
dialog = QDialog(self.host)
|
|
@@ -261,6 +332,8 @@ class IOManager:
|
|
|
261
332
|
for j in range(i + 1, num_atoms):
|
|
262
333
|
atom_i = mol.GetAtomWithIdx(i)
|
|
263
334
|
atom_j = mol.GetAtomWithIdx(j)
|
|
335
|
+
if atom_i.GetAtomicNum() == 0 or atom_j.GetAtomicNum() == 0:
|
|
336
|
+
continue
|
|
264
337
|
distance = rdMolTransforms.GetBondLength(conf, i, j)
|
|
265
338
|
symbol_i = atom_i.GetSymbol()
|
|
266
339
|
symbol_j = atom_j.GetSymbol()
|
|
@@ -55,6 +55,11 @@ BOND_OFFSET = 3.5
|
|
|
55
55
|
DEFAULT_BOND_LENGTH = 75 # Standard bond length used in templates
|
|
56
56
|
CLIPBOARD_MIME_TYPE = "application/x-moleditpy-fragment"
|
|
57
57
|
|
|
58
|
+
# XYZ dummy/pseudo-atom labels that map to RDKit wildcard atom (*)
|
|
59
|
+
DUMMY_XYZ_SYMBOLS: frozenset[str] = frozenset(
|
|
60
|
+
{"*", "-", "X", "DA", "DU", "DUM", "DUMMY", "Q", "BQ", "LP"}
|
|
61
|
+
)
|
|
62
|
+
|
|
58
63
|
# Physical bond length (approximate) used to convert scene pixels to angstroms.
|
|
59
64
|
# DEFAULT_BOND_LENGTH is the length in pixels used in the editor UI for a typical bond.
|
|
60
65
|
# Many molecular file formats expect coordinates in angstroms; use ~1.5 Å as a typical single-bond length.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/MoleditPy_linux.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.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.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/plugins/plugin_manager.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/plugins/plugin_manager_window.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.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.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/base_picking_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/bond_length_dialog.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/calculation_worker.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/color_settings_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/custom_interactor_style.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/custom_qt_interactor.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/dialog_3d_picking_mixin.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/edit_actions_logic.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/geometry_base_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/molecular_scene_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/move_selected_atoms_dialog.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/periodic_table_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/plugin_menu_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.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.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/template_preview_item.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/template_preview_view.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/translation_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/ui/user_template_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/utils/default_settings.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.2}/src/moleditpy_linux/utils/sip_isdeleted_safe.py
RENAMED
|
File without changes
|
|
File without changes
|