ms-contactmap 0.1.0__py3-none-any.whl
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.
- ms_contactmap/__init__.py +41 -0
- ms_contactmap/__main__.py +96 -0
- ms_contactmap/chem.py +709 -0
- ms_contactmap/detect/__init__.py +14 -0
- ms_contactmap/detect/engine.py +387 -0
- ms_contactmap/detect/geometry.py +762 -0
- ms_contactmap/detect/ligand.py +358 -0
- ms_contactmap/detect/protein.py +510 -0
- ms_contactmap/detect/roles.py +122 -0
- ms_contactmap/export.py +110 -0
- ms_contactmap/fast_layout.py +602 -0
- ms_contactmap/interactions.py +274 -0
- ms_contactmap/json_io.py +277 -0
- ms_contactmap/layout.py +1574 -0
- ms_contactmap/model.py +425 -0
- ms_contactmap/render.py +1588 -0
- ms_contactmap/widget.py +863 -0
- ms_contactmap-0.1.0.dist-info/METADATA +52 -0
- ms_contactmap-0.1.0.dist-info/RECORD +23 -0
- ms_contactmap-0.1.0.dist-info/WHEEL +5 -0
- ms_contactmap-0.1.0.dist-info/entry_points.txt +2 -0
- ms_contactmap-0.1.0.dist-info/licenses/LICENSE +21 -0
- ms_contactmap-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Maestro-style 2D protein-ligand interaction diagrams for PySide6.
|
|
2
|
+
|
|
3
|
+
Public objects are imported lazily to reduce startup time and keep analysis,
|
|
4
|
+
layout and Qt drawing independently usable.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from importlib import import_module
|
|
9
|
+
|
|
10
|
+
_PUBLIC = {
|
|
11
|
+
"Diagram": ("model", "Diagram"),
|
|
12
|
+
"DiagramView": ("widget", "DiagramView"),
|
|
13
|
+
"Interaction": ("model", "Interaction"),
|
|
14
|
+
"InteractionDiagramWidget": ("widget", "InteractionDiagramWidget"),
|
|
15
|
+
"LayoutResult": ("layout", "LayoutResult"),
|
|
16
|
+
"LigandGeometry": ("chem", "LigandGeometry"),
|
|
17
|
+
"Residue": ("model", "Residue"),
|
|
18
|
+
"ResidueRef": ("model", "ResidueRef"),
|
|
19
|
+
"build_diagram": ("interactions", "build_diagram"),
|
|
20
|
+
"build_scene": ("render", "build_scene"),
|
|
21
|
+
"document_from_dict": ("json_io", "document_from_dict"),
|
|
22
|
+
"document_to_dict": ("json_io", "document_to_dict"),
|
|
23
|
+
"export_png": ("export", "export_png"),
|
|
24
|
+
"export_svg": ("export", "export_svg"),
|
|
25
|
+
"load_json": ("json_io", "load_json"),
|
|
26
|
+
"load_ligand": ("chem", "load_ligand"),
|
|
27
|
+
"save_json": ("json_io", "save_json"),
|
|
28
|
+
"solve_layout": ("layout", "solve_layout"),
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
__all__ = list(_PUBLIC)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def __getattr__(name: str):
|
|
35
|
+
try:
|
|
36
|
+
module_name, attribute = _PUBLIC[name]
|
|
37
|
+
except KeyError as exc:
|
|
38
|
+
raise AttributeError(name) from exc
|
|
39
|
+
value = getattr(import_module(f".{module_name}", __name__), attribute)
|
|
40
|
+
globals()[name] = value
|
|
41
|
+
return value
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Command line entry point.
|
|
2
|
+
|
|
3
|
+
python -m ms_contactmap data/4ps5.pdb --ligand 2TA --smiles "..." --json out.json
|
|
4
|
+
python -m ms_contactmap --from-json out.json --png out.png
|
|
5
|
+
python -m ms_contactmap --from-json out.json --show
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def main(argv: list[str] | None = None) -> int:
|
|
15
|
+
parser = argparse.ArgumentParser(prog="ms_contactmap", description=__doc__)
|
|
16
|
+
parser.add_argument("pdb", nargs="?", type=Path, help="PDB file of the complex")
|
|
17
|
+
parser.add_argument("--from-json", type=Path, default=None,
|
|
18
|
+
help="visualize a previously computed diagram JSON")
|
|
19
|
+
parser.add_argument("--ligand", help="ligand residue name, e.g. 2TA")
|
|
20
|
+
parser.add_argument("--chain", default=None, help="restrict to this chain")
|
|
21
|
+
parser.add_argument("--resnum", type=int, default=None, help="restrict to this residue number")
|
|
22
|
+
parser.add_argument("--smiles", default=None, help="ligand SMILES (required with a PDB input)")
|
|
23
|
+
parser.add_argument("--no-exposure", action="store_true",
|
|
24
|
+
help="skip SASA halos for a faster first analysis")
|
|
25
|
+
parser.add_argument("--json", dest="json_output", type=Path, default=None,
|
|
26
|
+
help="write reusable analysis and layout JSON here")
|
|
27
|
+
parser.add_argument("--png", type=Path, default=None, help="write a PNG here")
|
|
28
|
+
parser.add_argument("--svg", type=Path, default=None, help="write an SVG here")
|
|
29
|
+
parser.add_argument("--scale", type=float, default=2.0, help="PNG scale factor")
|
|
30
|
+
parser.add_argument("--transparent", action="store_true", help="PNG without white background")
|
|
31
|
+
parser.add_argument("--show", action="store_true", help="open the interactive window")
|
|
32
|
+
args = parser.parse_args(argv)
|
|
33
|
+
|
|
34
|
+
if not args.png and not args.svg and not args.json_output and not args.show:
|
|
35
|
+
parser.error("nothing to do: pass --json, --png, --svg or --show")
|
|
36
|
+
if args.from_json and args.pdb:
|
|
37
|
+
parser.error("choose either a PDB input or --from-json, not both")
|
|
38
|
+
if not args.from_json and not args.pdb:
|
|
39
|
+
parser.error("pass a PDB input or --from-json")
|
|
40
|
+
if not args.from_json and not args.ligand:
|
|
41
|
+
parser.error("--ligand is required with a PDB input")
|
|
42
|
+
if not args.from_json and not args.smiles:
|
|
43
|
+
parser.error("--smiles is required with a PDB input")
|
|
44
|
+
|
|
45
|
+
from .export import ensure_app
|
|
46
|
+
app = ensure_app()
|
|
47
|
+
from .widget import InteractionDiagramWidget
|
|
48
|
+
|
|
49
|
+
if args.from_json:
|
|
50
|
+
widget = InteractionDiagramWidget.from_json(args.from_json)
|
|
51
|
+
diagram = widget.diagram
|
|
52
|
+
else:
|
|
53
|
+
from .interactions import build_diagram
|
|
54
|
+
|
|
55
|
+
diagram = build_diagram(
|
|
56
|
+
args.pdb,
|
|
57
|
+
args.ligand,
|
|
58
|
+
args.smiles,
|
|
59
|
+
chain=args.chain,
|
|
60
|
+
resnum=args.resnum,
|
|
61
|
+
name=args.pdb.stem,
|
|
62
|
+
compute_exposure=not args.no_exposure,
|
|
63
|
+
)
|
|
64
|
+
# Batch exports need a solved scene immediately. A show-only command
|
|
65
|
+
# displays the window first and solves in the responsive Qt thread.
|
|
66
|
+
widget = InteractionDiagramWidget(
|
|
67
|
+
diagram if (args.png or args.svg or args.json_output) else None
|
|
68
|
+
)
|
|
69
|
+
assert diagram is not None
|
|
70
|
+
print(
|
|
71
|
+
f"{diagram.name}: {diagram.ligand_name}, "
|
|
72
|
+
f"{len(diagram.residues)} residues, {len(diagram.interactions)} interactions",
|
|
73
|
+
file=sys.stderr,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
if args.json_output:
|
|
77
|
+
widget.export_json(args.json_output)
|
|
78
|
+
print(f"wrote {args.json_output}", file=sys.stderr)
|
|
79
|
+
if args.png:
|
|
80
|
+
widget.export_png(args.png, scale=args.scale, background=None if args.transparent else "#ffffff")
|
|
81
|
+
print(f"wrote {args.png}", file=sys.stderr)
|
|
82
|
+
if args.svg:
|
|
83
|
+
widget.export_svg(args.svg)
|
|
84
|
+
print(f"wrote {args.svg}", file=sys.stderr)
|
|
85
|
+
if args.show:
|
|
86
|
+
widget.resize(1100, 780)
|
|
87
|
+
widget.setWindowTitle(f"{diagram.name} - {diagram.ligand_name}")
|
|
88
|
+
widget.show()
|
|
89
|
+
if widget.layout_result is None:
|
|
90
|
+
widget.set_diagram_async(diagram)
|
|
91
|
+
return app.exec()
|
|
92
|
+
return 0
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
if __name__ == "__main__":
|
|
96
|
+
raise SystemExit(main())
|