Skip to content

WindowView API

What is the WindowView API

The WindowView API is a framework for developing custom simulator subwindows using PySide6. The simulator windows run in a separate process as the robot code, requiring the use of I/O queues for handling live data between the robot code and the simulator.

Info

The WindowView API requires the usage of PySide6. PySide6 versions not installed as a dependency of KevinbotLib, or other Python Qt bindings are not officially supported, and may not function properly.

Creating and Registering a WindowView

examples/simulator/windowview_register.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from PySide6.QtWidgets import QLabel, QWidget

from kevinbotlib.robot import BaseRobot
from kevinbotlib.simulator.windowview import WindowView, register_window_view


@register_window_view("test.mywindowview")
class MyWindowView(WindowView):
    def __init__(self):
        super().__init__()

    @property
    def title(self):
        return "My Awesome WindowView"

    def generate(self) -> QWidget:
        return QLabel("Hello World!")


class DemoRobot(BaseRobot):
    def __init__(self):
        super().__init__(
            ["Test"],
            enable_stderr_logger=True,
        )

        if BaseRobot.IS_SIM:
            self.simulator.add_window("test.mywindowview", MyWindowView)
            self.telemetry.info(f"Registered WindowViews: {self.simulator.windows}")


if __name__ == "__main__":
    DemoRobot().run()

Tip

Each WindowView must have a different registered Window ID (in this case, test.mywindowview). It is recommended to use reverse domain name notation (e.g., com.example.myproduct.mywindowview). Do not use Window IDs starting with kevinbotlib or com.meowmeowahr.kevinbotlib

Working with Payloads

examples/simulator/windowview_payload.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
from typing import Any

from PySide6.QtWidgets import QLabel, QPushButton, QVBoxLayout, QWidget

from kevinbotlib.robot import BaseRobot
from kevinbotlib.simulator.windowview import WindowView, WindowViewOutputPayload, register_window_view


class ResetPayload(WindowViewOutputPayload):
    def payload(self) -> Any:
        return "reset"


@register_window_view("test.mywindowview")
class MyWindowView(WindowView):
    def __init__(self):
        super().__init__()

        self.widget = QWidget()

        self.layout = QVBoxLayout()
        self.widget.setLayout(self.layout)

        self.label = QLabel("Robot Cycles: ?")
        self.layout.addWidget(self.label)

        self.button = QPushButton("Reset")
        self.button.clicked.connect(lambda: self.send_payload(ResetPayload()))
        self.layout.addWidget(self.button)

    @property
    def title(self):
        return "My Awesome WindowView"

    def generate(self) -> QWidget:
        return self.widget

    def update(self, payload: Any) -> None:
        self.label.setText(f"Robot Cycles: {payload}")


class DemoRobot(BaseRobot):
    def __init__(self):
        super().__init__(
            ["Test"],
            enable_stderr_logger=True,
        )

        self.cycles = 0
        if BaseRobot.IS_SIM:
            self.simulator.add_window("test.mywindowview", MyWindowView)

            def reset_cycles(payload: WindowViewOutputPayload):
                if payload.payload() == "reset":
                    self.cycles = 0

            self.simulator.add_payload_callback(ResetPayload, reset_cycles)

    def robot_periodic(self, opmode: str, enabled: bool):
        super().robot_periodic(opmode, enabled)

        self.cycles += 1
        self.simulator.send_to_window("test.mywindowview", self.cycles)


if __name__ == "__main__":
    DemoRobot().run()