robowrap 0.8.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.
robowrap/__init__.py ADDED
@@ -0,0 +1,21 @@
1
+ from .arm import Arm, Gripper
2
+ from .camera import Camera
3
+ from .gun import Gun
4
+ from .modules import AiModule, Armor, Battery, Chassis, DistanceSensor, SensorAdaptor, Servo, Uart
5
+ from .robowrap import RoboMaster
6
+
7
+ __all__ = [
8
+ "AiModule",
9
+ "Arm",
10
+ "Armor",
11
+ "Battery",
12
+ "Camera",
13
+ "Chassis",
14
+ "DistanceSensor",
15
+ "Gripper",
16
+ "Gun",
17
+ "RoboMaster",
18
+ "SensorAdaptor",
19
+ "Servo",
20
+ "Uart",
21
+ ]
robowrap/_sdk.py ADDED
@@ -0,0 +1,67 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ try:
6
+ from robomaster import led as sdk_led
7
+ from robomaster import robot as sdk_robot
8
+ from robomaster.action import Action as SdkAction
9
+ except ImportError: # pragma: no cover - exercised in local tests without the SDK
10
+ sdk_led = None
11
+ sdk_robot = None
12
+ SdkAction = object
13
+
14
+
15
+ def create_sdk_robot() -> Any:
16
+ """Create a RoboMaster EP SDK robot instance."""
17
+ if sdk_robot is None:
18
+ raise ImportError(
19
+ "The DJI RoboMaster SDK is not installed. Install 'robomaster' or inject a "
20
+ "fake robot object when testing."
21
+ )
22
+ return sdk_robot.Robot()
23
+
24
+
25
+ def resolve_robot_mode(mode: str) -> Any:
26
+ """Map student-friendly robot modes onto SDK constants."""
27
+ normalized = mode.lower()
28
+ if normalized == "free":
29
+ return getattr(sdk_robot, "FREE", "free") if sdk_robot else "free"
30
+ if normalized == "chassis":
31
+ return getattr(sdk_robot, "CHASSIS_LEAD", "chassis_lead") if sdk_robot else "chassis_lead"
32
+ if normalized == "gun":
33
+ return getattr(sdk_robot, "GIMBAL_LEAD", "gimbal_lead") if sdk_robot else "gimbal_lead"
34
+ raise ValueError("mode must be 'free', 'chassis', or 'gun'.")
35
+
36
+
37
+ def normalize_robot_mode(mode: Any) -> str:
38
+ """Convert SDK robot mode responses back to the student-facing vocabulary."""
39
+ if mode in ("free", getattr(sdk_robot, "FREE", object())):
40
+ return "free"
41
+ if mode in ("chassis_lead", getattr(sdk_robot, "CHASSIS_LEAD", object())):
42
+ return "chassis"
43
+ if mode in ("gimbal_lead", getattr(sdk_robot, "GIMBAL_LEAD", object())):
44
+ return "gun"
45
+ return str(mode)
46
+
47
+
48
+ LED_COMPONENTS = {
49
+ "front": getattr(sdk_led, "COMP_BOTTOM_FRONT", "front") if sdk_led else "front",
50
+ "back": getattr(sdk_led, "COMP_BOTTOM_BACK", "back") if sdk_led else "back",
51
+ "left": getattr(sdk_led, "COMP_BOTTOM_LEFT", "left") if sdk_led else "left",
52
+ "right": getattr(sdk_led, "COMP_BOTTOM_RIGHT", "right") if sdk_led else "right",
53
+ "gun": getattr(sdk_led, "COMP_TOP_ALL", "gun") if sdk_led else "gun",
54
+ "gun_left": getattr(sdk_led, "COMP_TOP_LEFT", "gun_left") if sdk_led else "gun_left",
55
+ "gun_right": getattr(sdk_led, "COMP_TOP_RIGHT", "gun_right") if sdk_led else "gun_right",
56
+ "all": getattr(sdk_led, "COMP_ALL", "all") if sdk_led else "all",
57
+ }
58
+
59
+
60
+ LED_EFFECTS = {
61
+ "on": getattr(sdk_led, "EFFECT_ON", "on") if sdk_led else "on",
62
+ "off": getattr(sdk_led, "EFFECT_OFF", "off") if sdk_led else "off",
63
+ "pulse": getattr(sdk_led, "EFFECT_PULSE", "pulse") if sdk_led else "pulse",
64
+ "flash": getattr(sdk_led, "EFFECT_FLASH", "flash") if sdk_led else "flash",
65
+ "breath": getattr(sdk_led, "EFFECT_BREATH", "breath") if sdk_led else "breath",
66
+ "scrolling": getattr(sdk_led, "EFFECT_SCROLLING", "scrolling") if sdk_led else "scrolling",
67
+ }
robowrap/arm.py ADDED
@@ -0,0 +1,113 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Callable, Optional
4
+
5
+ from .helperFuncs import deprecated_alias, ensure_choice, ensure_int_range
6
+
7
+
8
+ class Arm:
9
+ def __init__(self, robomaster: Any) -> None:
10
+ self.robomaster = robomaster
11
+ self.robot = self.robomaster.robot
12
+ self.gripper = getattr(self.robomaster, "gripper", None)
13
+
14
+ def move(
15
+ self,
16
+ x: int = 0,
17
+ z: int = 0,
18
+ *,
19
+ blocking: bool = True,
20
+ timeout: Optional[float] = None,
21
+ ) -> Any:
22
+ """Move the arm relative to its current position in millimetres."""
23
+ ensure_int_range("x", x, -200, 200, unit="mm")
24
+ ensure_int_range("z", z, -200, 200, unit="mm")
25
+ action = self.robot.robotic_arm.move(x=x, y=z)
26
+ return self.robomaster._complete_action(action, blocking=blocking, timeout=timeout)
27
+
28
+ def move_to(
29
+ self,
30
+ x: int = 0,
31
+ z: int = 0,
32
+ *,
33
+ blocking: bool = True,
34
+ timeout: Optional[float] = None,
35
+ ) -> Any:
36
+ """Move the arm to an absolute position measured from startup in millimetres."""
37
+ ensure_int_range("x", x, -200, 200, unit="mm")
38
+ ensure_int_range("z", z, -200, 200, unit="mm")
39
+ action = self.robot.robotic_arm.moveto(x=x, y=z)
40
+ return self.robomaster._complete_action(action, blocking=blocking, timeout=timeout)
41
+
42
+ def recenter(self, *, blocking: bool = True, timeout: Optional[float] = None) -> Any:
43
+ action = self.robot.robotic_arm.recenter()
44
+ return self.robomaster._complete_action(action, blocking=blocking, timeout=timeout)
45
+
46
+ def reset(self, *, blocking: bool = True, timeout: Optional[float] = None) -> Any:
47
+ """Return the arm to its startup position."""
48
+ return self.recenter(blocking=blocking, timeout=timeout)
49
+
50
+ def subscribe_position(self, callback: Callable[..., Any], freq: int = 5) -> Any:
51
+ ensure_choice("freq", freq, (1, 5, 10, 20, 50))
52
+ return self.robot.robotic_arm.sub_position(freq=freq, callback=callback)
53
+
54
+ def unsubscribe_position(self) -> Any:
55
+ return self.robot.robotic_arm.unsub_position()
56
+
57
+ def pickup(self) -> None:
58
+ """Run a simple classroom pickup sequence."""
59
+ if self.gripper is None:
60
+ raise RuntimeError("The gripper wrapper is not available.")
61
+ self.move_to(x=0, z=50)
62
+ self.gripper.open()
63
+ self.robomaster.sleep(1.5)
64
+ self.move(x=175, z=-200)
65
+ self.gripper.close()
66
+ self.robomaster.sleep(1.5)
67
+ self.gripper.pause()
68
+ self.move_to(x=0, z=50)
69
+
70
+ def drop(self) -> None:
71
+ """Run a simple classroom drop sequence."""
72
+ if self.gripper is None:
73
+ raise RuntimeError("The gripper wrapper is not available.")
74
+ self.move(x=175, z=-200)
75
+ self.gripper.open()
76
+ self.robomaster.sleep(1.5)
77
+ self.move_to(x=0, z=50)
78
+ self.gripper.pause()
79
+
80
+ @deprecated_alias("move_to")
81
+ def moveTo(self, *args: Any, **kwargs: Any) -> Any:
82
+ return None
83
+
84
+
85
+ class Gripper:
86
+ def __init__(self, robomaster: Any) -> None:
87
+ self.robomaster = robomaster
88
+ self.robot = self.robomaster.robot
89
+
90
+ def open(self, power: int = 50) -> Any:
91
+ ensure_int_range("power", power, 1, 100)
92
+ return self.robot.gripper.open(power=power)
93
+
94
+ def close(self, power: int = 50) -> Any:
95
+ ensure_int_range("power", power, 1, 100)
96
+ return self.robot.gripper.close(power=power)
97
+
98
+ def pause(self) -> Any:
99
+ return self.robot.gripper.pause()
100
+
101
+ def stop(self) -> Any:
102
+ return self.pause()
103
+
104
+ def reset(self) -> Any:
105
+ """Stop the gripper motor and leave the claw in a safe passive state."""
106
+ return self.pause()
107
+
108
+ def subscribe_status(self, callback: Callable[..., Any], freq: int = 5) -> Any:
109
+ ensure_choice("freq", freq, (1, 5, 10, 20, 50))
110
+ return self.robot.gripper.sub_status(freq=freq, callback=callback)
111
+
112
+ def unsubscribe_status(self) -> Any:
113
+ return self.robot.gripper.unsub_status()
robowrap/camera.py ADDED
@@ -0,0 +1,373 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Callable, Optional
4
+
5
+ try: # pragma: no cover - optional dependency in local tests
6
+ import cv2
7
+ except ImportError: # pragma: no cover - optional dependency in local tests
8
+ cv2 = None
9
+
10
+ try: # pragma: no cover - optional dependency in local tests
11
+ from simple_pid import PID
12
+ except ImportError: # pragma: no cover - exercised in tests without the dependency
13
+ class PID: # type: ignore[override]
14
+ def __init__(self, p: float, i: float, d: float, *, setpoint: float = 0.0, sample_time: float = 0.05) -> None:
15
+ self.Kp = p
16
+ self.Ki = i
17
+ self.Kd = d
18
+ self.setpoint = setpoint
19
+ self.sample_time = sample_time
20
+ self.output_limits = (None, None)
21
+
22
+ def __call__(self, measurement: float) -> float:
23
+ error = self.setpoint - measurement
24
+ output = self.Kp * error
25
+ lower, upper = self.output_limits
26
+ if lower is not None:
27
+ output = max(lower, output)
28
+ if upper is not None:
29
+ output = min(upper, output)
30
+ return output
31
+
32
+ from .helperFuncs import (
33
+ deprecated_alias,
34
+ ensure_choice,
35
+ ensure_color,
36
+ ensure_range,
37
+ )
38
+
39
+
40
+ class Camera:
41
+ def __init__(self, robomaster: Any) -> None:
42
+ self.robomaster = robomaster
43
+ self.robot = self.robomaster.robot
44
+ self.camera = self.robot.camera
45
+ self.vision = self.robot.vision
46
+ self.streaming = False
47
+ self.audio_streaming = False
48
+ self.detecting = False
49
+ self.detect_mode = "line"
50
+ self.vision_debug = False
51
+ self.debug_color = (0, 255, 0)
52
+ self.resolution = "360p"
53
+ self.width = 640
54
+ self.height = 360
55
+ self.debug_mode: Any = False
56
+ self.frequency = 30
57
+ self.follow_speed = 0.5
58
+ self.follow_distance = 0.5
59
+ self.at_marker = False
60
+ self._debug_items: list[dict[str, Any]] = []
61
+ self.pid = PID(-330, 0, -28, setpoint=0.0, sample_time=1.0 / self.frequency)
62
+ self.pid.output_limits = (-self.follow_speed / 3.5 * 600, self.follow_speed / 3.5 * 600)
63
+
64
+ def _require_cv2(self) -> None:
65
+ if cv2 is None:
66
+ raise RuntimeError("OpenCV is required for Camera.view(). Install 'opencv-python'.")
67
+
68
+ def _update_resolution_dimensions(self) -> None:
69
+ if self.resolution == "360p":
70
+ self.width, self.height = 640, 360
71
+ elif self.resolution == "540p":
72
+ self.width, self.height = 960, 540
73
+ else:
74
+ self.width, self.height = 1280, 720
75
+
76
+ def set_resolution(self, resolution: str = "360p") -> None:
77
+ resolution = resolution.lower()
78
+ ensure_choice("resolution", resolution, ("360p", "540p", "720p"))
79
+ self.resolution = resolution
80
+ self._update_resolution_dimensions()
81
+
82
+ def start(self) -> Any:
83
+ if self.streaming:
84
+ return True
85
+ result = self.camera.start_video_stream(display=False, resolution=self.resolution)
86
+ self.streaming = bool(result is not False)
87
+ return result
88
+
89
+ def stop_detect(self) -> None:
90
+ if self.detecting:
91
+ self.vision.unsub_detect_info(self.detect_mode)
92
+ self.detecting = False
93
+
94
+ def stop(self) -> Any:
95
+ self.stop_detect()
96
+ if self.audio_streaming:
97
+ self.stop_audio_stream()
98
+ if not self.streaming:
99
+ return True
100
+ result = self.camera.stop_video_stream()
101
+ self.streaming = False
102
+ if cv2 is not None:
103
+ cv2.destroyAllWindows()
104
+ return result
105
+
106
+ def view(self) -> Any:
107
+ self._require_cv2()
108
+ if not self.streaming:
109
+ self.start()
110
+ image = self.camera.read_cv2_image(strategy="newest")
111
+ if image is None:
112
+ return None
113
+ for item in self._debug_items:
114
+ if item["type"] == "box":
115
+ cv2.rectangle(image, item["start"], item["end"], item["color"], 2)
116
+ elif item["type"] == "point":
117
+ cv2.circle(image, item["point"], 2, item["color"], -1)
118
+ cv2.imshow("RoboMaster", image)
119
+ cv2.waitKey(1)
120
+ return image
121
+
122
+ def set_pid(self, p: float = 330, i: float = 0, d: float = 28) -> None:
123
+ self.pid.Kp = -p
124
+ self.pid.Ki = -i
125
+ self.pid.Kd = -d
126
+
127
+ def set_detect_mode(self, mode: str = "line") -> None:
128
+ ensure_choice("mode", mode.lower(), ("person", "gesture", "line", "marker", "robot"))
129
+ self.detect_mode = mode.lower()
130
+
131
+ def set_vision_debug(self, value: bool = True) -> None:
132
+ self.vision_debug = bool(value)
133
+
134
+ def set_debug_color(self, color: tuple[int, int, int] = (255, 0, 0)) -> None:
135
+ self.debug_color = ensure_color("color", color)
136
+
137
+ def reset_vision(self) -> Any:
138
+ self.stop_detect()
139
+ self._debug_items = []
140
+ return self.vision.reset()
141
+
142
+ def _store_boxes(self, items: list[list[Any]]) -> None:
143
+ boxes: list[dict[str, Any]] = []
144
+ for item in items:
145
+ x = int(item[0] * self.width)
146
+ y = int(item[1] * self.height)
147
+ w = int(item[2] * self.width)
148
+ h = int(item[3] * self.height)
149
+ boxes.append(
150
+ {
151
+ "type": "box",
152
+ "start": (x - w // 2, y - h // 2),
153
+ "end": (x + w // 2, y + h // 2),
154
+ "color": self.debug_color,
155
+ }
156
+ )
157
+ self._debug_items = boxes
158
+
159
+ def _store_line(self, info: list[Any]) -> None:
160
+ points: list[dict[str, Any]] = []
161
+ for point in info[1:]:
162
+ x = int(point[0] * self.width)
163
+ y = int(point[1] * self.height)
164
+ points.append({"type": "point", "point": (x, y), "color": self.debug_color})
165
+ self._debug_items = points
166
+
167
+ def _detect_callback(self, info: Any) -> bool:
168
+ if not info:
169
+ self._debug_items = []
170
+ return False
171
+ if self.detect_mode in {"person", "gesture", "marker", "robot"}:
172
+ self._store_boxes(info)
173
+ elif self.detect_mode == "line":
174
+ self._store_line(info)
175
+ return True
176
+
177
+ def _restart_detection(self, mode: str, callback: Callable[..., Any], color: Optional[str] = None) -> Any:
178
+ ensure_choice("mode", mode, ("person", "gesture", "line", "marker", "robot"))
179
+ if color is not None:
180
+ ensure_choice("color", color, ("red", "green", "blue"))
181
+ if not self.streaming:
182
+ self.start()
183
+ if self.detecting:
184
+ self.stop_detect()
185
+ self.detect_mode = mode
186
+ result = self.vision.sub_detect_info(mode, color, callback)
187
+ self.detecting = bool(result is not False)
188
+ return result
189
+
190
+ def detect(self, name: Optional[str] = None, color: Optional[str] = None) -> Any:
191
+ mode = self.detect_mode if name is None else name.lower()
192
+ return self._restart_detection(mode, self._detect_callback, color=color)
193
+
194
+ def detect_person(self) -> Any:
195
+ return self.detect("person")
196
+
197
+ def detect_gesture(self) -> Any:
198
+ return self.detect("gesture")
199
+
200
+ def detect_line(self, color: str = "red") -> Any:
201
+ return self.detect("line", color=color)
202
+
203
+ def detect_marker(self, color: str = "red") -> Any:
204
+ return self.detect("marker", color=color)
205
+
206
+ def detect_robot(self) -> Any:
207
+ return self.detect("robot")
208
+
209
+ def set_follow_speed(self, speed: float) -> None:
210
+ ensure_range("speed", speed, 0.05, 3.5, unit="m/s")
211
+ self.follow_speed = speed
212
+ self.pid.output_limits = (-self.follow_speed / 3.5 * 600, self.follow_speed / 3.5 * 600)
213
+
214
+ def set_follow_distance(self, distance: float) -> None:
215
+ ensure_range("distance", distance, 0.0, 1.0)
216
+ self.follow_distance = distance
217
+
218
+ def _follow_callback(self, info: Any) -> bool:
219
+ self._detect_callback(info)
220
+ if self.detect_mode != "line":
221
+ return False
222
+ if info == [0] or not info:
223
+ self.robomaster.stop()
224
+ return False
225
+ follow_point = min(5, len(info) - 2)
226
+ tangent_angle = info[follow_point + 1][2]
227
+ turn_speed = self.pid(tangent_angle)
228
+ self.robomaster.set_speed(x=self.follow_speed, z=turn_speed)
229
+ return True
230
+
231
+ def follow(self, name: Optional[str] = None, color: str = "red") -> Any:
232
+ mode = self.detect_mode if name is None else name.lower()
233
+ if mode != "line":
234
+ raise NotImplementedError("Only line following is implemented in the student wrapper.")
235
+ return self._restart_detection(mode, self._follow_callback, color=color)
236
+
237
+ def follow_line(self, color: str = "red") -> Any:
238
+ return self.follow("line", color=color)
239
+
240
+ def move_to_marker(
241
+ self,
242
+ marker_type: str = "1",
243
+ *,
244
+ color: str = "red",
245
+ error: float = 0.06,
246
+ speed: float = 1.0,
247
+ min_speed: float = 0.02,
248
+ target_x: float = 0.0,
249
+ target_y: float = 0.5,
250
+ ) -> Any:
251
+ ensure_choice("color", color, ("red", "green", "blue"))
252
+ ensure_range("error", error, 0.0, 1.0)
253
+ ensure_range("speed", speed, 0.05, 3.5, unit="m/s")
254
+ ensure_range("min_speed", min_speed, 0.0, 1.0, unit="m/s")
255
+ ensure_range("target_x", target_x, -1.0, 1.0)
256
+ ensure_range("target_y", target_y, -1.0, 1.0)
257
+ marker_type = str(marker_type)
258
+ self.at_marker = False
259
+
260
+ def _callback(info: Any) -> bool:
261
+ self._detect_callback(info)
262
+ if not info:
263
+ return False
264
+ for marker in info:
265
+ if str(marker[4]) != marker_type:
266
+ continue
267
+ x = marker[0] * 2 - 1
268
+ y = marker[1] * 2 - 1
269
+ if abs(x - target_x) > error:
270
+ lateral = (x - target_x) * speed
271
+ if 0 < abs(lateral) < min_speed:
272
+ lateral = min_speed if lateral > 0 else -min_speed
273
+ self.robomaster.set_speed(y=lateral)
274
+ return False
275
+ if abs(y - target_y) > error:
276
+ forward = (target_y - y) * speed
277
+ if 0 < abs(forward) < min_speed:
278
+ forward = min_speed if forward > 0 else -min_speed
279
+ self.robomaster.set_speed(x=forward)
280
+ return False
281
+ self.robomaster.stop()
282
+ self.stop_detect()
283
+ self.at_marker = True
284
+ return True
285
+ return False
286
+
287
+ return self._restart_detection("marker", _callback, color=color)
288
+
289
+ def take_photo(self) -> Any:
290
+ return self.camera.take_photo()
291
+
292
+ def start_audio_stream(self) -> Any:
293
+ result = self.camera.start_audio_stream()
294
+ self.audio_streaming = bool(result is not False)
295
+ return result
296
+
297
+ def stop_audio_stream(self) -> Any:
298
+ result = self.camera.stop_audio_stream()
299
+ self.audio_streaming = False
300
+ return result
301
+
302
+ def read_audio_frame(self, timeout: float = 1.0) -> Any:
303
+ ensure_range("timeout", timeout, 0.01, 60.0, unit="seconds")
304
+ return self.camera.read_audio_frame(timeout=timeout)
305
+
306
+ def record_audio(self, save_file: str = "output.wav", seconds: float = 5.0, sample_rate: int = 48000) -> Any:
307
+ ensure_range("seconds", seconds, 0.1, 600.0, unit="seconds")
308
+ ensure_choice("sample_rate", sample_rate, (8000, 16000, 32000, 44100, 48000))
309
+ return self.camera.record_audio(save_file=save_file, seconds=seconds, sample_rate=sample_rate)
310
+
311
+ @deprecated_alias("set_resolution")
312
+ def setResolution(self, *args: Any, **kwargs: Any) -> Any:
313
+ return None
314
+
315
+ @deprecated_alias("stop_detect")
316
+ def stopDetect(self, *args: Any, **kwargs: Any) -> Any:
317
+ return None
318
+
319
+ @deprecated_alias("set_pid")
320
+ def setPID(self, *args: Any, **kwargs: Any) -> Any:
321
+ return None
322
+
323
+ @deprecated_alias("set_detect_mode")
324
+ def setDetectMode(self, *args: Any, **kwargs: Any) -> Any:
325
+ return None
326
+
327
+ @deprecated_alias("set_vision_debug")
328
+ def setVisionDebug(self, *args: Any, **kwargs: Any) -> Any:
329
+ return None
330
+
331
+ @deprecated_alias("set_debug_color")
332
+ def setDebugColor(self, *args: Any, **kwargs: Any) -> Any:
333
+ return None
334
+
335
+ @deprecated_alias("detect_person")
336
+ def detectPerson(self, *args: Any, **kwargs: Any) -> Any:
337
+ return None
338
+
339
+ @deprecated_alias("detect_gesture")
340
+ def detectGesture(self, *args: Any, **kwargs: Any) -> Any:
341
+ return None
342
+
343
+ @deprecated_alias("detect_line")
344
+ def detectLine(self, *args: Any, **kwargs: Any) -> Any:
345
+ return None
346
+
347
+ @deprecated_alias("detect_marker")
348
+ def detectMarker(self, *args: Any, **kwargs: Any) -> Any:
349
+ return None
350
+
351
+ @deprecated_alias("detect_robot")
352
+ def detectRobot(self, *args: Any, **kwargs: Any) -> Any:
353
+ return None
354
+
355
+ @deprecated_alias("set_follow_speed")
356
+ def setFollowSpeed(self, *args: Any, **kwargs: Any) -> Any:
357
+ return None
358
+
359
+ @deprecated_alias("set_follow_distance")
360
+ def setFollowDistance(self, *args: Any, **kwargs: Any) -> Any:
361
+ return None
362
+
363
+ @deprecated_alias("follow_line")
364
+ def followLine(self, *args: Any, **kwargs: Any) -> Any:
365
+ return None
366
+
367
+ @deprecated_alias("move_to_marker")
368
+ def moveToMarker(self, *args: Any, **kwargs: Any) -> Any:
369
+ return None
370
+
371
+ @deprecated_alias("view")
372
+ def display(self, *args: Any, **kwargs: Any) -> Any:
373
+ return None