MoleditPy-linux 4.5.0__tar.gz → 4.5.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.
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/PKG-INFO +1 -1
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/pyproject.toml +1 -1
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/MoleditPy_linux.egg-info/PKG-INFO +1 -1
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/core/molecular_data.py +111 -34
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/main.py +2 -2
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/angle_dialog.py +22 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/bond_length_dialog.py +47 -1
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/compute_logic.py +25 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/dihedral_dialog.py +23 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/edit_actions_logic.py +17 -22
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/io_logic.py +31 -18
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/settings_tabs/settings_other_tab.py +2 -2
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/string_importers.py +1 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/LICENSE +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/README.md +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/setup.cfg +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/MoleditPy_linux.egg-info/SOURCES.txt +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/MoleditPy_linux.egg-info/dependency_links.txt +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/MoleditPy_linux.egg-info/entry_points.txt +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/MoleditPy_linux.egg-info/requires.txt +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/MoleditPy_linux.egg-info/top_level.txt +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/__init__.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/__main__.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/assets/file_icon.ico +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/assets/icon.icns +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/assets/icon.ico +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/assets/icon.png +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/core/__init__.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/core/mol_geometry.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/plugins/__init__.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/plugins/plugin_interface.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/plugins/plugin_manager.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/plugins/plugin_manager_window.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/__init__.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/about_dialog.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/align_plane_dialog.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/alignment_dialog.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/analysis_window.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/app_state.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/atom_item.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/atom_picking.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/base_picking_dialog.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/bond_item.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/calculation_worker.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/color_settings_dialog.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/constrained_optimization_dialog.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/custom_interactor_style.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/custom_qt_interactor.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/dialog_3d_picking_mixin.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/dialog_logic.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/edit_3d_logic.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/export_logic.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/geometry_base_dialog.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/main_window.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/main_window_init.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/mirror_dialog.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/molecular_scene_handler.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/molecule_scene.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/move_group_dialog.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/move_selected_atoms_dialog.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/periodic_table_dialog.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/planarize_dialog.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/plugin_menu_manager.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/settings_dialog.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/settings_tabs/__init__.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/settings_tabs/settings_2d_tab.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/settings_tabs/settings_3d_tabs.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/settings_tabs/settings_tab_base.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/template_preview_item.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/template_preview_view.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/translation_dialog.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/ui_manager.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/user_template_dialog.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/view_3d_logic.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/zoomable_view.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/utils/__init__.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/utils/constants.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/utils/default_settings.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/utils/sip_isdeleted_safe.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/utils/suppress_log.py +0 -0
- {moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/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.5.
|
|
3
|
+
Version: 4.5.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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: MoleditPy-linux
|
|
3
|
-
Version: 4.5.
|
|
3
|
+
Version: 4.5.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
|
|
@@ -30,6 +30,93 @@ class PointTuple(tuple):
|
|
|
30
30
|
return float(self[1])
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
def _mdl_radical_code(radical_electrons: int) -> int:
|
|
34
|
+
"""Map a count of unpaired electrons to an MDL radical code."""
|
|
35
|
+
# MDL encodes 1 = singlet, 2 = doublet, 3 = triplet.
|
|
36
|
+
if radical_electrons <= 0:
|
|
37
|
+
return 0
|
|
38
|
+
return 2 if radical_electrons == 1 else 3
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _mdl_property_lines(atom_records: List[Dict[str, Any]]) -> List[str]:
|
|
42
|
+
"""Build the M CHG / M RAD property lines for a V2000 block."""
|
|
43
|
+
lines = []
|
|
44
|
+
for tag, key, encode in (
|
|
45
|
+
("CHG", "charge", int),
|
|
46
|
+
("RAD", "radical", _mdl_radical_code),
|
|
47
|
+
):
|
|
48
|
+
entries = [
|
|
49
|
+
(idx, encode(rec[key]))
|
|
50
|
+
for idx, rec in enumerate(atom_records, start=1)
|
|
51
|
+
if rec[key]
|
|
52
|
+
]
|
|
53
|
+
# The MDL property block allows at most 8 entries per line.
|
|
54
|
+
for start in range(0, len(entries), 8):
|
|
55
|
+
chunk = entries[start : start + 8]
|
|
56
|
+
lines.append(
|
|
57
|
+
f"M {tag}{len(chunk):3d}"
|
|
58
|
+
+ "".join(f"{idx:4d}{val:4d}" for idx, val in chunk)
|
|
59
|
+
)
|
|
60
|
+
return lines
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _to_v2000_block(
|
|
64
|
+
atom_records: List[Dict[str, Any]], bond_records: List[Dict[str, Any]]
|
|
65
|
+
) -> str:
|
|
66
|
+
"""Render collected atom/bond records as an MDL V2000 MOL block."""
|
|
67
|
+
lines = ["", " MoleditPy", ""]
|
|
68
|
+
lines.append(
|
|
69
|
+
f"{len(atom_records):3d}{len(bond_records):3d} 0 0 0 0 0 0 0 0999 V2000"
|
|
70
|
+
)
|
|
71
|
+
for rec in atom_records:
|
|
72
|
+
# Charge and radical travel in the M CHG / M RAD blocks below, so every
|
|
73
|
+
# per-atom field here stays zero.
|
|
74
|
+
lines.append(
|
|
75
|
+
f"{rec['x']:10.4f}{rec['y']:10.4f}{0.0:10.4f} {rec['symbol']:<3}"
|
|
76
|
+
" 0 0 0 0 0 0 0 0 0 0 0 0"
|
|
77
|
+
)
|
|
78
|
+
for rec in bond_records:
|
|
79
|
+
lines.append(
|
|
80
|
+
f"{rec['a1']:3d}{rec['a2']:3d}{rec['order']:3d}{rec['stereo']:3d} 0 0 0"
|
|
81
|
+
)
|
|
82
|
+
lines.extend(_mdl_property_lines(atom_records))
|
|
83
|
+
lines.append("M END")
|
|
84
|
+
return "\n".join(lines) + "\n"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _to_v3000_block(
|
|
88
|
+
atom_records: List[Dict[str, Any]], bond_records: List[Dict[str, Any]]
|
|
89
|
+
) -> str:
|
|
90
|
+
"""Render collected atom/bond records as an MDL V3000 MOL block."""
|
|
91
|
+
lines = ["", " MoleditPy", "", " 0 0 0 0 0 0 0 0 0 0999 V3000"]
|
|
92
|
+
lines.append("M V30 BEGIN CTAB")
|
|
93
|
+
lines.append(f"M V30 COUNTS {len(atom_records)} {len(bond_records)} 0 0 0")
|
|
94
|
+
lines.append("M V30 BEGIN ATOM")
|
|
95
|
+
for idx, rec in enumerate(atom_records, start=1):
|
|
96
|
+
extra = ""
|
|
97
|
+
if rec["charge"]:
|
|
98
|
+
extra += f" CHG={int(rec['charge'])}"
|
|
99
|
+
if rec["radical"]:
|
|
100
|
+
extra += f" RAD={_mdl_radical_code(rec['radical'])}"
|
|
101
|
+
lines.append(
|
|
102
|
+
f"M V30 {idx} {rec['symbol']} "
|
|
103
|
+
f"{rec['x']:.4f} {rec['y']:.4f} 0.0000 0{extra}"
|
|
104
|
+
)
|
|
105
|
+
lines.append("M V30 END ATOM")
|
|
106
|
+
lines.append("M V30 BEGIN BOND")
|
|
107
|
+
for idx, rec in enumerate(bond_records, start=1):
|
|
108
|
+
cfg = ""
|
|
109
|
+
if rec["stereo"] == 1:
|
|
110
|
+
cfg = " CFG=1"
|
|
111
|
+
elif rec["stereo"] == 6:
|
|
112
|
+
cfg = " CFG=3"
|
|
113
|
+
lines.append(f"M V30 {idx} {rec['order']} {rec['a1']} {rec['a2']}{cfg}")
|
|
114
|
+
lines.append("M V30 END BOND")
|
|
115
|
+
lines.append("M V30 END CTAB")
|
|
116
|
+
lines.append("M END")
|
|
117
|
+
return "\n".join(lines) + "\n"
|
|
118
|
+
|
|
119
|
+
|
|
33
120
|
class MolecularData:
|
|
34
121
|
"""In-memory graph of atoms and bonds, independent of any UI framework."""
|
|
35
122
|
|
|
@@ -348,7 +435,7 @@ class MolecularData:
|
|
|
348
435
|
|
|
349
436
|
# Counts line and bond indices must only reflect atoms actually written
|
|
350
437
|
atom_map: Dict[int, int] = {}
|
|
351
|
-
|
|
438
|
+
atom_records: List[Dict[str, Any]] = []
|
|
352
439
|
for old_id, atom in self.atoms.items():
|
|
353
440
|
# Convert scene pixel coordinates to angstroms when emitting MOL block
|
|
354
441
|
pos = atom.get("pos")
|
|
@@ -362,37 +449,23 @@ class MolecularData:
|
|
|
362
449
|
else:
|
|
363
450
|
continue
|
|
364
451
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
elif charge == 1:
|
|
375
|
-
chg_code = 3
|
|
376
|
-
elif charge == -1:
|
|
377
|
-
chg_code = 5
|
|
378
|
-
elif charge == -2:
|
|
379
|
-
chg_code = 6
|
|
380
|
-
elif charge == -3:
|
|
381
|
-
chg_code = 7
|
|
382
|
-
|
|
383
|
-
atom_map[old_id] = len(atom_lines)
|
|
384
|
-
atom_lines.append(
|
|
385
|
-
f"{x:10.4f}{y:10.4f}{z:10.4f} {symbol:<3} 0 0 0{chg_code:3d} 0 0 0 0 0 0 0\n"
|
|
452
|
+
atom_map[old_id] = len(atom_records)
|
|
453
|
+
atom_records.append(
|
|
454
|
+
{
|
|
455
|
+
"x": x_px * ANGSTROM_PER_PIXEL,
|
|
456
|
+
"y": y_px * ANGSTROM_PER_PIXEL,
|
|
457
|
+
"symbol": atom["symbol"],
|
|
458
|
+
"charge": int(atom.get("charge", 0) or 0),
|
|
459
|
+
"radical": int(atom.get("radical", 0) or 0),
|
|
460
|
+
}
|
|
386
461
|
)
|
|
387
462
|
|
|
388
|
-
|
|
463
|
+
bond_records: List[Dict[str, Any]] = []
|
|
389
464
|
for (id1, id2), bond in self.bonds.items():
|
|
390
465
|
if id1 not in atom_map or id2 not in atom_map:
|
|
391
466
|
continue
|
|
392
|
-
|
|
393
|
-
# Bond order may be a float (1.5 = aromatic); V2000 uses code 4.
|
|
467
|
+
# Bond order may be a float (1.5 = aromatic); MDL uses code 4.
|
|
394
468
|
order_val = float(bond["order"])
|
|
395
|
-
order = 4 if order_val == 1.5 else int(order_val)
|
|
396
469
|
stereo_code = 0
|
|
397
470
|
bond_stereo = bond.get("stereo", 0)
|
|
398
471
|
if bond_stereo == 1:
|
|
@@ -400,16 +473,20 @@ class MolecularData:
|
|
|
400
473
|
elif bond_stereo == 2:
|
|
401
474
|
stereo_code = 6
|
|
402
475
|
|
|
403
|
-
|
|
404
|
-
|
|
476
|
+
bond_records.append(
|
|
477
|
+
{
|
|
478
|
+
"a1": atom_map[id1] + 1,
|
|
479
|
+
"a2": atom_map[id2] + 1,
|
|
480
|
+
"order": 4 if order_val == 1.5 else int(order_val),
|
|
481
|
+
"stereo": stereo_code,
|
|
482
|
+
}
|
|
405
483
|
)
|
|
406
484
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
return mol_block
|
|
485
|
+
# V2000 packs the counts into three-character fields, so anything past
|
|
486
|
+
# 999 would run the atom count into the bond count and corrupt the file.
|
|
487
|
+
if len(atom_records) > 999 or len(bond_records) > 999:
|
|
488
|
+
return _to_v3000_block(atom_records, bond_records)
|
|
489
|
+
return _to_v2000_block(atom_records, bond_records)
|
|
413
490
|
|
|
414
491
|
def to_template_dict(
|
|
415
492
|
self, name: str, version: str = "1.0", application_version: str = ""
|
|
@@ -189,7 +189,7 @@ def setup_logging() -> None:
|
|
|
189
189
|
log_dir = os.path.join(os.path.expanduser("~"), ".moleditpy")
|
|
190
190
|
try:
|
|
191
191
|
os.makedirs(log_dir, exist_ok=True)
|
|
192
|
-
log_path = os.path.join(log_dir, "
|
|
192
|
+
log_path = os.path.join(log_dir, "moleditpy.log")
|
|
193
193
|
fh = logging.handlers.RotatingFileHandler(
|
|
194
194
|
log_path, maxBytes=1_048_576, backupCount=3, encoding="utf-8"
|
|
195
195
|
)
|
|
@@ -231,7 +231,7 @@ def main() -> None:
|
|
|
231
231
|
if sys.platform == "win32":
|
|
232
232
|
# Taskbar grouping ID follows the major version
|
|
233
233
|
major = VERSION.split(".")[0] if VERSION and VERSION != "Unknown" else "0"
|
|
234
|
-
myappid = f"hyoko.
|
|
234
|
+
myappid = f"hyoko.moleditpy.{major}"
|
|
235
235
|
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
|
|
236
236
|
|
|
237
237
|
parser = argparse.ArgumentParser(
|
|
@@ -35,6 +35,7 @@ from ..core.mol_geometry import (
|
|
|
35
35
|
calc_angle_deg,
|
|
36
36
|
get_connected_group,
|
|
37
37
|
)
|
|
38
|
+
from ..utils.suppress_log import suppress_log
|
|
38
39
|
|
|
39
40
|
if TYPE_CHECKING:
|
|
40
41
|
from .main_window import MainWindow
|
|
@@ -468,5 +469,26 @@ class AngleDialog(GeometryBaseDialog):
|
|
|
468
469
|
atoms_to_move,
|
|
469
470
|
)
|
|
470
471
|
|
|
472
|
+
achieved = calc_angle_deg(positions[idx_a], positions[idx_b], positions[idx_c])
|
|
473
|
+
|
|
471
474
|
# Write updated positions back using inherited helper
|
|
472
475
|
self._update_molecule_geometry(positions)
|
|
476
|
+
|
|
477
|
+
self._warn_if_angle_not_applied(new_angle_deg, achieved)
|
|
478
|
+
|
|
479
|
+
def _warn_if_angle_not_applied(self, requested: float, achieved: float) -> None:
|
|
480
|
+
"""Tell the user when the requested angle could not be reached.
|
|
481
|
+
|
|
482
|
+
For an angle inside a ring the connected group reached from one arm
|
|
483
|
+
comes back around and contains all three atoms, so they rotate
|
|
484
|
+
together and the angle is unchanged. Leaving the old value silently
|
|
485
|
+
looks like the edit was applied.
|
|
486
|
+
"""
|
|
487
|
+
if abs(achieved - requested) < 0.5:
|
|
488
|
+
return
|
|
489
|
+
with suppress_log(AttributeError, RuntimeError, TypeError):
|
|
490
|
+
self.main_window.statusBar().showMessage(
|
|
491
|
+
f"Angle unchanged ({achieved:.2f} deg): the three atoms rotate "
|
|
492
|
+
"together, which happens for an angle inside a ring.",
|
|
493
|
+
5000,
|
|
494
|
+
)
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/bond_length_dialog.py
RENAMED
|
@@ -14,7 +14,7 @@ from __future__ import annotations
|
|
|
14
14
|
import logging
|
|
15
15
|
|
|
16
16
|
import numpy as np
|
|
17
|
-
from typing import Any, TYPE_CHECKING, Optional, Sequence
|
|
17
|
+
from typing import Any, Dict, TYPE_CHECKING, Optional, Sequence
|
|
18
18
|
|
|
19
19
|
from PyQt6.QtCore import Qt
|
|
20
20
|
from PyQt6.QtWidgets import (
|
|
@@ -32,6 +32,7 @@ from rdkit import Chem
|
|
|
32
32
|
|
|
33
33
|
from .geometry_base_dialog import GeometryBaseDialog
|
|
34
34
|
from ..core.mol_geometry import calc_distance, get_connected_group
|
|
35
|
+
from ..utils.suppress_log import suppress_log
|
|
35
36
|
|
|
36
37
|
if TYPE_CHECKING:
|
|
37
38
|
from .main_window import MainWindow
|
|
@@ -359,6 +360,8 @@ class BondLengthDialog(GeometryBaseDialog):
|
|
|
359
360
|
if current_distance == 0:
|
|
360
361
|
return
|
|
361
362
|
|
|
363
|
+
original = {i: np.array(p, dtype=float) for i, p in positions.items()}
|
|
364
|
+
|
|
362
365
|
direction = direction / current_distance
|
|
363
366
|
|
|
364
367
|
if self.both_groups_radio.isChecked():
|
|
@@ -400,3 +403,46 @@ class BondLengthDialog(GeometryBaseDialog):
|
|
|
400
403
|
|
|
401
404
|
# Write updated positions back using inherited helper
|
|
402
405
|
self._update_molecule_geometry(positions)
|
|
406
|
+
|
|
407
|
+
self._warn_if_other_bonds_moved(original, positions, idx1, idx2)
|
|
408
|
+
|
|
409
|
+
def _warn_if_other_bonds_moved(
|
|
410
|
+
self,
|
|
411
|
+
before: Dict[int, Any],
|
|
412
|
+
after: Dict[int, Any],
|
|
413
|
+
idx1: int,
|
|
414
|
+
idx2: int,
|
|
415
|
+
) -> None:
|
|
416
|
+
"""Report a neighbouring bond that this edit resized as a side effect.
|
|
417
|
+
|
|
418
|
+
A bond inside a ring cannot be resized alone: the group translated
|
|
419
|
+
along the bond axis wraps back around the ring, so the ring closes at
|
|
420
|
+
a different length. Stretching a benzene bond to 1.80 A pulls its
|
|
421
|
+
neighbour to 1.24 A, shorter than a double bond, with nothing on
|
|
422
|
+
screen to say so.
|
|
423
|
+
"""
|
|
424
|
+
edited = {idx1, idx2}
|
|
425
|
+
worst_delta = 0.0
|
|
426
|
+
worst_label = ""
|
|
427
|
+
for bond in self.mol.GetBonds():
|
|
428
|
+
i, j = bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()
|
|
429
|
+
if {i, j} == edited or i not in before or j not in before:
|
|
430
|
+
continue
|
|
431
|
+
delta = calc_distance(after[i], after[j]) - calc_distance(
|
|
432
|
+
before[i], before[j]
|
|
433
|
+
)
|
|
434
|
+
if abs(delta) > abs(worst_delta):
|
|
435
|
+
worst_delta = delta
|
|
436
|
+
worst_label = (
|
|
437
|
+
f"{self.mol.GetAtomWithIdx(i).GetSymbol()}{i}-"
|
|
438
|
+
f"{self.mol.GetAtomWithIdx(j).GetSymbol()}{j}"
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
if abs(worst_delta) < 1e-4:
|
|
442
|
+
return
|
|
443
|
+
with suppress_log(AttributeError, RuntimeError, TypeError):
|
|
444
|
+
self.main_window.statusBar().showMessage(
|
|
445
|
+
f"Bond {worst_label} also changed by {worst_delta:+.3f} A: a bond "
|
|
446
|
+
"inside a ring cannot be resized on its own.",
|
|
447
|
+
5000,
|
|
448
|
+
)
|
|
@@ -19,6 +19,7 @@ from PyQt6.QtCore import QThread, QTimer, QPoint
|
|
|
19
19
|
from PyQt6.QtGui import QAction, QColor
|
|
20
20
|
from PyQt6.QtWidgets import QMenu, QMessageBox, QWidget
|
|
21
21
|
from rdkit import Chem
|
|
22
|
+
from rdkit.Chem import AllChem
|
|
22
23
|
|
|
23
24
|
from . import OBABEL_AVAILABLE
|
|
24
25
|
|
|
@@ -458,7 +459,31 @@ class ComputeManager:
|
|
|
458
459
|
# Defer so Qt finishes routing the modal result before restoring focus.
|
|
459
460
|
QTimer.singleShot(0, self.host.init_manager.view_2d.setFocus)
|
|
460
461
|
|
|
462
|
+
def _ez_consistent_mol_block(self) -> Optional[str]:
|
|
463
|
+
"""MOL block whose 2D geometry encodes the editor's E/Z labels."""
|
|
464
|
+
data = self.host.state_manager.data
|
|
465
|
+
if not any(b.get("stereo", 0) in (3, 4) for b in data.bonds.values()):
|
|
466
|
+
return None
|
|
467
|
+
stereo_mol = data.to_rdkit_mol(use_2d_stereo=False)
|
|
468
|
+
if stereo_mol is None:
|
|
469
|
+
return None
|
|
470
|
+
try:
|
|
471
|
+
# MDL carries double-bond cis/trans only in the 2D coordinates, so a
|
|
472
|
+
# label that contradicts how the user drew the bond is lost unless
|
|
473
|
+
# the layout is rebuilt from the stereo. These coordinates only seed
|
|
474
|
+
# 3D generation; the 2D canvas keeps the user's own layout.
|
|
475
|
+
planar = Chem.Mol(stereo_mol)
|
|
476
|
+
planar.RemoveAllConformers()
|
|
477
|
+
AllChem.Compute2DCoords(planar)
|
|
478
|
+
return str(Chem.MolToMolBlock(planar, includeStereo=True))
|
|
479
|
+
except (RuntimeError, ValueError) as e:
|
|
480
|
+
logging.warning("E/Z-consistent 2D layout failed: %s", e)
|
|
481
|
+
return None
|
|
482
|
+
|
|
461
483
|
def _setup_mol_block_for_worker(self, mol: Chem.Mol) -> str:
|
|
484
|
+
ez_block = self._ez_consistent_mol_block()
|
|
485
|
+
if ez_block:
|
|
486
|
+
return ez_block
|
|
462
487
|
mol_block = self.host.state_manager.data.to_mol_block()
|
|
463
488
|
if not mol_block:
|
|
464
489
|
mol_block = Chem.MolToMolBlock(mol, includeStereo=True)
|
|
@@ -35,6 +35,7 @@ from ..core.mol_geometry import (
|
|
|
35
35
|
calculate_dihedral,
|
|
36
36
|
get_connected_group,
|
|
37
37
|
)
|
|
38
|
+
from ..utils.suppress_log import suppress_log
|
|
38
39
|
|
|
39
40
|
if TYPE_CHECKING:
|
|
40
41
|
from .main_window import MainWindow
|
|
@@ -469,5 +470,27 @@ class DihedralDialog(GeometryBaseDialog):
|
|
|
469
470
|
positions, idx1, idx2, idx3, idx4, new_dihedral_deg, atoms_to_move
|
|
470
471
|
)
|
|
471
472
|
|
|
473
|
+
achieved = calculate_dihedral(positions, idx1, idx2, idx3, idx4)
|
|
474
|
+
|
|
472
475
|
# Write updated positions back using inherited helper
|
|
473
476
|
self._update_molecule_geometry(positions)
|
|
477
|
+
|
|
478
|
+
self._warn_if_torsion_not_applied(new_dihedral_deg, achieved)
|
|
479
|
+
|
|
480
|
+
def _warn_if_torsion_not_applied(self, requested: float, achieved: float) -> None:
|
|
481
|
+
"""Tell the user when the requested torsion could not be reached.
|
|
482
|
+
|
|
483
|
+
A torsion inside a ring cannot rotate: the connected group reached from
|
|
484
|
+
one side comes back around and contains all four atoms, so they turn
|
|
485
|
+
together and the angle is unchanged. Silently leaving the old value
|
|
486
|
+
looks like the edit was applied.
|
|
487
|
+
"""
|
|
488
|
+
difference = abs(achieved - requested) % 360.0
|
|
489
|
+
if min(difference, 360.0 - difference) < 0.5:
|
|
490
|
+
return
|
|
491
|
+
with suppress_log(AttributeError, RuntimeError, TypeError):
|
|
492
|
+
self.main_window.statusBar().showMessage(
|
|
493
|
+
f"Dihedral unchanged ({achieved:.2f}°): the four atoms rotate "
|
|
494
|
+
"together, which happens for a bond inside a ring.",
|
|
495
|
+
5000,
|
|
496
|
+
)
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/edit_actions_logic.py
RENAMED
|
@@ -583,26 +583,17 @@ class EditActionsManager:
|
|
|
583
583
|
if orig_id not in self.host.state_manager.data.atoms:
|
|
584
584
|
continue
|
|
585
585
|
|
|
586
|
-
#
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
586
|
+
# Hydrogens still needing an atom of their own. GetTotalNumHs()
|
|
587
|
+
# excludes H already drawn as atoms but includes the
|
|
588
|
+
# property-explicit H that sanitization assigns to an aromatic
|
|
589
|
+
# N-H; subtracting GetNumExplicitHs() here would cancel exactly
|
|
590
|
+
# that term and leave luminol's two N-H without hydrogens.
|
|
591
|
+
try:
|
|
592
|
+
implicit_h = int(rd_atom.GetTotalNumHs())
|
|
593
|
+
except (AttributeError, RuntimeError, ValueError, TypeError):
|
|
594
|
+
implicit_h = 0
|
|
595
|
+
if implicit_h < 0:
|
|
593
596
|
implicit_h = 0
|
|
594
|
-
if implicit_h == 0:
|
|
595
|
-
# Fallback
|
|
596
|
-
try:
|
|
597
|
-
total_h = int(rd_atom.GetTotalNumHs())
|
|
598
|
-
explicit_h = (
|
|
599
|
-
int(rd_atom.GetNumExplicitHs())
|
|
600
|
-
if hasattr(rd_atom, "GetNumExplicitHs")
|
|
601
|
-
else 0
|
|
602
|
-
)
|
|
603
|
-
implicit_h = max(0, total_h - explicit_h)
|
|
604
|
-
except (AttributeError, RuntimeError, ValueError, TypeError):
|
|
605
|
-
implicit_h = 0
|
|
606
597
|
|
|
607
598
|
if implicit_h <= 0:
|
|
608
599
|
continue
|
|
@@ -954,12 +945,16 @@ class EditActionsManager:
|
|
|
954
945
|
continue
|
|
955
946
|
original_id = atom.GetIntProp("_original_atom_id")
|
|
956
947
|
|
|
957
|
-
#
|
|
948
|
+
# GetTotalNumHs() counts implicit plus property-explicit H but
|
|
949
|
+
# not H drawn as its own atom, which is exactly what the label
|
|
950
|
+
# should show. Sanitization files an aromatic N-H (luminol,
|
|
951
|
+
# pyrrole) under the property, where GetNumImplicitHs() alone
|
|
952
|
+
# reports 0 and the H silently vanishes from the label.
|
|
958
953
|
try:
|
|
959
|
-
h_count = int(atom.
|
|
954
|
+
h_count = int(atom.GetTotalNumHs())
|
|
960
955
|
except (AttributeError, RuntimeError, ValueError, TypeError):
|
|
961
956
|
try:
|
|
962
|
-
h_count = int(atom.
|
|
957
|
+
h_count = int(atom.GetNumImplicitHs())
|
|
963
958
|
except (AttributeError, RuntimeError, ValueError, TypeError):
|
|
964
959
|
h_count = 0
|
|
965
960
|
|
|
@@ -152,8 +152,11 @@ class IOManager:
|
|
|
152
152
|
|
|
153
153
|
def _mol_from_xyz_lines(self, raw_lines: list[str]) -> Any:
|
|
154
154
|
"""Create an RDKit molecule from XYZ text lines."""
|
|
155
|
-
lines = [ln.strip() for ln in raw_lines
|
|
156
|
-
|
|
155
|
+
lines = [ln.strip() for ln in raw_lines]
|
|
156
|
+
# Only leading comments are metadata. In a headed XYZ the second line is
|
|
157
|
+
# the title and may itself begin with '#', so discarding every '#' line
|
|
158
|
+
# would shift the atom rows up one and silently drop the first atom.
|
|
159
|
+
while lines and (not lines[0] or lines[0].startswith("#")):
|
|
157
160
|
lines.pop(0)
|
|
158
161
|
|
|
159
162
|
if not lines:
|
|
@@ -167,9 +170,11 @@ class IOManager:
|
|
|
167
170
|
if num_atoms == 0:
|
|
168
171
|
raise ValueError("XYZ file has zero atoms")
|
|
169
172
|
except ValueError as exc:
|
|
170
|
-
# Not a standard headed XYZ — treat all lines as atom rows
|
|
173
|
+
# Not a standard headed XYZ — treat all lines as atom rows. There is
|
|
174
|
+
# no title line here, so interleaved comments are safe to drop.
|
|
171
175
|
if "zero atoms" in str(exc) or "too few" in str(exc):
|
|
172
176
|
raise
|
|
177
|
+
lines = [ln for ln in lines if ln and not ln.startswith("#")]
|
|
173
178
|
num_atoms = len(lines)
|
|
174
179
|
atom_start = 0
|
|
175
180
|
|
|
@@ -442,6 +447,7 @@ class IOManager:
|
|
|
442
447
|
num_atoms = mol.GetNumAtoms()
|
|
443
448
|
bonds_added = []
|
|
444
449
|
|
|
450
|
+
candidates = []
|
|
445
451
|
for i in range(num_atoms):
|
|
446
452
|
for j in range(i + 1, num_atoms):
|
|
447
453
|
atom_i = mol.GetAtomWithIdx(i)
|
|
@@ -456,21 +462,27 @@ class IOManager:
|
|
|
456
462
|
expected = radius_i + radius_j
|
|
457
463
|
tolerance = 1.2 if (symbol_i == "H" or symbol_j == "H") else 1.3
|
|
458
464
|
if expected * 0.5 <= distance <= expected * tolerance:
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
465
|
+
candidates.append((distance, i, j, symbol_i, symbol_j))
|
|
466
|
+
|
|
467
|
+
# Nearest pairs first. The monovalent-H rule below accepts whichever
|
|
468
|
+
# partner it sees first, so index order would otherwise decide the
|
|
469
|
+
# connectivity and the same geometry could bond an H differently
|
|
470
|
+
# depending only on how the file happened to list its atoms.
|
|
471
|
+
candidates.sort(key=lambda c: c[0])
|
|
472
|
+
|
|
473
|
+
for distance, i, j, symbol_i, symbol_j in candidates:
|
|
474
|
+
if mol.GetBondBetweenAtoms(i, j) is not None:
|
|
475
|
+
continue
|
|
476
|
+
if (symbol_i == "H" and mol.GetAtomWithIdx(i).GetDegree() >= 1) or (
|
|
477
|
+
symbol_j == "H" and mol.GetAtomWithIdx(j).GetDegree() >= 1
|
|
478
|
+
):
|
|
479
|
+
continue
|
|
480
|
+
try:
|
|
481
|
+
mol.AddBond(i, j, Chem.BondType.SINGLE)
|
|
482
|
+
bonds_added.append((i, j, distance))
|
|
483
|
+
except (RuntimeError, ValueError, TypeError):
|
|
484
|
+
# Safe defensive fallback catching RuntimeError, ValueError, TypeError
|
|
485
|
+
logging.debug("Suppressed non-critical error", exc_info=True)
|
|
474
486
|
|
|
475
487
|
return len(bonds_added)
|
|
476
488
|
|
|
@@ -816,6 +828,7 @@ class IOManager:
|
|
|
816
828
|
atom.GetSymbol(),
|
|
817
829
|
QPointF(scene_x, scene_y),
|
|
818
830
|
charge=atom.GetFormalCharge(),
|
|
831
|
+
radical=atom.GetNumRadicalElectrons(),
|
|
819
832
|
)
|
|
820
833
|
rdkit_idx_to_my_id[i] = atom_id
|
|
821
834
|
|
|
@@ -117,10 +117,10 @@ class SettingsOtherTab(SettingsTabBase):
|
|
|
117
117
|
|
|
118
118
|
self.log_to_file_checkbox = QCheckBox()
|
|
119
119
|
self.log_to_file_checkbox.setToolTip(
|
|
120
|
-
"Save application log to ~/.moleditpy/
|
|
120
|
+
"Save application log to ~/.moleditpy/moleditpy.log (rotated, max 1 MB × 3)."
|
|
121
121
|
)
|
|
122
122
|
form_layout.addRow(
|
|
123
|
-
"Save log to file (~/.moleditpy/
|
|
123
|
+
"Save log to file (~/.moleditpy/moleditpy.log):", self.log_to_file_checkbox
|
|
124
124
|
)
|
|
125
125
|
|
|
126
126
|
self.log_level_debug_checkbox = QCheckBox()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/MoleditPy_linux.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/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
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/plugins/plugin_interface.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/plugins/plugin_manager.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/plugins/plugin_manager_window.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/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
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/base_picking_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/calculation_worker.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/color_settings_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/custom_interactor_style.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/custom_qt_interactor.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/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.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/geometry_base_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/molecular_scene_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/move_selected_atoms_dialog.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/periodic_table_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/plugin_menu_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/settings_tabs/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/template_preview_item.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/template_preview_view.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/translation_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/ui/user_template_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/utils/default_settings.py
RENAMED
|
File without changes
|
{moleditpy_linux-4.5.0 → moleditpy_linux-4.5.1}/src/moleditpy_linux/utils/sip_isdeleted_safe.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|