MoleditPy-linux 4.1.1__tar.gz → 4.1.3__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.3}/PKG-INFO +1 -1
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/pyproject.toml +1 -1
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/MoleditPy_linux.egg-info/PKG-INFO +1 -1
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/__init__.py +0 -2
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/plugins/plugin_interface.py +14 -5
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/plugins/plugin_manager.py +2 -1
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/io_logic.py +184 -111
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/main_window.py +1 -2
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/move_selected_atoms_dialog.py +102 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/view_3d_logic.py +1 -1
- moleditpy_linux-4.1.3/src/moleditpy_linux/utils/__init__.py +11 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/utils/constants.py +5 -0
- moleditpy_linux-4.1.1/src/moleditpy_linux/core/__init__.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/LICENSE +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/README.md +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/setup.cfg +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/MoleditPy_linux.egg-info/SOURCES.txt +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/MoleditPy_linux.egg-info/dependency_links.txt +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/MoleditPy_linux.egg-info/entry_points.txt +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/MoleditPy_linux.egg-info/requires.txt +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/MoleditPy_linux.egg-info/top_level.txt +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/__main__.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/assets/file_icon.ico +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/assets/icon.icns +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/assets/icon.ico +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/assets/icon.png +0 -0
- {moleditpy_linux-4.1.1/src/moleditpy_linux/plugins → moleditpy_linux-4.1.3/src/moleditpy_linux/core}/__init__.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/core/mol_geometry.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/core/molecular_data.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/main.py +0 -0
- {moleditpy_linux-4.1.1/src/moleditpy_linux/ui/settings_tabs → moleditpy_linux-4.1.3/src/moleditpy_linux/plugins}/__init__.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/plugins/plugin_manager_window.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/__init__.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/about_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/align_plane_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/alignment_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/analysis_window.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/angle_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/app_state.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/atom_item.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/atom_picking.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/base_picking_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/bond_item.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/bond_length_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/calculation_worker.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/color_settings_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/compute_logic.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/constrained_optimization_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/custom_interactor_style.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/custom_qt_interactor.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/dialog_3d_picking_mixin.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/dialog_logic.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/dihedral_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/edit_3d_logic.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/edit_actions_logic.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/export_logic.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/geometry_base_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/main_window_init.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/mirror_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/molecular_scene_handler.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/molecule_scene.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/move_group_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/periodic_table_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/planarize_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/plugin_menu_manager.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/settings_dialog.py +0 -0
- {moleditpy_linux-4.1.1/src/moleditpy_linux/utils → moleditpy_linux-4.1.3/src/moleditpy_linux/ui/settings_tabs}/__init__.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/settings_tabs/settings_2d_tab.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/settings_tabs/settings_3d_tabs.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/settings_tabs/settings_other_tab.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/settings_tabs/settings_tab_base.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/string_importers.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/template_preview_item.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/template_preview_view.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/translation_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/ui_manager.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/user_template_dialog.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/zoomable_view.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/utils/default_settings.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/utils/sip_isdeleted_safe.py +0 -0
- {moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/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.3
|
|
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.3
|
|
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.3}/src/moleditpy_linux/plugins/plugin_interface.py
RENAMED
|
@@ -190,11 +190,9 @@ class PluginContext:
|
|
|
190
190
|
Get or set the current molecule (RDKit Mol object). Shortcut for current_molecule.
|
|
191
191
|
"""
|
|
192
192
|
mw = self.get_main_window()
|
|
193
|
-
|
|
194
|
-
mw.view_3d_manager.current_mol
|
|
195
|
-
|
|
196
|
-
else None
|
|
197
|
-
)
|
|
193
|
+
if mw and hasattr(mw, "view_3d_manager"):
|
|
194
|
+
return mw.view_3d_manager.current_mol
|
|
195
|
+
return None
|
|
198
196
|
|
|
199
197
|
@current_mol.setter
|
|
200
198
|
def current_mol(self, mol: Any) -> None:
|
|
@@ -499,6 +497,17 @@ class PluginContext:
|
|
|
499
497
|
if mw and hasattr(mw, "string_importer_manager"):
|
|
500
498
|
mw.string_importer_manager.load_from_smiles(smiles)
|
|
501
499
|
|
|
500
|
+
def show_xyz_data(
|
|
501
|
+
self, xyz_text: str, source_name: str = "XYZ data"
|
|
502
|
+
) -> Optional[Any]:
|
|
503
|
+
"""Display XYZ text in the 3D viewer and return the loaded RDKit Mol."""
|
|
504
|
+
mw = self.get_main_window()
|
|
505
|
+
if mw and hasattr(mw, "io_manager"):
|
|
506
|
+
show = getattr(mw.io_manager, "show_xyz_data", None)
|
|
507
|
+
if show is not None:
|
|
508
|
+
return show(xyz_text, source_name=source_name)
|
|
509
|
+
return None
|
|
510
|
+
|
|
502
511
|
def to_xyz_block(self) -> Optional[str]:
|
|
503
512
|
"""Return the current 3D structure as an XYZ block (only element x y z lines)."""
|
|
504
513
|
mol = self.current_mol
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/plugins/plugin_manager.py
RENAMED
|
@@ -588,7 +588,8 @@ class PluginManager:
|
|
|
588
588
|
selected_atom_ids.add(item.atom_id)
|
|
589
589
|
|
|
590
590
|
# Now map these editor IDs to RDKit indices
|
|
591
|
-
|
|
591
|
+
v3d = getattr(self.main_window, "view_3d_manager", None)
|
|
592
|
+
mol = v3d.current_mol if v3d else None
|
|
592
593
|
if mol and selected_atom_ids:
|
|
593
594
|
for i in range(mol.GetNumAtoms()):
|
|
594
595
|
atom = mol.GetAtomWithIdx(i)
|
|
@@ -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()
|
|
@@ -19,6 +19,7 @@ if TYPE_CHECKING:
|
|
|
19
19
|
from rdkit import Chem
|
|
20
20
|
|
|
21
21
|
# PyQt6 Modules
|
|
22
|
+
import copy
|
|
22
23
|
from PyQt6.QtCore import pyqtSignal
|
|
23
24
|
from PyQt6.QtWidgets import QMainWindow, QMessageBox
|
|
24
25
|
|
|
@@ -254,8 +255,6 @@ class MainWindow(QMainWindow):
|
|
|
254
255
|
|
|
255
256
|
def save_state_snapshot(self) -> None:
|
|
256
257
|
"""Create a deep copy snapshot of the current state for undo/redo comparison."""
|
|
257
|
-
import copy
|
|
258
|
-
|
|
259
258
|
try:
|
|
260
259
|
self.state_manager.saved_state = copy.deepcopy(
|
|
261
260
|
self.state_manager.get_current_state()
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/move_selected_atoms_dialog.py
RENAMED
|
@@ -56,6 +56,8 @@ class MoveSelectedAtomsDialog(BasePickingDialog):
|
|
|
56
56
|
self.mouse_moved_during_drag: bool = False
|
|
57
57
|
self._consume_next_left_release: bool = False
|
|
58
58
|
self.highlight_actor: Optional[pv.Actor] = None
|
|
59
|
+
self.original_style: Optional[Any] = None
|
|
60
|
+
self.rubber_band_style: Optional[Any] = None
|
|
59
61
|
|
|
60
62
|
self.widgets: dict[str, Any] = {}
|
|
61
63
|
|
|
@@ -211,6 +213,13 @@ class MoveSelectedAtomsDialog(BasePickingDialog):
|
|
|
211
213
|
def _init_buttons_ui(self, layout: QVBoxLayout) -> None:
|
|
212
214
|
"""Initialize bottom buttons."""
|
|
213
215
|
button_layout = QHBoxLayout()
|
|
216
|
+
|
|
217
|
+
box_select_btn = QPushButton("Box Selection: OFF")
|
|
218
|
+
box_select_btn.setCheckable(True)
|
|
219
|
+
box_select_btn.clicked.connect(self.toggle_box_selection)
|
|
220
|
+
self.widgets["box_select_btn"] = box_select_btn
|
|
221
|
+
button_layout.addWidget(box_select_btn)
|
|
222
|
+
|
|
214
223
|
clear_btn = QPushButton("Clear Selection")
|
|
215
224
|
clear_btn.clicked.connect(self.clear_selection)
|
|
216
225
|
self.widgets["clear_button"] = clear_btn
|
|
@@ -238,6 +247,24 @@ class MoveSelectedAtomsDialog(BasePickingDialog):
|
|
|
238
247
|
|
|
239
248
|
e_type = event.type()
|
|
240
249
|
|
|
250
|
+
btn = self.widgets.get("box_select_btn")
|
|
251
|
+
box_selection_on = btn is not None and btn.isChecked()
|
|
252
|
+
|
|
253
|
+
if e_type == QEvent.Type.MouseButtonPress:
|
|
254
|
+
self._click_press_pos = getattr(event, "pos", lambda: None)()
|
|
255
|
+
elif e_type == QEvent.Type.MouseButtonRelease:
|
|
256
|
+
if hasattr(self, "_click_press_pos") and self._click_press_pos is not None:
|
|
257
|
+
pos = getattr(event, "pos", lambda: None)()
|
|
258
|
+
if pos:
|
|
259
|
+
diff = pos - self._click_press_pos
|
|
260
|
+
if diff.manhattanLength() < 3:
|
|
261
|
+
if box_selection_on:
|
|
262
|
+
self.clear_selection()
|
|
263
|
+
self._click_press_pos = None
|
|
264
|
+
|
|
265
|
+
if box_selection_on:
|
|
266
|
+
return False
|
|
267
|
+
|
|
241
268
|
if e_type == QEvent.Type.MouseButtonDblClick:
|
|
242
269
|
# Ignore double clicks and reset state
|
|
243
270
|
self.is_dragging_group = False
|
|
@@ -645,3 +672,78 @@ class MoveSelectedAtomsDialog(BasePickingDialog):
|
|
|
645
672
|
self.update_display()
|
|
646
673
|
self.is_dragging_group = False
|
|
647
674
|
self.drag_start_pos = None
|
|
675
|
+
|
|
676
|
+
def toggle_box_selection(self, checked: bool) -> None:
|
|
677
|
+
"""Toggle Box Selection mode using PyVista's rectangle picker."""
|
|
678
|
+
plotter = self.main_window.view_3d_manager.plotter
|
|
679
|
+
if plotter is None or plotter.interactor is None:
|
|
680
|
+
return
|
|
681
|
+
|
|
682
|
+
btn = self.widgets["box_select_btn"]
|
|
683
|
+
if checked:
|
|
684
|
+
btn.setText("Box Selection: ON")
|
|
685
|
+
# Save original style if not saved
|
|
686
|
+
if self.original_style is None:
|
|
687
|
+
self.original_style = plotter.interactor.GetInteractorStyle()
|
|
688
|
+
|
|
689
|
+
# Using PyVista's built-in picking which reliably draws the box correctly
|
|
690
|
+
plotter.enable_rectangle_picking(
|
|
691
|
+
callback=self.on_rectangle_picked,
|
|
692
|
+
show_message=False,
|
|
693
|
+
start=True,
|
|
694
|
+
color="red",
|
|
695
|
+
)
|
|
696
|
+
else:
|
|
697
|
+
btn.setText("Box Selection: OFF")
|
|
698
|
+
plotter.disable_picking()
|
|
699
|
+
# Restore original style
|
|
700
|
+
if self.original_style is not None:
|
|
701
|
+
plotter.interactor.SetInteractorStyle(self.original_style)
|
|
702
|
+
self.rubber_band_style = None
|
|
703
|
+
|
|
704
|
+
def on_rectangle_picked(self, selection: Any) -> None:
|
|
705
|
+
"""Handle PyVista rectangle picking callback."""
|
|
706
|
+
if not hasattr(selection, "viewport"):
|
|
707
|
+
return
|
|
708
|
+
|
|
709
|
+
x0, y0, x1, y1 = selection.viewport
|
|
710
|
+
x_min = min(x0, x1)
|
|
711
|
+
x_max = max(x0, x1)
|
|
712
|
+
y_min = min(y0, y1)
|
|
713
|
+
y_max = max(y0, y1)
|
|
714
|
+
|
|
715
|
+
# If the drag box is small, treat it as a single click to clear selection
|
|
716
|
+
if abs(x_max - x_min) < 15 and abs(y_max - y_min) < 15:
|
|
717
|
+
self.clear_selection()
|
|
718
|
+
return
|
|
719
|
+
|
|
720
|
+
plotter = self.main_window.view_3d_manager.plotter
|
|
721
|
+
if plotter is None:
|
|
722
|
+
return
|
|
723
|
+
renderer = plotter.renderer
|
|
724
|
+
|
|
725
|
+
positions = self.main_window.view_3d_manager.atom_positions_3d
|
|
726
|
+
if positions is not None:
|
|
727
|
+
added = False
|
|
728
|
+
for atom_idx, pos in enumerate(positions):
|
|
729
|
+
renderer.SetWorldPoint(float(pos[0]), float(pos[1]), float(pos[2]), 1.0)
|
|
730
|
+
renderer.WorldToDisplay()
|
|
731
|
+
display = renderer.GetDisplayPoint()
|
|
732
|
+
|
|
733
|
+
if x_min <= display[0] <= x_max and y_min <= display[1] <= y_max:
|
|
734
|
+
if atom_idx not in self.selected_atoms:
|
|
735
|
+
self.selected_atoms.add(atom_idx)
|
|
736
|
+
added = True
|
|
737
|
+
|
|
738
|
+
if added:
|
|
739
|
+
self.show_atom_labels()
|
|
740
|
+
self.update_display()
|
|
741
|
+
|
|
742
|
+
def reject(self) -> None:
|
|
743
|
+
"""Clean up when dialog closes."""
|
|
744
|
+
# Ensure we restore the interactor style if it was left in Box Selection mode
|
|
745
|
+
btn = self.widgets.get("box_select_btn")
|
|
746
|
+
if btn and btn.isChecked():
|
|
747
|
+
btn.setChecked(False)
|
|
748
|
+
self.toggle_box_selection(False)
|
|
749
|
+
super().reject()
|
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
"""
|
|
@@ -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
|
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/MoleditPy_linux.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/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
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/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.3}/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.3}/src/moleditpy_linux/ui/base_picking_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/bond_length_dialog.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/calculation_worker.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/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.3}/src/moleditpy_linux/ui/custom_interactor_style.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/custom_qt_interactor.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/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.3}/src/moleditpy_linux/ui/edit_actions_logic.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/geometry_base_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/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.3}/src/moleditpy_linux/ui/periodic_table_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/plugin_menu_manager.py
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
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/template_preview_item.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/template_preview_view.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/translation_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/ui/user_template_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/utils/default_settings.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.1.1 → moleditpy_linux-4.1.3}/src/moleditpy_linux/utils/sip_isdeleted_safe.py
RENAMED
|
File without changes
|
|
File without changes
|