浏览代码

integrate joystick

Falko Schindler 2 年之前
父节点
当前提交
2a50e13189

+ 29 - 0
nicegui/elements/joystick.py

@@ -0,0 +1,29 @@
+from typing import Any, Callable, Optional
+
+from .. import vue
+from ..element import Element
+
+vue.register_component('joystick', __file__, 'joystick.vue', ['lib/nipplejs.min.js'])
+
+
+class Joystick(Element):
+
+    def __init__(self, *,
+                 on_start: Optional[Callable] = None,
+                 on_move: Optional[Callable] = None,
+                 on_end: Optional[Callable] = None,
+                 **options: Any) -> None:
+        """Joystick
+
+        Create a joystick based on `nipple.js <https://yoannmoi.net/nipplejs/>`_.
+
+        :param on_start: callback for when the user touches the joystick
+        :param on_move: callback for when the user moves the joystick
+        :param on_end: callback for when the user releases the joystick
+        :param options: arguments like `color` which should be passed to the `underlying nipple.js library <https://github.com/yoannmoinet/nipplejs#options>`_
+        """
+        super().__init__('joystick')
+        self.on('start', on_start)
+        self.on('move', on_move, args=['data'])
+        self.on('end', on_end)
+        self._props['options'] = options

+ 31 - 0
nicegui/elements/joystick.vue

@@ -0,0 +1,31 @@
+<template>
+  <div class="joystick"></div>
+</template>
+
+<script>
+export default {
+  mounted() {
+    const joystick = nipplejs.create({
+      zone: document.querySelector(".joystick"),
+      position: { left: "50%", top: "50%" },
+      dynamicPage: true,
+      ...this.$props.options,
+    });
+    joystick.on("start", (e) => this.$emit("start", e));
+    joystick.on("move", (_, data) => this.$emit("move", { data }));
+    joystick.on("end", (e) => this.$emit("end", e));
+  },
+  props: {
+    options: Object,
+  },
+};
+</script>
+
+<style scoped>
+:scope {
+  background-color: AliceBlue;
+  position: relative;
+  width: 10em;
+  height: 10em;
+}
+</style>

+ 0 - 28
nicegui/elements/old/custom_view.py

@@ -1,28 +0,0 @@
-import justpy as jp
-
-
-class CustomView(jp.JustpyBaseComponent):
-
-    def __init__(self, vue_type, **options):
-        self.vue_type = vue_type
-
-        self.pages = {}
-        self.classes = ''
-        self.style = ''
-        self.options = jp.Dict(**options)
-        self.components = []
-
-        super().__init__(temp=False)
-
-    def react(self, _):
-        pass
-
-    def convert_object_to_dict(self):
-        return {
-            'vue_type': self.vue_type,
-            'id': self.id,
-            'show': True,
-            'classes': self.classes,
-            'style': self.style,
-            'options': self.options,
-        }

+ 0 - 47
nicegui/elements/old/joystick.js

@@ -1,47 +0,0 @@
-Vue.component("joystick", {
-  template: `
-  <div data-nicegui='joystick' v-bind:id="jp_props.id" :class="jp_props.classes" style="background-color:AliceBlue;position:relative;width:10em;height:10em" :style="jp_props.style"></div>
-    `,
-  mounted() {
-    const joystick = nipplejs.create({
-      zone: document.getElementById(this.$props.jp_props.id),
-      dynamicPage: true,
-      ...this.$props.jp_props.options,
-    });
-    joystick.on("start", () => {
-      const event = {
-        event_type: "onStart",
-        vue_type: this.$props.jp_props.vue_type,
-        id: this.$props.jp_props.id,
-        page_id: page_id,
-        websocket_id: websocket_id,
-      };
-      send_to_server(event, "event");
-    });
-    joystick.on("move", (_, data) => {
-      delete data.instance;
-      const event = {
-        event_type: "onMove",
-        vue_type: this.$props.jp_props.vue_type,
-        id: this.$props.jp_props.id,
-        page_id: page_id,
-        websocket_id: websocket_id,
-        data: data,
-      };
-      send_to_server(event, "event");
-    });
-    joystick.on("end", () => {
-      const event = {
-        event_type: "onEnd",
-        vue_type: this.$props.jp_props.vue_type,
-        id: this.$props.jp_props.id,
-        page_id: page_id,
-        websocket_id: websocket_id,
-      };
-      send_to_server(event, "event");
-    });
-  },
-  props: {
-    jp_props: Object,
-  },
-});

+ 0 - 60
nicegui/elements/old/joystick.py

@@ -1,60 +0,0 @@
-from typing import Any, Callable, Optional
-
-from ..routes import add_dependencies
-from .custom_view import CustomView
-from .element import Element
-
-add_dependencies(__file__, ['nipplejs.min.js'])
-
-
-class JoystickView(CustomView):
-
-    def __init__(self,
-                 on_start: Optional[Callable],
-                 on_move: Optional[Callable],
-                 on_end: Optional[Callable],
-                 **options: Any):
-        super().__init__('joystick', **options)
-
-        self.on_start = on_start
-        self.on_move = on_move
-        self.on_end = on_end
-        self.allowed_events = ['onStart', 'onMove', 'onEnd']
-        self.initialize(temp=False,
-                        onStart=self.handle_start,
-                        onMove=self.handle_move,
-                        onEnd=self.handle_end)
-
-    def handle_start(self, msg):
-        if self.on_start is not None:
-            return self.on_start(msg) or False
-        return False
-
-    def handle_move(self, msg):
-        if self.on_move is not None:
-            return self.on_move(msg) or False
-        return False
-
-    def handle_end(self, msg):
-        if self.on_end is not None:
-            return self.on_end(msg) or False
-        return False
-
-
-class Joystick(Element):
-
-    def __init__(self, *,
-                 on_start: Optional[Callable] = None,
-                 on_move: Optional[Callable] = None,
-                 on_end: Optional[Callable] = None,
-                 **options: Any):
-        """Joystick
-
-        Create a joystick based on `nipple.js <https://yoannmoi.net/nipplejs/>`_.
-
-        :param on_start: callback for when the user touches the joystick
-        :param on_move: callback for when the user moves the joystick
-        :param on_end: callback for when the user releases the joystick
-        :param options: arguments like `color` which should be passed to the `underlying nipple.js library <https://github.com/yoannmoinet/nipplejs#options>`_
-        """
-        super().__init__(JoystickView(on_start, on_move, on_end, **options))

+ 1 - 0
nicegui/ui.py

@@ -9,6 +9,7 @@ from .elements.column import Column as column
 from .elements.html import Html as html
 from .elements.html import Html as html
 from .elements.icon import Icon as icon
 from .elements.icon import Icon as icon
 from .elements.image import Image as image
 from .elements.image import Image as image
+from .elements.joystick import Joystick as joystick
 from .elements.label import Label as label
 from .elements.label import Label as label
 from .elements.row import Row as row
 from .elements.row import Row as row
 from .elements.separator import Separator as separator
 from .elements.separator import Separator as separator

+ 42 - 14
nicegui/vue.py

@@ -1,3 +1,6 @@
+from __future__ import annotations
+
+from dataclasses import dataclass
 from pathlib import Path
 from pathlib import Path
 from typing import Dict, List, Tuple
 from typing import Dict, List, Tuple
 
 
@@ -5,16 +8,27 @@ import vbuild
 from starlette.responses import FileResponse
 from starlette.responses import FileResponse
 from starlette.routing import Route
 from starlette.routing import Route
 
 
-components: Dict[str, Path] = {}
+components: Dict[str, Component] = {}
+
 
 
+@dataclass
+class Component:
+    name: str
+    path: Path
+    dependencies: List[str]
 
 
-def register_component(name: str, py_filepath: str, component_filepath: str) -> None:
+
+def register_component(name: str, py_filepath: str, component_filepath: str, dependencies: List[str] = []) -> None:
     assert name not in components
     assert name not in components
-    components[name] = Path(py_filepath).parent / component_filepath
+    components[name] = Component(
+        name=name,
+        path=Path(py_filepath).parent / component_filepath,
+        dependencies=[Path(py_filepath).parent / dependency for dependency in dependencies],
+    )
 
 
 
 
 def generate_vue_content() -> Tuple[str]:
 def generate_vue_content() -> Tuple[str]:
-    builds = [vbuild.VBuild(p.name, p.read_text()) for p in components.values() if p.suffix == '.vue']
+    builds = [vbuild.VBuild(c.name, c.path.read_text()) for c in components.values() if c.path.suffix == '.vue']
     return (
     return (
         '\n'.join(v.html for v in builds),
         '\n'.join(v.html for v in builds),
         '<style>' + '\n'.join(v.style for v in builds) + '</style>',
         '<style>' + '\n'.join(v.style for v in builds) + '</style>',
@@ -22,19 +36,33 @@ def generate_vue_content() -> Tuple[str]:
     )
     )
 
 
 
 
-def get_js_components() -> Dict[str, str]:
-    return {name: filepath for name, filepath in components.items() if filepath.suffix == '.js'}
+def get_js_components() -> List[Component]:
+    return [c for c in components.values() if c.path.suffix == '.js']
 
 
 
 
 def generate_js_routes() -> List[Route]:
 def generate_js_routes() -> List[Route]:
-    return [
-        Route(f'/_vue/{name}', lambda _, filepath=filepath: FileResponse(filepath, media_type='text/javascript'))
-        for name, filepath in get_js_components().items()
-    ]
+    routes: List[Route] = []
+    for component in components.values():
+        for dependency in component.dependencies:
+            print(dependency, flush=True)
+            routes.append(Route(f'/_vue/{component.name}/{dependency}',
+                                lambda _, path=dependency: FileResponse(path, media_type='text/javascript')))
+    for component in get_js_components():
+        routes.append(Route(f'/_vue/{component.name}',
+                            lambda _, path=component.path: FileResponse(path, media_type='text/javascript')))
+    return routes
 
 
 
 
 def generate_js_imports() -> str:
 def generate_js_imports() -> str:
-    return '\n'.join(f'''
-        import {{ default as {name} }} from "/_vue/{name}";
-        app.component("{name}", {name});
-    ''' for name in get_js_components().keys())
+    result = ''
+    for component in components.values():
+        for dependency in component.dependencies:
+            result += f'''
+                import "/_vue/{component.name}/{dependency}";
+            '''
+    for component in get_js_components():
+        result += f'''
+            import {{ default as {component.name} }} from "/_vue/{component.name}";
+            app.component("{component.name}", {component.name});
+        '''
+    return result