[MouseFox logo]
The MouseFox Project
Join The Community Discord Server
[Discord logo]
Edit on GitHub

mousefox.app.adminframe

Home of AdminFrame.

  1"""Home of `AdminFrame`."""
  2
  3import arrow
  4import functools
  5import json
  6import kvex as kx
  7import pgnet
  8import pprint
  9
 10
 11_STATUSES = {s.value: s for s in pgnet.Status}
 12_STATUS_COLORS = {
 13    pgnet.Status.OK.value: kx.XColor.from_hex("00ff00"),
 14    pgnet.Status.UNEXPECTED.value: kx.XColor.from_hex("bbbb00"),
 15    pgnet.Status.BAD.value: kx.XColor.from_hex("ff0000"),
 16}
 17
 18
 19class AdminFrame(kx.XFrame):
 20    """Widget for admin controls."""
 21
 22    _conpath = "client.user.admin"
 23
 24    def __init__(self, client: pgnet.Client):
 25        """Initialize the class with a client."""
 26        super().__init__()
 27        self._client = client
 28        self._make_widgets()
 29        self.app.controller.set_active_callback(self._conpath, self.set_focus)
 30        self.app.controller.bind(f"{self._conpath}.focus", self.set_focus)
 31        self.app.controller.bind(f"{self._conpath}.debug", self._request_debug)
 32
 33    def _make_widgets(self):
 34        with self.app.subtheme_context("accent"):
 35            title = kx.fwrap(kx.XLabel(text="Admin Panel", bold=True, font_size="36sp"))
 36            title.set_size(y="40sp")
 37        # Requests frame
 38        requests_placeholder = kx.XPlaceholder(
 39            button_text="Get requests",
 40            callback=self._refresh_requests,
 41        )
 42        self.requests_frame = kx.XContainer(requests_placeholder)
 43        custom_input_label = kx.XLabel(
 44            text="Custom packet builder",
 45            bold=True,
 46            underline=True,
 47            font_size="18dp",
 48        )
 49        custom_input_title = kx.wrap(custom_input_label)
 50        custom_input_title.set_size(y="40dp")
 51        packet_input_widgets = dict(
 52            message=kx.XInputPanelWidget(
 53                label="Message:",
 54                label_hint=0.2,
 55                orientation="vertical",
 56            ),
 57            payload=kx.XInputPanelWidget(
 58                label="Payload JSON:",
 59                label_hint=0.2,
 60                orientation="vertical",
 61            ),
 62        )
 63        self.packet_input = kx.XInputPanel(
 64            packet_input_widgets,
 65            reset_text="",
 66            invoke_text="Send packet",
 67        )
 68        self.packet_input.bind(on_invoke=self._on_packet_input)
 69        self.packet_input.set_size(y="100dp")
 70        self.custom_packet_frame = kx.XDBox()
 71        self.custom_packet_frame.add_widgets(custom_input_title, self.packet_input)
 72        # Response labels
 73        self.response_label = kx.XLabel(
 74            font_name="RobotoMono-Regular",
 75            padding=(10, 10),
 76            halign="left",
 77            valign="top",
 78            fixed_width=True,
 79        )
 80        response_label_frame = kx.XScroll(view=self.response_label)
 81        self.debug_label = kx.XLabel(
 82            font_name="RobotoMono-Regular",
 83            padding=(10, 10),
 84            halign="left",
 85            valign="top",
 86            fixed_width=True,
 87        )
 88        debug_label_frame = kx.fwrap(kx.XScroll(view=self.debug_label))
 89        debug_label_frame.set_size(x="300dp")
 90        # Assemble
 91        self.requests_frame.set_size(x="300dp")
 92        bottom_frame = kx.XBox()
 93        bottom_frame.add_widgets(
 94            debug_label_frame,
 95            response_label_frame,
 96            self.requests_frame,
 97        )
 98        main_frame = kx.XBox(orientation="vertical")
 99        main_frame.add_widgets(title, kx.pwrap(bottom_frame))
100        self.add_widget(kx.pwrap(main_frame))
101
102    def _refresh_requests(self):
103        self._client.send(pgnet.Packet(pgnet.util.Request.HELP), self._on_help_response)
104
105    def _on_help_response(self, response: pgnet.Response):
106        main_stack = kx.XDBox()
107        for request, params in response.payload.items():
108            panel_widgets = {
109                name: kx.XInputPanelWidget(label=f"{name}:", widget=ptype)
110                for name, ptype in params.items()
111            }
112            with self.app.subtheme_context("secondary"):
113                panel = kx.XInputPanel(
114                    panel_widgets,
115                    reset_text="",
116                    invoke_text=request,
117                    fill_button=True,
118                )
119                panel.on_invoke = functools.partial(self._on_request_invoke, request)
120                panel = panel
121            with self.app.subtheme_context("accent"):
122                text = request.removeprefix("__pgnet__.")
123                text = text.replace("_", " ").capitalize()
124                lbl = kx.XLabel(text=text, bold=True, font_size="18dp")
125                lbl = kx.pwrap(kx.fwrap(lbl))
126                lbl.set_size(y="40dp")
127            main_stack.add_widgets(lbl, panel)
128        if self.custom_packet_frame.parent:
129            self.custom_packet_frame.parent.remove_widget(self.custom_packet_frame)
130        main_stack.add_widget(self.custom_packet_frame)
131        scroll = kx.XScroll(view=main_stack)
132        self.requests_frame.content = kx.fpwrap(scroll, subtheme_name="secondary")
133
134    def _request_debug(self, *args):
135        self._client.send(
136            pgnet.Packet(pgnet.util.Request.DEBUG),
137            self._response_callback,
138        )
139
140    def _on_request_invoke(self, request: str, values: dict):
141        self._client.send(pgnet.Packet(request, values), self._response_callback)
142
143    def _on_packet_input(self, w, values):
144        message = values["message"]
145        payload_text = values["payload"]
146        try:
147            payload = json.loads(payload_text)
148        except Exception:
149            payload = dict()
150        packet = pgnet.Packet(message, payload)
151        self._client.send(packet, self._response_callback)
152        self.packet_input.set_focus("message")
153
154    def _response_callback(self, response: pgnet.Response):
155        sb = self.subtheme
156        status = _STATUSES[response.status]
157        status_color = _STATUS_COLORS[status]
158        statusstr = status_color.markup(status.name)
159        timestr = arrow.get(response.created_on).to("local").format("HH:mm:ss MMM DD")
160        debug_strs = [
161            f"{sb.fg2.markup('Status:')} {status.value} ({statusstr})",
162            f"{sb.fg2.markup('Created:')} {timestr}",
163            response.debug_repr,
164        ]
165        self.debug_label.text = "\n\n".join(debug_strs)
166        response_strs = [
167            f"{sb.fg2.markup('Response:')} {response.message}",
168        ]
169        for k, v in response.payload.items():
170            vstr = v if isinstance(v, str) else pprint.pformat(v, width=10_000)
171            response_strs.append(f"\n[u]{sb.fg2.markup(k)}[/u]\n{vstr}")
172        self.response_label.text = "\n".join(response_strs)
173
174    def set_focus(self, *args):
175        """Refresh requests frame on focus if empty."""
176        if not self.requests_frame.content:
177            self._refresh_requests()
class AdminFrame(kvex.widgets.layouts.XFrame):
 20class AdminFrame(kx.XFrame):
 21    """Widget for admin controls."""
 22
 23    _conpath = "client.user.admin"
 24
 25    def __init__(self, client: pgnet.Client):
 26        """Initialize the class with a client."""
 27        super().__init__()
 28        self._client = client
 29        self._make_widgets()
 30        self.app.controller.set_active_callback(self._conpath, self.set_focus)
 31        self.app.controller.bind(f"{self._conpath}.focus", self.set_focus)
 32        self.app.controller.bind(f"{self._conpath}.debug", self._request_debug)
 33
 34    def _make_widgets(self):
 35        with self.app.subtheme_context("accent"):
 36            title = kx.fwrap(kx.XLabel(text="Admin Panel", bold=True, font_size="36sp"))
 37            title.set_size(y="40sp")
 38        # Requests frame
 39        requests_placeholder = kx.XPlaceholder(
 40            button_text="Get requests",
 41            callback=self._refresh_requests,
 42        )
 43        self.requests_frame = kx.XContainer(requests_placeholder)
 44        custom_input_label = kx.XLabel(
 45            text="Custom packet builder",
 46            bold=True,
 47            underline=True,
 48            font_size="18dp",
 49        )
 50        custom_input_title = kx.wrap(custom_input_label)
 51        custom_input_title.set_size(y="40dp")
 52        packet_input_widgets = dict(
 53            message=kx.XInputPanelWidget(
 54                label="Message:",
 55                label_hint=0.2,
 56                orientation="vertical",
 57            ),
 58            payload=kx.XInputPanelWidget(
 59                label="Payload JSON:",
 60                label_hint=0.2,
 61                orientation="vertical",
 62            ),
 63        )
 64        self.packet_input = kx.XInputPanel(
 65            packet_input_widgets,
 66            reset_text="",
 67            invoke_text="Send packet",
 68        )
 69        self.packet_input.bind(on_invoke=self._on_packet_input)
 70        self.packet_input.set_size(y="100dp")
 71        self.custom_packet_frame = kx.XDBox()
 72        self.custom_packet_frame.add_widgets(custom_input_title, self.packet_input)
 73        # Response labels
 74        self.response_label = kx.XLabel(
 75            font_name="RobotoMono-Regular",
 76            padding=(10, 10),
 77            halign="left",
 78            valign="top",
 79            fixed_width=True,
 80        )
 81        response_label_frame = kx.XScroll(view=self.response_label)
 82        self.debug_label = kx.XLabel(
 83            font_name="RobotoMono-Regular",
 84            padding=(10, 10),
 85            halign="left",
 86            valign="top",
 87            fixed_width=True,
 88        )
 89        debug_label_frame = kx.fwrap(kx.XScroll(view=self.debug_label))
 90        debug_label_frame.set_size(x="300dp")
 91        # Assemble
 92        self.requests_frame.set_size(x="300dp")
 93        bottom_frame = kx.XBox()
 94        bottom_frame.add_widgets(
 95            debug_label_frame,
 96            response_label_frame,
 97            self.requests_frame,
 98        )
 99        main_frame = kx.XBox(orientation="vertical")
100        main_frame.add_widgets(title, kx.pwrap(bottom_frame))
101        self.add_widget(kx.pwrap(main_frame))
102
103    def _refresh_requests(self):
104        self._client.send(pgnet.Packet(pgnet.util.Request.HELP), self._on_help_response)
105
106    def _on_help_response(self, response: pgnet.Response):
107        main_stack = kx.XDBox()
108        for request, params in response.payload.items():
109            panel_widgets = {
110                name: kx.XInputPanelWidget(label=f"{name}:", widget=ptype)
111                for name, ptype in params.items()
112            }
113            with self.app.subtheme_context("secondary"):
114                panel = kx.XInputPanel(
115                    panel_widgets,
116                    reset_text="",
117                    invoke_text=request,
118                    fill_button=True,
119                )
120                panel.on_invoke = functools.partial(self._on_request_invoke, request)
121                panel = panel
122            with self.app.subtheme_context("accent"):
123                text = request.removeprefix("__pgnet__.")
124                text = text.replace("_", " ").capitalize()
125                lbl = kx.XLabel(text=text, bold=True, font_size="18dp")
126                lbl = kx.pwrap(kx.fwrap(lbl))
127                lbl.set_size(y="40dp")
128            main_stack.add_widgets(lbl, panel)
129        if self.custom_packet_frame.parent:
130            self.custom_packet_frame.parent.remove_widget(self.custom_packet_frame)
131        main_stack.add_widget(self.custom_packet_frame)
132        scroll = kx.XScroll(view=main_stack)
133        self.requests_frame.content = kx.fpwrap(scroll, subtheme_name="secondary")
134
135    def _request_debug(self, *args):
136        self._client.send(
137            pgnet.Packet(pgnet.util.Request.DEBUG),
138            self._response_callback,
139        )
140
141    def _on_request_invoke(self, request: str, values: dict):
142        self._client.send(pgnet.Packet(request, values), self._response_callback)
143
144    def _on_packet_input(self, w, values):
145        message = values["message"]
146        payload_text = values["payload"]
147        try:
148            payload = json.loads(payload_text)
149        except Exception:
150            payload = dict()
151        packet = pgnet.Packet(message, payload)
152        self._client.send(packet, self._response_callback)
153        self.packet_input.set_focus("message")
154
155    def _response_callback(self, response: pgnet.Response):
156        sb = self.subtheme
157        status = _STATUSES[response.status]
158        status_color = _STATUS_COLORS[status]
159        statusstr = status_color.markup(status.name)
160        timestr = arrow.get(response.created_on).to("local").format("HH:mm:ss MMM DD")
161        debug_strs = [
162            f"{sb.fg2.markup('Status:')} {status.value} ({statusstr})",
163            f"{sb.fg2.markup('Created:')} {timestr}",
164            response.debug_repr,
165        ]
166        self.debug_label.text = "\n\n".join(debug_strs)
167        response_strs = [
168            f"{sb.fg2.markup('Response:')} {response.message}",
169        ]
170        for k, v in response.payload.items():
171            vstr = v if isinstance(v, str) else pprint.pformat(v, width=10_000)
172            response_strs.append(f"\n[u]{sb.fg2.markup(k)}[/u]\n{vstr}")
173        self.response_label.text = "\n".join(response_strs)
174
175    def set_focus(self, *args):
176        """Refresh requests frame on focus if empty."""
177        if not self.requests_frame.content:
178            self._refresh_requests()

Widget for admin controls.

AdminFrame(client: pgnet.client.Client)
25    def __init__(self, client: pgnet.Client):
26        """Initialize the class with a client."""
27        super().__init__()
28        self._client = client
29        self._make_widgets()
30        self.app.controller.set_active_callback(self._conpath, self.set_focus)
31        self.app.controller.bind(f"{self._conpath}.focus", self.set_focus)
32        self.app.controller.bind(f"{self._conpath}.debug", self._request_debug)

Initialize the class with a client.

def set_focus(self, *args):
175    def set_focus(self, *args):
176        """Refresh requests frame on focus if empty."""
177        if not self.requests_frame.content:
178            self._refresh_requests()

Refresh requests frame on focus if empty.

Inherited Members
kivy.uix.anchorlayout.AnchorLayout
padding
anchor_x
anchor_y
do_layout
kivy.uix.layout.Layout
add_widget
remove_widget
layout_hint_with_bounds
kivy.uix.widget.Widget
proxy_ref
apply_class_lang_rules
collide_point
collide_widget
on_motion
on_touch_down
on_touch_move
on_touch_up
on_kv_post
clear_widgets
register_for_motion_event
unregister_for_motion_event
export_to_png
export_as_image
get_root_window
get_parent_window
walk
walk_reverse
to_widget
to_window
to_parent
to_local
get_window_matrix
x
y
width
height
pos
size
get_right
set_right
right
get_top
set_top
top
get_center_x
set_center_x
center_x
get_center_y
set_center_y
center_y
center
cls
children
parent
size_hint_x
size_hint_y
size_hint
pos_hint
size_hint_min_x
size_hint_min_y
size_hint_min
size_hint_max_x
size_hint_max_y
size_hint_max
ids
opacity
on_opacity
canvas
get_disabled
set_disabled
inc_disabled
dec_disabled
disabled
motion_filter
kivy._event.EventDispatcher
register_event_type
unregister_event_types
unregister_event_type
is_event_type
bind
unbind
fbind
funbind
unbind_uid
get_property_observers
events
dispatch
dispatch_generic
dispatch_children
setter
getter
property
properties
create_property
apply_property