matplotly 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.
- matplotly/__init__.py +124 -0
- matplotly/_api.py +984 -0
- matplotly/_code_gen.py +1793 -0
- matplotly/_commands.py +109 -0
- matplotly/_introspect.py +1197 -0
- matplotly/_profiles.py +241 -0
- matplotly/_renderer.py +79 -0
- matplotly/_style_import.py +155 -0
- matplotly/_types.py +31 -0
- matplotly/panels/__init__.py +37 -0
- matplotly/panels/_bar.py +788 -0
- matplotly/panels/_base.py +38 -0
- matplotly/panels/_color_utils.py +221 -0
- matplotly/panels/_distribution.py +1605 -0
- matplotly/panels/_errorbar.py +652 -0
- matplotly/panels/_fill.py +90 -0
- matplotly/panels/_global.py +1507 -0
- matplotly/panels/_heatmap.py +898 -0
- matplotly/panels/_histogram.py +938 -0
- matplotly/panels/_line.py +709 -0
- matplotly/panels/_marginal.py +944 -0
- matplotly/panels/_scatter.py +428 -0
- matplotly/panels/_subplot.py +846 -0
- matplotly-0.1.0.dist-info/METADATA +120 -0
- matplotly-0.1.0.dist-info/RECORD +27 -0
- matplotly-0.1.0.dist-info/WHEEL +4 -0
- matplotly-0.1.0.dist-info/licenses/LICENSE +21 -0
matplotly/_commands.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Undo/redo command system for figure modifications."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import Any, Callable
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class Command:
|
|
10
|
+
"""A single property change on a matplotlib artist."""
|
|
11
|
+
|
|
12
|
+
artist: Any
|
|
13
|
+
property_name: str
|
|
14
|
+
old_value: Any
|
|
15
|
+
new_value: Any
|
|
16
|
+
# For complex operations that can't use simple set_*
|
|
17
|
+
apply_fn: Callable[[], None] | None = None
|
|
18
|
+
revert_fn: Callable[[], None] | None = None
|
|
19
|
+
# Human-readable description for code gen
|
|
20
|
+
description: str = ""
|
|
21
|
+
|
|
22
|
+
def execute(self) -> None:
|
|
23
|
+
if self.apply_fn is not None:
|
|
24
|
+
self.apply_fn()
|
|
25
|
+
else:
|
|
26
|
+
setter = getattr(self.artist, f"set_{self.property_name}", None)
|
|
27
|
+
if setter is not None:
|
|
28
|
+
setter(self.new_value)
|
|
29
|
+
|
|
30
|
+
def undo(self) -> None:
|
|
31
|
+
if self.revert_fn is not None:
|
|
32
|
+
self.revert_fn()
|
|
33
|
+
else:
|
|
34
|
+
setter = getattr(self.artist, f"set_{self.property_name}", None)
|
|
35
|
+
if setter is not None:
|
|
36
|
+
setter(self.old_value)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class BatchCommand:
|
|
41
|
+
"""A group of commands applied/reverted together as one undo step."""
|
|
42
|
+
|
|
43
|
+
commands: list[Command] = field(default_factory=list)
|
|
44
|
+
description: str = ""
|
|
45
|
+
|
|
46
|
+
def execute(self) -> None:
|
|
47
|
+
for cmd in self.commands:
|
|
48
|
+
cmd.execute()
|
|
49
|
+
|
|
50
|
+
def undo(self) -> None:
|
|
51
|
+
for cmd in reversed(self.commands):
|
|
52
|
+
cmd.undo()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class CommandStack:
|
|
56
|
+
"""Manages undo/redo stacks with a maximum depth."""
|
|
57
|
+
|
|
58
|
+
def __init__(self, max_depth: int = 100,
|
|
59
|
+
on_change: Callable[[], None] | None = None):
|
|
60
|
+
self._undo_stack: list[Command | BatchCommand] = []
|
|
61
|
+
self._redo_stack: list[Command | BatchCommand] = []
|
|
62
|
+
self._max_depth = max_depth
|
|
63
|
+
self._on_change = on_change
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def can_undo(self) -> bool:
|
|
67
|
+
return len(self._undo_stack) > 0
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def can_redo(self) -> bool:
|
|
71
|
+
return len(self._redo_stack) > 0
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def history(self) -> list[Command | BatchCommand]:
|
|
75
|
+
return list(self._undo_stack)
|
|
76
|
+
|
|
77
|
+
def execute(self, cmd: Command | BatchCommand) -> None:
|
|
78
|
+
"""Execute a command and push it onto the undo stack."""
|
|
79
|
+
cmd.execute()
|
|
80
|
+
self._undo_stack.append(cmd)
|
|
81
|
+
if len(self._undo_stack) > self._max_depth:
|
|
82
|
+
self._undo_stack.pop(0)
|
|
83
|
+
self._redo_stack.clear()
|
|
84
|
+
if self._on_change:
|
|
85
|
+
self._on_change()
|
|
86
|
+
|
|
87
|
+
def undo(self) -> None:
|
|
88
|
+
if not self._undo_stack:
|
|
89
|
+
return
|
|
90
|
+
cmd = self._undo_stack.pop()
|
|
91
|
+
cmd.undo()
|
|
92
|
+
self._redo_stack.append(cmd)
|
|
93
|
+
if self._on_change:
|
|
94
|
+
self._on_change()
|
|
95
|
+
|
|
96
|
+
def redo(self) -> None:
|
|
97
|
+
if not self._redo_stack:
|
|
98
|
+
return
|
|
99
|
+
cmd = self._redo_stack.pop()
|
|
100
|
+
cmd.execute()
|
|
101
|
+
self._undo_stack.append(cmd)
|
|
102
|
+
if self._on_change:
|
|
103
|
+
self._on_change()
|
|
104
|
+
|
|
105
|
+
def clear(self) -> None:
|
|
106
|
+
self._undo_stack.clear()
|
|
107
|
+
self._redo_stack.clear()
|
|
108
|
+
if self._on_change:
|
|
109
|
+
self._on_change()
|