Пример #1
0
class ReticoBuilder(flx.PyComponent):
    """The main connection between the GUI (JS) and the Model (Python).
    The ReticoBuilder object lives on the Python-side and has access to all
    interfaces of the Javascript-side."""

    connecting_state = flx.BoolProp(settable=True)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.connecting_module = None
        self.connecting_direction = None
        self.load_map = {}
        self.load_c_list = []

    def init(self):
        module_list = modlist.get_module_list()
        file_list = glob.glob("save/*.rtc")
        self.widget = ReticoWidget(self, module_list, file_list)
        self.modules = {}
        self.running = False
        self.set_connecting_state(False)
        self.connecting_module = None
        self.connecting_direction = None

    @flx.action
    def register_module(self, module_type, parent, gui, params):
        """Creates a new module model with the given type and parameters and
        gives the model a reference to the GUI object (ReticoModule) that the
        module is representing.

        Params:
            module_type (str): The name of the class of the module that should
                be instantiated.
            parent (str): The parent name in the module list (e.g. ReTiCo,
                Google, ...)
            gui (ReticoModule): A retico module gui widget that represents the
                new module.
            params (dict): A json string of parameters that the retico module
                should be initialized with.
        """
        if self.running:
            gui.set_parent(None)
            gui.dispose()
            return
        pymodule = modlist.MODULE_LIST[parent][module_type](
            gui, params, None, flx_session=self.session)
        self.modules[id(gui)] = pymodule
        gui.set_mtitle(pymodule.retico_module.name())
        pymodule.enable_buttons()

    @flx.action
    def create_parameter_dialogue(self, module_type, parent):
        """Creates a parameters dialogue for the given module type.

        Before showing the dialogue, this method retrieves the default parameter
        for the module so that the parameter dialogue shows a  default.

        Params:
            module_type (str): The name of the class of the module
            parent (str): The parent name in the module list (e.g. ReTiCo, ...)
        """
        params = json.dumps(
            modlist.MODULE_LIST[parent][module_type].PARAMETERS)
        self.widget.create_dialogue(params, module_type, parent)

    @flx.action
    def init_out_click(self, gui):
        if (
                self.running
        ):  # If the network is in running state we do not allow to make new connections
            return
        module = self.modules[id(gui)]
        out_iu = module.MODULE.output_iu()

        self.set_connecting_state(True)
        self.connecting_module = gui
        self.connecting_direction = "out"

        gui.highlight(True, "border")
        for m in self.modules.values():
            if gui != m.gui:
                m.enable_output_iu(out_iu)

    @flx.action
    def init_in_click(self, gui):
        if (
                self.running
        ):  # If the network is in running state we do not allow to make new connections
            return
        module = self.modules[id(gui)]
        in_ius = module.MODULE.input_ius()

        self.set_connecting_state(True)
        self.connecting_module = gui
        self.connecting_direction = "in"

        gui.highlight(True, "border")
        for m in self.modules.values():
            if gui != m.gui:
                m.enable_input_ius(in_ius)

    @flx.action
    def connect_to(self, gui):
        if self.running:
            return
        if gui != self.connecting_module:
            if self.connecting_direction == "in":
                in_module = self.modules[id(self.connecting_module)]
                out_module = self.modules[id(gui)]
            else:
                in_module = self.modules[id(gui)]
                out_module = self.modules[id(self.connecting_module)]

            out_module.retico_module.subscribe(in_module.retico_module)
            self.widget.add_connection(out_module.gui, in_module.gui)

        self.set_connecting_state(False)
        self.connecting_module = None
        self.connecting_direction = None

        for m in self.modules.values():
            m.enable_buttons()

    @flx.action
    def delete_module(self, gui):
        if self.running:
            return
        self.widget.delete_module(gui)
        self.modules[id(gui)].retico_module.remove()
        del self.modules[id(gui)]
        self.set_connecting_state(False)
        self.connecting_module = None
        self.connecting_direction = None

        for m in self.modules.values():
            m.enable_buttons()

        gui.set_parent(None)
        gui.dispose()

    @flx.action
    def run(self):
        self.running = True
        self.widget.set_running("yellow")
        for m in self.modules.values():
            m.setup()

        self.widget.set_running("red")
        for m in self.modules.values():
            print("Running", m.retico_module)
            m.run()

    @flx.action
    def stop(self):
        self.running = False
        self.widget.set_running(None)
        for m in self.modules.values():
            m.stop()

    @flx.action
    def update_module_info(self):
        for m in self.modules.values():
            m.update_running_info()

    @flx.action
    def set_module_content(self):
        for m in self.modules.values():
            m.set_content()

    @flx.action
    def save(self, filename):
        path = "save/%s" % filename
        last_m = None
        for m in self.modules.values():
            meta = m.retico_module.meta_data
            meta["widget"] = str(m.__class__.__name__)
            meta["left"] = m.gui.p_left
            meta["top"] = m.gui.p_top
            meta["width"] = m.gui.p_width
            meta["height"] = m.gui.p_height
            meta["active"] = m.gui.active
            meta["id"] = id(m)
            last_m = m
        headless.save(last_m.retico_module, path)
        filenames = glob.glob("save/*.rtc")
        self.widget.update_file_tree(filenames)

    @flx.action
    def load(self, filename):
        path = "save/%s" % filename
        m_list, c_list = headless.load(path)
        self.load_map = {}
        self.load_c_list = c_list
        for m in m_list:
            type = m.meta_data.get("widget", None)
            if not type:
                type = str(m.__class__.__name__)
            parent = self.get_parent(type)
            x = m.meta_data.get("left", 20)
            y = m.meta_data.get("top", 20)
            h = m.meta_data.get("height", 150)
            w = m.meta_data.get("width", 150)
            mid = m.meta_data.get("id", id(m))
            active = m.meta_data.get("active", True)
            self.load_map[mid] = m
            self.widget.create_existing_module(type, parent, x, y, w, h, mid,
                                               active)
        self.widget.load_connections()

    @flx.action
    def load_connections(self):
        for (a, b) in self.load_c_list:
            aid = a.meta_data.get("id", id(a))
            bid = b.meta_data.get("id", id(b))
            agui = self.load_map[aid]
            bgui = self.load_map[bid]
            self.widget.add_connection(agui, bgui)

    def get_parent(self, name):
        for k in modlist.MODULE_LIST.keys():
            for n in modlist.MODULE_LIST[k].keys():
                if n == name:
                    return k

    @flx.action
    def register_existing_module(self, type, parent, gui, mid):
        m = self.load_map[mid]
        self.load_map[mid] = gui
        pymodule = modlist.MODULE_LIST[parent][type](gui,
                                                     None,
                                                     m,
                                                     flx_session=self.session)
        self.modules[id(gui)] = pymodule
        gui.set_mtitle(pymodule.retico_module.name())
        pymodule.enable_buttons()

    @flx.action
    def clear(self):
        for m in list(self.modules.values()):
            self.delete_module(m.gui)
        self.modules = {}

    @flx.action
    def toggle_module(self, gui):
        gui.set_active(not gui.active)
        gui.display_active()

    @flx.action
    def handle_trigger(self, gui, text):
        self.modules[id(gui)].handle_trigger(text)
Пример #2
0
class ReticoModule(flx.Widget):

    p_left = flx.IntProp(settable=True)
    p_top = flx.IntProp(settable=True)
    p_width = flx.IntProp(settable=True)
    p_height = flx.IntProp(settable=True)

    active = flx.BoolProp(settable=True)

    def init(self, retico_widget):
        self.retico_widget = retico_widget
        self.l_title = flx.Label(text="", css_class="title-label")
        self.close_button = flx.Button(text="X", css_class="close-button")
        with flx.VBox(
                style=
                "cursor: default; padding-bottom: 30px; padding-left: 20px; padding-right:20px; padding-top: 30px;"
        ) as self.content_box:
            self.content_box.set_padding("20px")
        self.out_button_l = flx.Button(text="◀",
                                       css_class="out-button left-button")
        self.out_button_r = flx.Button(text="▶",
                                       css_class="out-button right-button")
        self.in_button_l = flx.Button(text="▶",
                                      css_class="in-button left-button")
        self.in_button_r = flx.Button(text="◀",
                                      css_class="in-button right-button")
        self.enable_button = flx.Button(text="enabled",
                                        css_class="enable-button")
        self.trigger_edit = flx.LineEdit(self.content_box)
        self.trigger_button = flx.Button(parent=self.content_box,
                                         text="Trigger")
        self.trigger_edit.set_parent(None)
        self.trigger_button.set_parent(None)
        self.trigger_button.set_disabled(True)
        self.set_active(True)
        self.set_position()

    def set_position(self):
        rect = self.node.getBoundingClientRect()
        mpane = self.retico_widget.mpane.node

        self.set_p_left(rect.left + mpane.scrollLeft)
        self.set_p_top(rect.top + mpane.scrollTop)
        self.set_p_width(rect.width)
        self.set_p_height(rect.height)

        window.setTimeout(self.set_position, 100)

    @flx.action
    def clear_content(self):
        for child in self.content_box.children:
            child.set_parent(None)
            child.dispose()

    @flx.action
    def add_info(self, text):
        style = "font-size: 10pt; text-align: center;"
        lbl = flx.Label(style=style, parent=self.content_box)
        lbl.set_html(text)

    @flx.action
    def update_info(self, text):
        style = "font-size: 10pt; text-align: center;"
        if len(self.content_box.children) > 1:
            self.clear_content()
            flx.Label(style=style, parent=self.content_box)
        self.content_box.children[0].set_html(text)

    @flx.action
    def create_trigger(self):
        self.trigger_edit.set_parent(self.content_box)
        self.trigger_button.set_parent(self.content_box)

    @flx.reaction("trigger_button.pointer_click")
    def trigger_clicked(self):
        params_txt = self.trigger_edit.text
        print("TRIGGER CLICKED!", str(params_txt))
        self.retico_widget.model.handle_trigger(self, params_txt)

    # @flx.action
    # def set_content(self, content_list):
    #     for child in self.content_box.children:
    #         child.set_parent(None)
    #         child.dispose()
    #     for element in content_list:
    #         element.set_parent(self.content_box)

    @flx.action
    def disable_input_buttons(self):
        self.in_button_l.set_disabled(True)
        self.in_button_r.set_disabled(True)

    @flx.action
    def disable_output_buttons(self):
        self.out_button_l.set_disabled(True)
        self.out_button_r.set_disabled(True)

    @flx.action
    def enable_input_buttons(self):
        self.in_button_l.set_disabled(False)
        self.in_button_r.set_disabled(False)

    @flx.action
    def enable_output_buttons(self):
        self.out_button_l.set_disabled(False)
        self.out_button_r.set_disabled(False)

    @flx.action
    def enable_close_button(self):
        self.close_button.set_disabled(False)
        self.trigger_button.set_disabled(True)

    @flx.action
    def disable_close_button(self):
        self.close_button.set_disabled(True)
        self.trigger_button.set_disabled(False)

    @flx.action
    def highlight(self, active, color="#ffd"):
        self.node.style["box-shadow"] = None
        if active:
            if color == "border":
                self.node.style[
                    "box-shadow"] = "rgba(255, 255, 255, 0.6) 0px 0px 20px"
            elif color == "red-border":
                self.node.style[
                    "box-shadow"] = "rgba(255, 0, 0, 0.6) 0px 0px 20px"
            else:
                self.node.style["background-color"] = color
        else:
            self.node.style["background-color"] = "#fff"

    @flx.reaction("out_button_l.pointer_click", "out_button_r.pointer_click")
    def out_button_click(self):
        if not self.retico_widget.model.connecting_state:
            self.retico_widget.model.init_out_click(self)
        else:
            self.retico_widget.model.connect_to(self)

    @flx.reaction("in_button_l.pointer_click", "in_button_r.pointer_click")
    def in_button_click(self):
        if not self.retico_widget.model.connecting_state:
            self.retico_widget.model.init_in_click(self)
        else:
            self.retico_widget.model.connect_to(self)

    @flx.reaction("close_button.pointer_click")
    def _close(self):
        self.retico_widget.model.delete_module(self)

    @flx.reaction("enable_button.pointer_click")
    def enable_button_click(self):
        self.retico_widget.model.toggle_module(self)

    @flx.action
    def set_mtitle(self, thing):
        self.l_title.set_text(thing)

    def in_pos(self):
        rect = self.node.getBoundingClientRect()
        mp_node = self.retico_widget.mpane.node
        return (
            rect.left + (rect.width / 2) + mp_node.scrollLeft,
            rect.bottom - 13 + mp_node.scrollTop,
        )

    def out_pos(self):
        rect = self.node.getBoundingClientRect()
        mp_node = self.retico_widget.mpane.node
        return (
            rect.left + (rect.width / 2) + mp_node.scrollLeft,
            rect.top + 45 + mp_node.scrollTop,
        )

    @flx.action
    def setup(self):
        self.disable_input_buttons()
        self.disable_output_buttons()
        self.disable_close_button()
        self.highlight(True, "red-border")

    @flx.action
    def stop(self):
        self.enable_close_button()
        self.highlight(False)

    @flx.action
    def display_active(self):
        if self.active:
            self.node.style["opacity"] = 1
            self.enable_button.set_text("enabled")
        else:
            self.node.style["opacity"] = 0.1
            self.enable_button.set_text("disabled")
Пример #3
0
class Relay(flx.Component):
    #	def init(self):
    #		super().init()
    #		self.time = "Relay Default Time"
    #		self.newTime = False
    #
    #	def setTime(self, time):
    #		self.time = time
    #		self.newTime = True
    vin = flx.StringProp("", settable=True)
    timeString = flx.StringProp("", settable=True)
    voltage = flx.FloatProp(0, settable=True)
    capacity = flx.FloatProp(0, settable=True)
    pandaRecording = flx.IntProp(0, settable=True)
    pandaGps = flx.IntProp(0, settable=True)
    vehicleControlRunning = flx.BoolProp(False, settable=True)

    @flx.reaction('vin')
    def on_vin(self, *events):
        for ev in events:
            self.updateVin(ev.new_value)

    @flx.reaction('timeString')
    def on_timeString(self, *events):
        for ev in events:
            self.updateTime(ev.new_value)

    @flx.reaction('voltage')
    def on_voltage(self, *events):
        for ev in events:
            self.updateVoltage(ev.new_value)

    @flx.reaction('capacity')
    def on_voltage(self, *events):
        for ev in events:
            self.updateCapacity(ev.new_value)

    @flx.reaction('pandaRecording')
    def on_pandaRecording(self, *events):
        for ev in events:
            self.updatePandaRecording(ev.new_value)

    @flx.reaction('pandaGps')
    def on_pandaGps(self, *events):
        for ev in events:
            self.updatePandaGps(ev.new_value)

    @flx.reaction('vehicleControlRunning')
    def on_vehicleControlRunning(self, *events):
        for ev in events:
            self.updateVehicleControlRunning(ev.new_value)

    """ Global object to relay paint events to all participants.
	"""

    @flx.emitter
    def updateVin(self, value):
        return dict(value=value)

    @flx.emitter
    def updateTime(self, value):
        return dict(value=value)

    @flx.emitter
    def updateVoltage(self, value):
        return dict(value=value)

    @flx.emitter
    def updateCapacity(self, value):
        return dict(value=value)

    @flx.emitter
    def updatePandaRecording(self, value):
        return dict(value=value)

    @flx.emitter
    def updatePandaGps(self, value):
        return dict(value=value)

    @flx.emitter
    def updateVehicleControlRunning(self, value):
        return dict(value=value)
Пример #4
0
class SplineWidget(flx.CanvasWidget):

    spline_type = flx.EnumProp(SPLINES,
                               'cardinal',
                               settable=True,
                               doc="""
        "The type of spline
        """)

    closed = flx.BoolProp(False,
                          settable=True,
                          doc="""
        Whether the spline is closed
        """)

    tension = flx.FloatProp(0.5,
                            settable=True,
                            doc="""
        The tension parameter for the Cardinal spline.
        """)

    _current_node = flx.Property(None, settable=True)

    def init(self):
        self.ctx = self.node.getContext('2d')
        self.xx = [0.90, 0.80, 0.70, 0.60, 0.50, 0.40, 0.10, 0.23, 0.61, 0.88]
        self.yy = [0.90, 0.60, 0.90, 0.60, 0.90, 0.70, 0.55, 0.19, 0.11, 0.38]

    def factors_linear(self, t):
        return [0, t, (1 - t), 0]

    def factors_basis(self, t):
        f0 = (1 - t)**3 / 6.0
        f1 = (3 * t**3 - 6 * t**2 + 4) / 6.0
        f2 = (-3 * t**3 + 3 * t**2 + 3 * t + 1) / 6.0
        f3 = t**3 / 6.0
        return f0, f1, f2, f3

    def factors_cardinal(self, t):
        tension = self.tension
        tau = 0.5 * (1 - tension)
        f0 = -tau * (t**3 - 2 * t**2 + t)
        f3 = +tau * (t**3 - 1 * t**2)
        f1 = 2 * t**3 - 3 * t**2 + 1 - f3
        f2 = -2 * t**3 + 3 * t**2 - f0
        return f0, f1, f2, f3

    def factors_catmullrom(self, t):
        f0 = -0.5 * t**3 + 1.0 * t**2 - 0.5 * t
        f1 = +1.5 * t**3 - 2.5 * t**2 + 1
        f2 = -1.5 * t**3 + 2.0 * t**2 + 0.5 * t
        f3 = +0.5 * t**3 - 0.5 * t**2
        return f0, f1, f2, f3

    def factors_lagrange(self, t):
        k = -1.0
        f0 = t / k * (t - 1) / (k - 1) * (t - 2) / (k - 2)
        k = 0
        f1 = (t + 1) / (k + 1) * (t - 1) / (k - 1) * (t - 2) / (k - 2)
        k = 1
        f2 = (t + 1) / (k + 1) * t / k * (t - 2) / (k - 2)
        k = 2
        f3 = (t + 1) / (k + 1) * t / k * (t - 1) / (k - 1)
        return f0, f1, f2, f3

    def factors_lanczos(self, t):
        sin = window.Math.sin
        pi = window.Math.PI
        tt = (1 + t)
        f0 = 2 * sin(pi * tt) * sin(pi * tt / 2) / (pi * pi * tt * tt)
        tt = (2 - t)
        f3 = 2 * sin(pi * tt) * sin(pi * tt / 2) / (pi * pi * tt * tt)
        if t != 0:
            tt = t
            f1 = 2 * sin(pi * tt) * sin(pi * tt / 2) / (pi * pi * tt * tt)
        else:
            f1 = 1
        if t != 1:
            tt = (1 - t)
            f2 = 2 * sin(pi * tt) * sin(pi * tt / 2) / (pi * pi * tt * tt)
        else:
            f2 = 1
        return f0, f1, f2, f3

    @flx.reaction('pointer_down')
    def _on_pointer_down(self, *events):
        for ev in events:
            w, h = self.size
            # Get closest point
            closest, dist = -1, 999999
            for i in range(len(self.xx)):
                x, y = self.xx[i] * w, self.yy[i] * h
                d = ((x - ev.pos[0])**2 + (y - ev.pos[1])**2)**0.5
                if d < dist:
                    closest, dist = i, d
            # Did we touch it or not
            if dist < 9:
                i = closest
                if 'Shift' in ev.modifiers:  # Remove point
                    self.xx.pop(i)
                    self.yy.pop(i)
                    self._set_current_node(None)
                    self.update()
                else:
                    self._set_current_node(i)
            else:
                if 'Shift' in ev.modifiers:
                    # Add point
                    if not self.xx:
                        i = 0  # There were no points
                    else:
                        # Add in between two points. Compose the vectors
                        # from closest points to neightbour points and to the
                        # cicked point. Check with which vector the latter vector
                        # aligns the best by calculating their angles.
                        #
                        # Get the three points
                        p0 = self.xx[closest + 0] * w, self.yy[closest + 0] * h
                        if closest == 0:
                            p2 = self.xx[closest + 1] * w, self.yy[closest +
                                                                   1] * h
                            p1 = p0[0] - (p2[0] - p0[0]), p0[1] - (p2[1] -
                                                                   p0[1])
                        elif closest == len(self.xx) - 1:
                            p1 = self.xx[closest - 1] * w, self.yy[closest -
                                                                   1] * h
                            p2 = p0[0] - (p1[0] - p0[0]), p0[1] - (p1[1] -
                                                                   p0[1])
                        else:
                            p1 = self.xx[closest - 1] * w, self.yy[closest -
                                                                   1] * h
                            p2 = self.xx[closest + 1] * w, self.yy[closest +
                                                                   1] * h
                        # Calculate vectors, and normalize
                        v1 = p1[0] - p0[0], p1[1] - p0[1]
                        v2 = p2[0] - p0[0], p2[1] - p0[1]
                        v3 = ev.pos[0] - p0[0], ev.pos[1] - p0[1]
                        m1 = (v1[0]**2 + v1[1]**2)**0.5
                        m2 = (v2[0]**2 + v2[1]**2)**0.5
                        m3 = (v3[0]**2 + v3[1]**2)**0.5
                        v1 = v1[0] / m1, v1[1] / m1
                        v2 = v2[0] / m2, v2[1] / m2
                        v3 = v3[0] / m3, v3[1] / m3
                        # Calculate angle
                        a1 = window.Math.acos(v1[0] * v3[0] + v1[1] * v3[1])
                        a2 = window.Math.acos(v2[0] * v3[0] + v2[1] * v3[1])
                        i = closest if a1 < a2 else closest + 1
                    self.xx.insert(i, ev.pos[0] / w)
                    self.yy.insert(i, ev.pos[1] / h)
                    self._set_current_node(i)

    @flx.reaction('pointer_up')
    def _on_pointer_up(self, *events):
        self._set_current_node(None)

    @flx.reaction('pointer_move')
    def _on_pointer_move(self, *events):
        ev = events[-1]
        if self._current_node is not None:
            i = self._current_node
            w, h = self.size
            self.xx[i] = ev.pos[0] / w
            self.yy[i] = ev.pos[1] / h
            self.update()

    @flx.reaction('size', 'spline_type', 'tension', 'closed', '_current_node')
    def update(self, *events):

        # Init
        ctx = self.ctx
        w, h = self.size
        ctx.clearRect(0, 0, w, h)

        # Get coordinates
        xx = [x * w for x in self.xx]
        yy = [y * h for y in self.yy]
        #
        if self.closed:
            xx = xx[-1:] + xx + xx[:2]
            yy = yy[-1:] + yy + yy[:2]
        else:
            xx = [xx[0] - (xx[1] - xx[0])] + xx + [xx[-1] - (xx[-2] - xx[-1])]
            yy = [yy[0] - (yy[1] - yy[0])] + yy + [yy[-1] - (yy[-2] - yy[-1])]

        # Draw grid
        ctx.strokeStyle = '#eee'
        ctx.lineWidth = 1
        for y in range(0, h, 20):
            ctx.beginPath()
            ctx.moveTo(0, y)
            ctx.lineTo(w, y)
            ctx.stroke()
        for x in range(0, w, 20):
            ctx.beginPath()
            ctx.moveTo(x, 0)
            ctx.lineTo(x, h)
            ctx.stroke()

        # Draw nodes
        ctx.fillStyle = '#acf'
        ctx.strokeStyle = '#000'
        ctx.lineWidth = 2
        for i in range(1, len(xx) - 1):
            ctx.beginPath()
            ctx.arc(xx[i], yy[i], 9, 0, 6.2831)
            ctx.fill()
            ctx.stroke()

        # Select interpolation function
        fun = self['factors_' + self.spline_type.lower()]
        if not fun:
            fun = lambda: (0, 1, 0, 0)

        # Draw lines

        for i in range(1, len(xx) - 2):

            ctx.lineCap = "round"
            ctx.lineWidth = 3
            ctx.strokeStyle = '#008'
            support = 1 if self.spline_type == 'LINEAR' else 2
            if self._current_node is not None:
                if i - (support + 1) < self._current_node < i + support:
                    ctx.strokeStyle = '#08F'
                    ctx.lineWidth = 5

            # Get coordinates of the four points
            x0, y0 = xx[i - 1], yy[i - 1]
            x1, y1 = xx[i + 0], yy[i + 0]
            x2, y2 = xx[i + 1], yy[i + 1]
            x3, y3 = xx[i + 2], yy[i + 2]

            # Interpolate
            ctx.beginPath()
            # lineto = ctx.moveTo.bind(ctx)
            lineto = ctx.lineTo.bind(ctx)
            n = 30
            for t in [i / n for i in range(n + 1)]:
                f0, f1, f2, f3 = fun(t)
                x = x0 * f0 + x1 * f1 + x2 * f2 + x3 * f3
                y = y0 * f0 + y1 * f1 + y2 * f2 + y3 * f3

                lineto(x, y)
                lineto = ctx.lineTo.bind(ctx)

            ctx.stroke()
Пример #5
0
class LeafletWidget(flx.Widget):
    """ A widget that shows a slippy/tile-map using Leaflet.
    """
    
    layers = flx.ListProp([], doc="""
        List of tilemap layer tuples: (url, 'Layer').
        """)
    
    zoom = flx.IntProp(8, settable=True, doc="""
        Zoom level for the map.
        """)
    
    min_zoom = flx.IntProp(0, settable=True, doc="""
        self zoom level for the map.
        """)
    
    max_zoom = flx.IntProp(18, settable=True, doc="""
        Maximum zoom level for the map.
        """)
    
    center = flx.FloatPairProp((5.2, 5.5), settable=True, doc="""
        The center of the map.
        """)
    
    show_layers = flx.BoolProp(False, settable=True, doc="""
        Whether to show layers-icon on the top-right of the map.
        """)
    
    show_scale = flx.BoolProp(False, settable=True, doc="""
        Whether to show scale at bottom-left of map.
        """)
    
    @flx.action
    def add_layer(self, url, name=None):
        """ Add a layer to the map.
        """
        # Avoid duplicates
        self.remove_layer(url)
        if name:
            self.remove_layer(name)
        # Add layer
        layers = self.layers + [(url, name or 'Layer')]
        self._mutate_layers(layers)
    
    @flx.action
    def remove_layer(self, url_or_name):
        """ Remove a layer from the map by url or name.
        """
        layers = list(self.layers)
        for i in reversed(range(len(layers))):
            if url_or_name in layers[i]:
                layers.pop(i)
        self._mutate_layers(layers)

    def _create_dom(self):
        global L, document
        node = document.createElement('div')
        self.mapnode = document.createElement('div')
        node.appendChild(self.mapnode)
        self.mapnode.id = 'maproot'
        self.mapnode.style.position = 'absolute'
        self.mapnode.style.top = '0px'
        self.mapnode.style.left = '0px'
        self.map = L.map(self.mapnode)
        self.map.on('zoomend', self.map_handle_zoom)
        self.map.on('moveend', self.map_handle_move)
        self.map.on('click', self.map_handle_mouse)
        self.map.on('dblclick', self.map_handle_mouse)
        # Container to keep track of leaflet layer objects
        self.layer_container = []
        self.layer_control = L.control.layers()
        self.scale = L.control.scale({'imperial': False, 'maxWidth': 200})
        # Set the path for icon images
        L.Icon.Default.prototype.options.imagePath = '_data/shared/'
        return node

    def map_handle_zoom(self, e):
        global isNaN
        zoom = self.map.getZoom()
        if isNaN(zoom):
            return
        if zoom != self.zoom:
            self.set_zoom(zoom)

    def map_handle_move(self, e):
        center_coord = self.map.getCenter()
        center = center_coord.lat, center_coord.lng
        if center != self.center:
            self.set_center(center)

    def map_handle_mouse(self, e):
        latlng = [e.latlng.lat, e.latlng.lng]
        xy = [e.layerPoint.x, e.layerPoint.y]
        self.pointer_event(e.type, latlng, xy)

    @flx.emitter
    def pointer_event(self, event, latlng, xy):
        return {'event': event, 'latlng': latlng, 'xy': xy}

    @flx.reaction
    def __handle_zoom(self):
        self.map.setZoom(self.zoom)

    @flx.reaction
    def __handle_min_zoom(self):
        self.map.setMinZoom(self.min_zoom)

    @flx.reaction
    def __handle_max_zoom(self):
        self.map.setMaxZoom(self.max_zoom)

    @flx.reaction
    def __handle_center(self):
        self.map.panTo(self.center)

    @flx.reaction
    def __handle_show_layers(self):
        if self.show_layers:
            self.map.addControl(self.layer_control)
        else:
            self.map.removeControl(self.layer_control)

    @flx.reaction
    def __handle_show_scale(self):
        if self.show_scale:
            self.map.addControl(self.scale)
        else:
            self.map.removeControl(self.scale)

    @flx.reaction
    def __size_changed(self):
        size = self.size
        if size[0] or size[1]:
            self.mapnode.style.width = size[0] + 'px'
            self.mapnode.style.height = size[1] + 'px'
            # Notify the map that it's container's size changed
            self.map.invalidateSize()

    @flx.reaction
    def __layers_changed(self):
        global L
        for layer in self.layer_container:
            self.layer_control.removeLayer(layer)
            if self.map.hasLayer(layer):
                self.map.removeLayer(layer)
        for layer_url, layer_name in self.layers:
            if not layer_url.endswith('.png'):
                if not layer_url.endswith('/'):
                    layer_url += '/'
                layer_url += '{z}/{x}/{y}.png'
            new_layer = L.tileLayer(layer_url)
            self.layer_container.append(new_layer)
            self.map.addLayer(new_layer)
            self.layer_control.addOverlay(new_layer, layer_name)
Пример #6
0
class MyApp_JS(MyWidget):
    CSS = '''
    body{
        background: #28282a;
        color: #fff;
    }
    iframe{
        background: #fff;
    }
    .flx-Button{
        background: #424242;
        color: #fff;
        border: none;
    }
    .flx-Button:hover {
        background-color: #888;
    }
    .flx-CodeEditor {
        height: 100%;
    }
    iframe.flx-HTMLPreview {
        min-width: 10vw;
    }
    .mono{
        font-family: monospace;
    }
    .small-padding{
        padding: 1em !important;
    }
    '''
    ready = flx.BoolProp(False)

    def init(self):
        global window
        super().init()
        self.buttons = {}
        with flx.VBox():
            self.buttons.source = flx.Button(css_class='mono')
            with SHBox(flex=1) as h:
                with SContent():
                    self.editor = CodeEditor(flex=1)
                with SContent():
                    with flx.VBox(css_class='small-padding'):
                        self.buttons.open_html = flx.Button(text='View html')
                        self.buttons.open_pdf = flx.Button(
                            text='Printable html')
                        with flx.VBox(flex=1):
                            self.preview = HTMLPreview()
                h.split_children()
        self._init_ready()
        return

    async def _init_ready(self):
        while not self.editor or not self.preview:
            await self.sleep(50)
        self._mutate_ready(True)
        self._init_focus()
        return

    async def _init_focus(self):
        while self.document.hasFocus() == False:
            await self.sleep(50)
        self.editor.node.focus()
        return

    @flx.action
    def set_editor(self, text):
        self.editor.cm.setValue(text)

    @flx.reaction('editor.emit_hotkey')
    def on_hotkey(self, *events):
        self.emit_hotkey(events[-1])

    @flx.emitter
    def emit_hotkey(self, event):
        return dict(value=event.value)

    @flx.action
    def set_preview(self, content):
        iframe = self.preview.node
        href = iframe.contentWindow.location.href
        iframe.srcdoc = content
        self._set_preview(iframe, href)
        return

    async def _set_preview(self, iframe, href):
        if href == 'about:srcdoc':
            iframe.contentWindow.location.reload()
        else:
            # Wait for reveal.js to reset to slide 0
            while href == iframe.contentWindow.location.href:
                await self.sleep(10)
            while href != iframe.contentWindow.location.href:
                iframe.contentWindow.location.href = href
                await self.sleep(10)
        return

    @flx.reaction('buttons.open_html.pointer_click')
    def _on_open_html(self):
        self.emit_button('open_html')

    @flx.reaction('buttons.open_pdf.pointer_click')
    def _on_open_pdf(self):
        self.emit_button('open_pdf')

    @flx.reaction('buttons.source.pointer_click')
    def _on_open_pdf(self):
        self.emit_button('source')

    @flx.emitter
    def emit_button(self, event):
        return dict(value=event)

    @flx.action
    def set_source(self, text):
        self.buttons.source.set_text(text)
Пример #7
0
class MyApp_JS(MyWidget):
    ready = flx.BoolProp(False)

    def init(self):
        super().init()
        with flx.VBox():
            with flx.HBox():
                self.wtitle = flx.Label(flex=1)
                self.wversion = flx.Label(text='v0.0.0')
            self.wprompt = MyPrompt()
            self.wconsole = MyConsole(flex=1)
        self._init_focus()
        self._init_ready()
        return

    async def _init_focus(self):
        while self.document.hasFocus() == False:
            await self.sleep(50)
        self.wprompt.set_focus()
        return

    async def _init_ready(self):
        while not self.wtitle or not self.wprompt or not self.wconsole:
            await self.sleep(50)
        self._mutate_ready(True)
        return

    @flx.emitter
    def emit_option(self, event):
        return event

    @flx.emitter
    def emit_text(self, event):
        return event

    @flx.emitter
    def emit_escape(self, event):
        return event

    @flx.emitter
    def emit_interrupt(self, event):
        return event

    @flx.emitter
    def emit_version(self, event):
        return event

    @flx.reaction('wprompt.emit_option', mode='greedy')
    def listen_option(self, *events):
        return self.emit_option(events[-1])

    @flx.reaction('wprompt.emit_text', mode='greedy')
    def listen_text(self, *events):
        return self.emit_text(events[-1])

    @flx.reaction('wprompt.emit_escape', mode='greedy')
    def listen_escape(self, *events):
        return self.emit_escape(events[-1])

    @flx.reaction('wprompt.emit_interrupt', mode='greedy')
    def listen_interrupt(self, *events):
        return self.emit_interrupt(events[-1])

    @flx.reaction('wversion.pointer_click', mode='greedy')
    def listen_version(self, *events):
        return self.emit_version(events[-1])

    @flx.action
    def set_focus(self):
        self.wprompt.set_focus()

    @flx.action
    def set_output(self, text):
        self.wconsole.set_output(text)

    @flx.action
    def append_output(self, text):
        self.wconsole.append_output(text)

    @flx.action
    def clear_output(self, text):
        self.wconsole.clear_output(text)

    @flx.action
    def set_properties(self, kwargs):
        self.wprompt.set_properties(kwargs)

    @flx.action
    def set_title(self, text):
        if self.ready:
            self.wtitle.set_text(text)

    @flx.action
    def set_version(self, text):
        if self.ready:
            self.wversion.set_text(text)