mujoco-toolbox 0.2.0__tar.gz → 0.2.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.
- {mujoco_toolbox-0.2.0 → mujoco_toolbox-0.2.2}/PKG-INFO +2 -2
- {mujoco_toolbox-0.2.0 → mujoco_toolbox-0.2.2}/README.md +1 -1
- {mujoco_toolbox-0.2.0 → mujoco_toolbox-0.2.2}/mujoco_toolbox/__init__.py +20 -28
- {mujoco_toolbox-0.2.0 → mujoco_toolbox-0.2.2}/mujoco_toolbox/assets.py +1 -1
- {mujoco_toolbox-0.2.0 → mujoco_toolbox-0.2.2}/mujoco_toolbox/builder.py +39 -32
- mujoco_toolbox-0.2.0/mujoco_toolbox/Controller.py → mujoco_toolbox-0.2.2/mujoco_toolbox/controller.py +31 -20
- mujoco_toolbox-0.2.0/mujoco_toolbox/Utils.py → mujoco_toolbox-0.2.2/mujoco_toolbox/utils.py +9 -13
- mujoco_toolbox-0.2.0/mujoco_toolbox/Wrapper.py → mujoco_toolbox-0.2.2/mujoco_toolbox/wrapper.py +162 -138
- mujoco_toolbox-0.2.2/pyproject.toml +127 -0
- mujoco_toolbox-0.2.0/pyproject.toml +0 -95
- {mujoco_toolbox-0.2.0 → mujoco_toolbox-0.2.2}/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: mujoco-toolbox
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: A modern package to handle MuJoCo environments
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: MuJoCo,robotics,simulation,toolbox,wrapper
|
|
@@ -38,6 +38,7 @@ Description-Content-Type: text/markdown
|
|
|
38
38
|

|
|
39
39
|

|
|
40
40
|
[](https://github.com/MGross21/mujoco-toolbox/actions/workflows/publish.yml)
|
|
41
|
+
[](https://github.com/MGross21/mujoco-toolbox/actions/workflows/docs.yml)
|
|
41
42
|
|
|
42
43
|
# Mujoco Toolbox
|
|
43
44
|
|
|
@@ -102,7 +103,6 @@ ffmpeg -version
|
|
|
102
103
|
import mujoco_toolbox as mjtb
|
|
103
104
|
|
|
104
105
|
# Optional Global Variables
|
|
105
|
-
mjtb.VERBOSITY = True # Additional Performance and Debugging Print Statements (Default=False)
|
|
106
106
|
mjtb.CAPTURE_PARAMETERS = "all" # MjData Simulation Parameters (Default = ['time', 'qpos', 'qvel', 'act', 'qacc', 'xpos', 'xquat', 'xmat', 'ctrl', 'sensordata'])
|
|
107
107
|
|
|
108
108
|
# Example Code
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|

|
|
3
3
|

|
|
4
4
|
[](https://github.com/MGross21/mujoco-toolbox/actions/workflows/publish.yml)
|
|
5
|
+
[](https://github.com/MGross21/mujoco-toolbox/actions/workflows/docs.yml)
|
|
5
6
|
|
|
6
7
|
# Mujoco Toolbox
|
|
7
8
|
|
|
@@ -66,7 +67,6 @@ ffmpeg -version
|
|
|
66
67
|
import mujoco_toolbox as mjtb
|
|
67
68
|
|
|
68
69
|
# Optional Global Variables
|
|
69
|
-
mjtb.VERBOSITY = True # Additional Performance and Debugging Print Statements (Default=False)
|
|
70
70
|
mjtb.CAPTURE_PARAMETERS = "all" # MjData Simulation Parameters (Default = ['time', 'qpos', 'qvel', 'act', 'qacc', 'xpos', 'xquat', 'xmat', 'ctrl', 'sensordata'])
|
|
71
71
|
|
|
72
72
|
# Example Code
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Mujoco Toolbox
|
|
1
|
+
"""Mujoco Toolbox.
|
|
3
2
|
==============
|
|
4
3
|
A toolbox for working with MuJoCo simulations. This package provides various utilities and controllers to facilitate
|
|
5
4
|
the simulation process.
|
|
@@ -13,43 +12,42 @@ Modules:
|
|
|
13
12
|
Constants:
|
|
14
13
|
----------
|
|
15
14
|
- CAPTURE_PARAMETERS: List of MjData fields to capture during simulation.
|
|
16
|
-
- VERBOSITY: Global variable to control verbosity of the package.
|
|
17
15
|
|
|
18
|
-
Notes
|
|
19
|
-
|
|
16
|
+
Notes
|
|
17
|
+
-----
|
|
20
18
|
This package is still under development. Report any issues to https://github.com/MGross21/mujoco-toolbox/issues.
|
|
19
|
+
|
|
21
20
|
"""
|
|
22
21
|
|
|
23
22
|
from .assets import WORLD_ASSETS, GloveBox
|
|
24
23
|
from .builder import Builder
|
|
25
|
-
from .
|
|
26
|
-
from .
|
|
27
|
-
from .
|
|
24
|
+
from .controller import cosineController, randomController, realTimeController, sineController, stepController
|
|
25
|
+
from .utils import _Platform, print_success, print_warning, timer
|
|
26
|
+
from .wrapper import Wrapper
|
|
28
27
|
|
|
29
|
-
__version__ = "0.2.
|
|
28
|
+
__version__ = "0.2.2"
|
|
30
29
|
__author__ = "Michael Gross"
|
|
31
30
|
__github_repo__ = "mujoco-toolbox"
|
|
32
31
|
__license__ = "MIT"
|
|
33
32
|
__status__ = "Development"
|
|
34
|
-
|
|
35
|
-
# `from mujoco_toolbox import *` will import these objects
|
|
36
33
|
__all__ = [
|
|
34
|
+
"CAPTURE_PARAMETERS",
|
|
35
|
+
"COMPUTER",
|
|
36
|
+
"WORLD_ASSETS",
|
|
37
|
+
"Builder",
|
|
38
|
+
"GloveBox",
|
|
37
39
|
"Wrapper",
|
|
38
|
-
"sineController",
|
|
39
40
|
"cosineController",
|
|
40
41
|
"randomController",
|
|
41
|
-
"stepController",
|
|
42
42
|
"realTimeController",
|
|
43
|
+
"sineController",
|
|
44
|
+
"stepController",
|
|
43
45
|
"timer",
|
|
44
|
-
"Computer",
|
|
45
|
-
"Builder",
|
|
46
|
-
"GloveBox",
|
|
47
|
-
"WORLD_ASSETS",
|
|
48
|
-
"CAPTURE_PARAMETERS",
|
|
49
|
-
"VERBOSITY",
|
|
50
46
|
]
|
|
51
47
|
|
|
52
|
-
|
|
48
|
+
COMPUTER = _Platform() # Singleton Instance
|
|
49
|
+
MAX_GEOM_SCALAR: int = 2 # Scalar value for mujoco.Renderer.max_geom
|
|
50
|
+
CAPTURE_PARAMETERS = [ # MjData default fields to capture during simulation
|
|
53
51
|
"time",
|
|
54
52
|
"qpos",
|
|
55
53
|
"qvel",
|
|
@@ -60,16 +58,10 @@ CAPTURE_PARAMETERS = [
|
|
|
60
58
|
"xmat",
|
|
61
59
|
"ctrl",
|
|
62
60
|
"sensordata",
|
|
63
|
-
]
|
|
64
|
-
|
|
65
|
-
VERBOSITY = False
|
|
61
|
+
]
|
|
66
62
|
|
|
67
|
-
if
|
|
68
|
-
from .Utils import print_warning
|
|
63
|
+
if __version__.split(".")[0] == "0":
|
|
69
64
|
print_warning(
|
|
70
65
|
f"{__package__} (v{__version__}) is still under development.",
|
|
71
66
|
f"Report any issues to https://github.com/MGross21/{__github_repo__}/issues",
|
|
72
67
|
)
|
|
73
|
-
|
|
74
|
-
# Create a singleton instance
|
|
75
|
-
Computer = _Platform()
|
|
@@ -3,14 +3,15 @@ import xml.etree.ElementTree as ET
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class Builder:
|
|
6
|
-
def __init__(self, xml_input: str):
|
|
7
|
-
"""
|
|
6
|
+
def __init__(self, xml_input: str) -> None:
|
|
7
|
+
"""Initialize with an XML string or a file path. Input is required."""
|
|
8
8
|
self.tree, self.root = self.load_model(xml_input)
|
|
9
9
|
|
|
10
10
|
def load_model(self, xml_input):
|
|
11
|
-
"""
|
|
11
|
+
"""Load a MuJoCo model from a file or XML string."""
|
|
12
12
|
if not isinstance(xml_input, str):
|
|
13
|
-
|
|
13
|
+
msg = "Input must be either an XML string or a file path"
|
|
14
|
+
raise ValueError(msg)
|
|
14
15
|
|
|
15
16
|
# Check if input is an XML string
|
|
16
17
|
if xml_input.strip().startswith("<"):
|
|
@@ -31,11 +32,13 @@ class Builder:
|
|
|
31
32
|
|
|
32
33
|
return tree, tree.getroot()
|
|
33
34
|
except ET.ParseError:
|
|
34
|
-
|
|
35
|
+
msg = "Invalid XML string provided"
|
|
36
|
+
raise ValueError(msg)
|
|
35
37
|
else:
|
|
36
38
|
# Treat input as file path
|
|
37
39
|
if not os.path.exists(xml_input):
|
|
38
|
-
|
|
40
|
+
msg = f"File not found: {xml_input}"
|
|
41
|
+
raise FileNotFoundError(msg)
|
|
39
42
|
|
|
40
43
|
# Handle URDF files
|
|
41
44
|
if xml_input.lower().endswith(".urdf"):
|
|
@@ -54,11 +57,11 @@ class Builder:
|
|
|
54
57
|
|
|
55
58
|
return tree, tree.getroot()
|
|
56
59
|
except ET.ParseError:
|
|
57
|
-
|
|
60
|
+
msg = f"Invalid XML file: {xml_input}"
|
|
61
|
+
raise ValueError(msg)
|
|
58
62
|
|
|
59
63
|
def wrap_urdf_in_mujoco(self, urdf_input):
|
|
60
|
-
"""
|
|
61
|
-
Wrap a URDF inside <mujoco> tags for MuJoCo compatibility.
|
|
64
|
+
"""Wrap a URDF inside <mujoco> tags for MuJoCo compatibility.
|
|
62
65
|
Input can be a file path, ElementTree, or XML string.
|
|
63
66
|
"""
|
|
64
67
|
# Handle string input (could be a file path or XML string)
|
|
@@ -69,18 +72,21 @@ class Builder:
|
|
|
69
72
|
root = ET.fromstring(urdf_input)
|
|
70
73
|
tree = ET.ElementTree(root)
|
|
71
74
|
except ET.ParseError:
|
|
72
|
-
|
|
75
|
+
msg = "Invalid URDF XML string provided"
|
|
76
|
+
raise ValueError(msg)
|
|
73
77
|
else:
|
|
74
78
|
# It's a file path
|
|
75
79
|
try:
|
|
76
80
|
tree = ET.parse(urdf_input)
|
|
77
81
|
except:
|
|
78
|
-
|
|
82
|
+
msg = f"Could not parse URDF file: {urdf_input}"
|
|
83
|
+
raise ValueError(msg)
|
|
79
84
|
# Handle ElementTree input
|
|
80
85
|
elif isinstance(urdf_input, ET.ElementTree):
|
|
81
86
|
tree = urdf_input
|
|
82
87
|
else:
|
|
83
|
-
|
|
88
|
+
msg = "URDF input must be a string or ElementTree"
|
|
89
|
+
raise TypeError(msg)
|
|
84
90
|
|
|
85
91
|
root = tree.getroot()
|
|
86
92
|
|
|
@@ -95,8 +101,8 @@ class Builder:
|
|
|
95
101
|
# Create a new tree with the <mujoco> root
|
|
96
102
|
return ET.ElementTree(mujoco_elem), mujoco_elem
|
|
97
103
|
|
|
98
|
-
def merge_tags(self, tag_name, root_1, root_2):
|
|
99
|
-
"""
|
|
104
|
+
def merge_tags(self, tag_name, root_1, root_2) -> None:
|
|
105
|
+
"""Merge a specific tag (e.g., worldbody, asset) from two models."""
|
|
100
106
|
section_1 = root_1.find(tag_name)
|
|
101
107
|
section_2 = root_2.find(tag_name)
|
|
102
108
|
|
|
@@ -111,7 +117,7 @@ class Builder:
|
|
|
111
117
|
section_1.append(element)
|
|
112
118
|
|
|
113
119
|
def __add__(self, other):
|
|
114
|
-
"""
|
|
120
|
+
"""Implement the + operator for merging two models."""
|
|
115
121
|
model_root = None
|
|
116
122
|
|
|
117
123
|
if isinstance(other, Builder):
|
|
@@ -121,7 +127,8 @@ class Builder:
|
|
|
121
127
|
# If the input is a string, load as XML
|
|
122
128
|
_, model_root = self.load_model(other)
|
|
123
129
|
else:
|
|
124
|
-
|
|
130
|
+
msg = "Addition only supported between Builder objects or with XML strings/files"
|
|
131
|
+
raise TypeError(msg)
|
|
125
132
|
|
|
126
133
|
# List of sections to merge
|
|
127
134
|
sections_to_merge = ["asset", "worldbody", "camera", "light", "contact", "equality",
|
|
@@ -135,22 +142,23 @@ class Builder:
|
|
|
135
142
|
return self
|
|
136
143
|
|
|
137
144
|
def __radd__(self, other):
|
|
138
|
-
"""
|
|
145
|
+
"""Implement the reverse + operator for merging two models."""
|
|
139
146
|
if other == 0: # Handle sum() starting value
|
|
140
147
|
return self
|
|
141
148
|
return self.__add__(other)
|
|
142
149
|
|
|
143
|
-
def save(self, file_path):
|
|
144
|
-
"""
|
|
150
|
+
def save(self, file_path) -> None:
|
|
151
|
+
"""Save the merged model to a file."""
|
|
145
152
|
if self.tree is not None:
|
|
146
153
|
# Format the XML with proper indentation before saving
|
|
147
154
|
self._indent_xml(self.root)
|
|
148
155
|
self.tree.write(file_path, encoding="utf-8", xml_declaration=True)
|
|
149
156
|
else:
|
|
150
|
-
|
|
157
|
+
msg = "No model loaded. Cannot save."
|
|
158
|
+
raise ValueError(msg)
|
|
151
159
|
|
|
152
|
-
def _indent_xml(self, elem, level=0):
|
|
153
|
-
"""
|
|
160
|
+
def _indent_xml(self, elem, level=0) -> None:
|
|
161
|
+
"""Add proper indentation to make the XML file more readable."""
|
|
154
162
|
i = "\n" + level * " "
|
|
155
163
|
if len(elem):
|
|
156
164
|
if not elem.text or not elem.text.strip():
|
|
@@ -161,24 +169,23 @@ class Builder:
|
|
|
161
169
|
self._indent_xml(sub_elem, level + 1)
|
|
162
170
|
if not elem[-1].tail or not elem[-1].tail.strip():
|
|
163
171
|
elem[-1].tail = i
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
elem.tail = i
|
|
172
|
+
elif level and (not elem.tail or not elem.tail.strip()):
|
|
173
|
+
elem.tail = i
|
|
167
174
|
|
|
168
|
-
def __str__(self):
|
|
169
|
-
"""
|
|
175
|
+
def __str__(self) -> str:
|
|
176
|
+
"""Return the XML string of the model."""
|
|
170
177
|
if self.tree is not None:
|
|
171
178
|
# Format the XML with proper indentation
|
|
172
179
|
self._indent_xml(self.root)
|
|
173
180
|
return ET.tostring(self.root, encoding="unicode", method="xml")
|
|
174
|
-
|
|
175
|
-
|
|
181
|
+
msg = "No model loaded. Cannot generate string."
|
|
182
|
+
raise ValueError(msg)
|
|
176
183
|
|
|
177
|
-
def __repr__(self):
|
|
178
|
-
"""
|
|
184
|
+
def __repr__(self) -> str:
|
|
185
|
+
"""Return the XML string of the model."""
|
|
179
186
|
return self.__str__()
|
|
180
187
|
|
|
181
188
|
@property
|
|
182
189
|
def xml(self):
|
|
183
|
-
"""
|
|
190
|
+
"""Property to quickly grab the XML string of the model."""
|
|
184
191
|
return str(self)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from numpy import cos, pi, random, sin
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
def _apply_control(model, data, value, joint=None, axis=None, delay=0):
|
|
4
|
+
def _apply_control(model, data, value, joint=None, axis=None, delay=0) -> None:
|
|
5
5
|
"""Common helper function for controller logic to reduce redundancy.
|
|
6
6
|
|
|
7
7
|
Args:
|
|
@@ -11,6 +11,7 @@ def _apply_control(model, data, value, joint=None, axis=None, delay=0):
|
|
|
11
11
|
joint (list[int], optional): Joints to apply control to.
|
|
12
12
|
axis (int, optional): Axis to apply control to.
|
|
13
13
|
delay (float, optional): Delay before applying control.
|
|
14
|
+
|
|
14
15
|
"""
|
|
15
16
|
# Don't apply control until after delay
|
|
16
17
|
if data.time < delay:
|
|
@@ -28,7 +29,7 @@ def _apply_control(model, data, value, joint=None, axis=None, delay=0):
|
|
|
28
29
|
data.qpos[axis] = value
|
|
29
30
|
|
|
30
31
|
|
|
31
|
-
def stepController(model, data, **kwargs):
|
|
32
|
+
def stepController(model, data, **kwargs) -> None:
|
|
32
33
|
"""A step controller for the simulation.
|
|
33
34
|
|
|
34
35
|
Args:
|
|
@@ -39,21 +40,24 @@ def stepController(model, data, **kwargs):
|
|
|
39
40
|
|
|
40
41
|
Returns:
|
|
41
42
|
None
|
|
43
|
+
|
|
42
44
|
"""
|
|
43
45
|
amplitude = kwargs.get("amplitude", 1)
|
|
44
|
-
joint = kwargs.get("joint"
|
|
45
|
-
axis = kwargs.get("axis"
|
|
46
|
+
joint = kwargs.get("joint")
|
|
47
|
+
axis = kwargs.get("axis")
|
|
46
48
|
delay = kwargs.get("delay", 0)
|
|
47
49
|
|
|
48
50
|
if delay < 0:
|
|
49
|
-
|
|
51
|
+
msg = "Delay must be non-negative."
|
|
52
|
+
raise ValueError(msg)
|
|
50
53
|
if joint is not None and axis is not None:
|
|
51
|
-
|
|
54
|
+
msg = "Cannot specify both 'joint' and 'axis'."
|
|
55
|
+
raise ValueError(msg)
|
|
52
56
|
|
|
53
57
|
_apply_control(model, data, amplitude, joint=joint, axis=axis, delay=delay)
|
|
54
58
|
|
|
55
59
|
|
|
56
|
-
def sineController(model, data, **kwargs):
|
|
60
|
+
def sineController(model, data, **kwargs) -> None:
|
|
57
61
|
"""A simple sine wave controller for the simulation.
|
|
58
62
|
|
|
59
63
|
Args:
|
|
@@ -65,21 +69,23 @@ def sineController(model, data, **kwargs):
|
|
|
65
69
|
|
|
66
70
|
Returns:
|
|
67
71
|
None
|
|
72
|
+
|
|
68
73
|
"""
|
|
69
74
|
amplitude = kwargs.get("amplitude", 1)
|
|
70
75
|
frequency = kwargs.get("frequency", 1)
|
|
71
76
|
phase = kwargs.get("phase", 0)
|
|
72
|
-
joint = kwargs.get("joint"
|
|
77
|
+
joint = kwargs.get("joint")
|
|
73
78
|
delay = kwargs.get("delay", 0)
|
|
74
79
|
|
|
75
80
|
if delay < 0:
|
|
76
|
-
|
|
81
|
+
msg = "Delay must be non-negative."
|
|
82
|
+
raise ValueError(msg)
|
|
77
83
|
|
|
78
84
|
value = amplitude * sin(2 * pi * frequency * data.time + phase)
|
|
79
85
|
_apply_control(model, data, value, joint=joint, delay=delay)
|
|
80
86
|
|
|
81
87
|
|
|
82
|
-
def cosineController(model, data, **kwargs):
|
|
88
|
+
def cosineController(model, data, **kwargs) -> None:
|
|
83
89
|
"""A simple cosine wave controller for the simulation.
|
|
84
90
|
|
|
85
91
|
Args:
|
|
@@ -91,21 +97,23 @@ def cosineController(model, data, **kwargs):
|
|
|
91
97
|
|
|
92
98
|
Returns:
|
|
93
99
|
None
|
|
100
|
+
|
|
94
101
|
"""
|
|
95
102
|
amplitude = kwargs.get("amplitude", 1)
|
|
96
103
|
frequency = kwargs.get("frequency", 1)
|
|
97
104
|
phase = kwargs.get("phase", 0)
|
|
98
|
-
joint = kwargs.get("joint"
|
|
105
|
+
joint = kwargs.get("joint")
|
|
99
106
|
delay = kwargs.get("delay", 0)
|
|
100
107
|
|
|
101
108
|
if delay < 0:
|
|
102
|
-
|
|
109
|
+
msg = "Delay must be non-negative."
|
|
110
|
+
raise ValueError(msg)
|
|
103
111
|
|
|
104
112
|
value = amplitude * cos(2 * pi * frequency * data.time + phase)
|
|
105
113
|
_apply_control(model, data, value, joint=joint, delay=delay)
|
|
106
114
|
|
|
107
115
|
|
|
108
|
-
def randomController(model, data, **kwargs):
|
|
116
|
+
def randomController(model, data, **kwargs) -> None:
|
|
109
117
|
"""A random controller for the simulation.
|
|
110
118
|
|
|
111
119
|
Args:
|
|
@@ -116,21 +124,24 @@ def randomController(model, data, **kwargs):
|
|
|
116
124
|
|
|
117
125
|
Returns:
|
|
118
126
|
None
|
|
127
|
+
|
|
119
128
|
"""
|
|
120
129
|
amplitude = kwargs.get("amplitude", 1)
|
|
121
|
-
joint = kwargs.get("joint"
|
|
122
|
-
axis = kwargs.get("axis"
|
|
130
|
+
joint = kwargs.get("joint")
|
|
131
|
+
axis = kwargs.get("axis")
|
|
123
132
|
delay = kwargs.get("delay", 0)
|
|
124
133
|
|
|
125
134
|
if delay < 0:
|
|
126
|
-
|
|
135
|
+
msg = "Delay must be non-negative."
|
|
136
|
+
raise ValueError(msg)
|
|
127
137
|
if joint is not None and axis is not None:
|
|
128
|
-
|
|
138
|
+
msg = "Cannot specify both 'joint' and 'axis'."
|
|
139
|
+
raise ValueError(msg)
|
|
129
140
|
|
|
130
141
|
value = amplitude * random.rand()
|
|
131
142
|
_apply_control(model, data, value, joint=joint, axis=axis, delay=delay)
|
|
132
143
|
|
|
133
|
-
def realTimeController(model, data, **kwargs):
|
|
144
|
+
def realTimeController(model, data, **kwargs) -> None:
|
|
134
145
|
"""A real-time controller for the simulation.
|
|
135
146
|
|
|
136
147
|
Args:
|
|
@@ -138,8 +149,9 @@ def realTimeController(model, data, **kwargs):
|
|
|
138
149
|
|
|
139
150
|
Returns:
|
|
140
151
|
None
|
|
152
|
+
|
|
141
153
|
"""
|
|
142
|
-
from .
|
|
154
|
+
from .utils import print_warning
|
|
143
155
|
for key, value in kwargs.get("controller_params", {}).items():
|
|
144
156
|
if hasattr(data, key):
|
|
145
157
|
setattr(data, key, value)
|
|
@@ -150,4 +162,3 @@ def realTimeController(model, data, **kwargs):
|
|
|
150
162
|
# if hasattr(data, 'control'):
|
|
151
163
|
# data.control = kwargs.get("control", data.control)
|
|
152
164
|
|
|
153
|
-
return
|
|
@@ -15,21 +15,17 @@ def timer(func):
|
|
|
15
15
|
result = func(*args, **kwargs)
|
|
16
16
|
end_time = time()
|
|
17
17
|
total_time = end_time - start_time
|
|
18
|
-
|
|
19
|
-
from . import VERBOSITY
|
|
20
|
-
|
|
21
|
-
if VERBOSITY:
|
|
22
|
-
print(f"Function '{func.__name__}' took {total_time:.4f} seconds")
|
|
18
|
+
print(f"Function {func.__name__} took {total_time:.4f} seconds to execute")
|
|
23
19
|
return result
|
|
24
20
|
|
|
25
21
|
return time_wrapper
|
|
26
22
|
|
|
27
23
|
def print_warning(*args, prefix: bool = True) -> None:
|
|
28
|
-
"""
|
|
29
|
-
Prints the first argument as an orange warning message and prints the other arguments in regular text, all on the same line.
|
|
24
|
+
"""Prints the first argument as an orange warning message and prints the other arguments in regular text, all on the same line.
|
|
30
25
|
|
|
31
26
|
Args:
|
|
32
27
|
*args: First argument is printed as an orange warning, other arguments are printed normally.
|
|
28
|
+
|
|
33
29
|
"""
|
|
34
30
|
if args:
|
|
35
31
|
if prefix:
|
|
@@ -54,11 +50,11 @@ def print_warning(*args, prefix: bool = True) -> None:
|
|
|
54
50
|
print(*args[1:])
|
|
55
51
|
|
|
56
52
|
def print_success(*args, prefix: bool = True) -> None:
|
|
57
|
-
"""
|
|
58
|
-
Prints the first argument as a green success message and prints the other arguments in regular text, all on the same line.
|
|
53
|
+
"""Prints the first argument as a green success message and prints the other arguments in regular text, all on the same line.
|
|
59
54
|
|
|
60
55
|
Args:
|
|
61
56
|
*args: First argument is printed as a green success message, other arguments are printed normally.
|
|
57
|
+
|
|
62
58
|
"""
|
|
63
59
|
if args:
|
|
64
60
|
if prefix:
|
|
@@ -83,7 +79,7 @@ def print_success(*args, prefix: bool = True) -> None:
|
|
|
83
79
|
print(*args[1:])
|
|
84
80
|
|
|
85
81
|
class _Platform:
|
|
86
|
-
def __init__(self):
|
|
82
|
+
def __init__(self) -> None:
|
|
87
83
|
self.OS = platform.system()
|
|
88
84
|
self.PROCESSOR = platform.processor()
|
|
89
85
|
self.MACHINE = platform.machine()
|
|
@@ -95,7 +91,7 @@ class _Platform:
|
|
|
95
91
|
self.RESOLUTION, self.GUI_ENABLED = self.get_resolution()
|
|
96
92
|
# self.NUM_MONITORS = get_monitors().__len__()
|
|
97
93
|
|
|
98
|
-
def __repr__(self):
|
|
94
|
+
def __repr__(self) -> str:
|
|
99
95
|
return (
|
|
100
96
|
f"Operating System: {self.OS}\n"
|
|
101
97
|
f"Processor: {self.PROCESSOR}\n"
|
|
@@ -109,11 +105,11 @@ class _Platform:
|
|
|
109
105
|
f"GUI Enabled: {self.GUI_ENABLED}"
|
|
110
106
|
)
|
|
111
107
|
|
|
112
|
-
def __str__(self):
|
|
108
|
+
def __str__(self) -> str:
|
|
113
109
|
return self.__repr__()
|
|
114
110
|
|
|
115
111
|
@staticmethod
|
|
116
|
-
def get_developer_mode():
|
|
112
|
+
def get_developer_mode() -> str:
|
|
117
113
|
if "ipykernel" in sys.modules and not sys.stdin.isatty():
|
|
118
114
|
return "jupyter"
|
|
119
115
|
return "terminal" if hasattr(sys, "ps1") else "script"
|