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)
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")
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)
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()
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)
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)
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)