class MyObject(event.Component): att = event.Attribute() # Props to test basic stuff foo = event.AnyProp(6, settable=True, doc='can be anything') bar = event.StringProp('xx') # not settable # Props to test array mutations eggs = event.ListProp([], settable=True) eggs2 = event.ListProp(settable=True) eggs3 = event.ListProp([3, 4]) # All kinds of props, defaults anyprop = event.AnyProp(doc='can be anything', settable=True) boolprop = event.BoolProp(settable=True) tristateprop = event.TriStateProp(settable=True) intprop = event.IntProp(settable=True) floatprop = event.FloatProp(settable=True) stringprop = event.StringProp(settable=True) tupleprop = event.TupleProp(settable=True) listprop = event.ListProp(settable=True) dictprop = event.DictProp(settable=True) componentprop = event.ComponentProp(settable=True) # can be None # nullprop = event.NullProp(None, settable=True) # eitherprop = event.EitherProp(event.IntProp, event.NoneProp) _privateprop = event.IntProp(settable=True)
class Person(event.Component): first_name = event.StringProp('Jane', settable=True) last_name = event.StringProp('Doe', settable=True) children = event.ListProp([], doc='The children of this person') @event.action def add_child(self, child): self._mutate_children(self.children + [child])
class MyApp(app.JsComponent): """ This the root of the app, accessible via self.root on any component. It functions as a central data-store. In this case it is a JsComponent, but it can also be a PyComponent if that makes more sense. """ first_name = event.StringProp(settable=True) last_name = event.StringProp(settable=True) def init(self): View()
class ColabPainting(app.PyComponent): """ The Python side of the app. There is one instance per connection. """ color = event.ColorProp(settable=True, doc="Paint color") status = event.StringProp('', settable=True, doc="Status text") def init(self): self.set_color(random.choice(COLORS)) self.widget = ColabPaintingView(self) self._update_participants() @event.action def add_paint(self, pos): """ Add paint at the specified position. """ relay.add_paint_for_all(pos, self.color.hex) @relay.reaction('add_paint_for_all') # note that we connect to relay here def _any_user_adds_paint(self, *events): """ Receive global paint event from the relay, invoke action on view. """ for ev in events: self.widget.add_paint_to_canvas(ev.pos, ev.color) @app.manager.reaction('connections_changed') def _update_participants(self, *events): if self.session.status: sessions = app.manager.get_connections(self.session.app_name) n = len(sessions) del sessions self.set_status('%i persons are painting' % n)
class Greeter(event.Component): message = event.StringProp('', settable=True) @event.reaction def show_message(self): print('Message:', self.message)
class MyDefaults(event.Component): # Custom defaults anyprop2 = event.AnyProp(7, doc='can be anything') boolprop2 = event.BoolProp(True) intprop2 = event.IntProp(-9) floatprop2 = event.FloatProp(800.45) stringprop2 = event.StringProp('heya') tupleprop2 = event.TupleProp((2, 'xx')) listprop2 = event.ListProp([3, 'yy']) dictprop2 = event.DictProp({'foo':3, 'bar': 4}) componentprop2 = event.ComponentProp(None)
class ScaleImageWidget(flx.Widget): """ Display an image from a url. The ``node`` of this widget is an `<img> <https://developer.mozilla.org/docs/Web/HTML/Element/img>`_ wrapped in a `<div> <https://developer.mozilla.org/docs/Web/HTML/Element/div>`_ (the ``outernode``) to handle sizing. """ DEFAULT_MIN_SIZE = 16, 16 _sequence = 0 source = event.StringProp('', settable=True, doc=""" The source of the image, This can be anything that an HTML img element supports. """) stretch = event.BoolProp(False, settable=True, doc=""" Whether the image should stretch to fill all available space, or maintain its aspect ratio (default). """) def _create_dom(self): global window outer = window.document.createElement('div') inner = window.document.createElement('img') outer.appendChild(inner) return outer, inner @event.reaction def __resize_image(self): size = self.size if self.stretch: self.node.style.maxWidth = None self.node.style.maxHeight = None self.node.style.width = size[0] + 'px' self.node.style.height = size[1] + 'px' else: self.node.style.backgroundColor = None self.node.style.marginLeft = "5%" self.node.style.marginTop = "5%" self.node.style.maxWidth = "90%" self.node.style.maxWidth = "auto" self.node.style.width = "90%" self.node.style.height = "auto" @event.reaction def __source_changed(self): self.node.src = self.source
class Person(event.Component): first_name = event.StringProp('Jane', settable=True) last_name = event.StringProp('Doe', settable=True) children = event.ListProp([], doc='The children of this person') # Actions @event.action def add_child(self, child): self._mutate_children(self.children + [child]) @event.emitter def eat(self): #self.emit('eat', {'name': self.first_name}) return {'name': self.first_name} # Reactions @event.reaction('first_name:xx', 'last_name') def greet_explitic(self, *events): print('Explicit hello %s %s' % (self.first_name, self.last_name)) @event.reaction def greet_implicit(self): print('Implicit hello %s %s' % (self.first_name, self.last_name)) @event.reaction('children*.first_name') def greetall_explicit(self, *events): print('Explicit hey kids ' + ', '.join(n.first_name for n in self.children) + '!') @event.reaction def greetall_implicit(self): print('Implicit hey kids ' + ', '.join(n.first_name for n in self.children) + '!') @event.reaction('!eat') def track_eating(self, *events): for ev in events: print(ev.name + ' is eating')
class FileTab(ui.Widget): """ Tab for file plotting mode """ title = event.StringProp('File mode') def init(self): with ui.VBox(): ui.Widget(flex=1) ui.Label(flex=0, text='Select a gcode file here:') #with ui.HBox(flex=0): #self.path_label = ui.LineEdit(flex=3, text='C:/Path/to/Gcode/File.gcode', disabled=True) #self.open_button = ui.Button(flex=1, text='Open...', title='open') self.open_gcode_widget = OpenFileWidget() ui.Widget(flex=1) with ui.HBox(flex=0): self.use_gcode_speeds_button = ui.ToggleButton( flex=0, text='Use gcode speeds', title='use_gcode_speed') ui.Widget(flex=1) ui.Widget(flex=8) # @event.reaction('open_button.mouse_click') # def _button_clicked(self, *events): # ev = events[-1] # if ev.source.title == 'open': # self.root.update_info_label('open clicked') @event.reaction('use_gcode_speeds_button.checked') def _button_toggled(self, *events): self.root.handle_use_gcode_speeds_clicked( self.use_gcode_speeds_button.checked) @event.reaction('open_gcode_widget.file') def _new_gcode_file_loaded(self): self._convert_gcode_file_to_string() @event.action def _convert_gcode_file_to_string(self): def _get_string(event): self.root.handle_new_gcode_file(event.target.result) if self.open_gcode_widget.file is not None: reader = window.FileReader() reader.onload = _get_string reader.readAsText(self.open_gcode_widget.file) @event.action def propagate_change(self, name_changed): if name_changed == 'settings': self.use_gcode_speeds_button.set_checked( self.root.settings.get('use_gcode_speeds', self.use_gcode_speeds_button.checked))
class Markdown(Widget): """ A widget that shows a rendered Markdown content. """ content = event.StringProp(settable=True, doc=""" The markdown content to be rendered """) @event.reaction def __content_change(self): global showdown conv = showdown.Converter() self.node.innerHTML = conv.makeHtml(self.content)
class Foo(Component): __x = 3 foo = event.StringProp('asd', settable=True) @event.action def do_bar(self, v=0): print(v) @event.reaction def react2foo(self): print(self.foo) def __xx(self): pass
class LineTab(ui.Widget): """ Tab for line plotting mode """ title = event.StringProp('Line mode') def init(self): with ui.VBox(): ui.HBox(flex=1) with ui.HBox(flex=0): self.gcode_line = ui.LineEdit( flex=3, placeholder_text='e.g. G01 Y10 Y2 Z-1') ui.HBox(flex=4) @event.reaction('gcode_line.user_text') def _text_changed(self, *events): self.root.on_gcode_line_text_changed(self.gcode_line.user_text) @event.action def propagate_change(self, name_changed): pass
class RawTab(ui.Widget): """ Tab for raw plotting mode """ title = event.StringProp('Raw mode') def init(self): with ui.VBox(): ui.HBox(flex=1) with ui.HBox(flex=0): self.raw_command = ui.LineEdit(flex=3, placeholder_text='e.g. XA1000') ui.HBox(flex=4) @event.reaction('raw_command.user_text') def _text_changed(self, *events): self.root.on_raw_text_changed(self.raw_command.user_text) @event.action def propagate_change(self, name_changed): pass
class CachingExample(event.Component): source = event.StringProp('', settable=True, doc='The input for the calculations.') data = event.AnyProp(None, settable=True, doc='Cache of the calculation result.') @event.reaction('source') def download_data(self, *events): """ Simulate a download of data from the web. takes a while. """ if self.source: time.sleep(2) self.set_data(hash(self.source)) @event.reaction def show_data(self): """ handler to show the data. Can be called at any time. """ if self.data is not None: print('The data is', self.data)
class MyDefaults2(MyDefaults): floatprop2 = event.FloatProp(3.14) stringprop2 = event.StringProp('hi')
class Example(ui.Widget): persons = event.TupleProp((), doc=""" People to show cards for""") first_name = event.StringProp('', settable=True) last_name = event.StringProp('', settable=True) @event.action def add_person(self, name, info): """ Add a person to our stack. """ ppl = list(self.persons) ppl.append((name, info)) self._mutate_persons(ppl) def _button_clicked(self, *events): self.add_person(self.first_name, self.last_name) def _render_dom(self): """ This function gets automatically called when needed; Flexx is aware of what properties are used here. """ # Create form elements form_nodes = [ ui.create_element( 'div', {'class': 'form-group mb-2'}, ui.create_element( 'input', { 'class': 'form-control', 'id': 'inputFirstName', 'oninput': lambda e: self.set_first_name(e.target.value) }, 'First name')), ui.create_element( 'div', {'class': 'form-group mx-sm-3 mb-2'}, ui.create_element( 'input', { 'class': 'form-control', 'id': 'inputLastName', 'oninput': lambda e: self.set_last_name(e.target.value) }, 'Last name')), ui.create_element('button', { 'class': 'btn btn-primary mb-2', 'onclick': self._button_clicked }, 'Submit'), ] # Create virtual DOM nodes for all persons. We use bootstrap cards card_nodes = [] for name, info in self.persons: person_node = ui.create_element( 'div', {'class': 'card'}, ui.create_element( 'div', {'class': 'card-body'}, ui.create_element('h5', {'class': 'card-title'}, name), ui.create_element('p', {'class': 'card-text'}, info), )) card_nodes.append(person_node) # Compose finaly DOM tree return ui.create_element( 'div', {}, ui.create_element('div', {'class': 'form-inline'}, form_nodes), *card_nodes)
class Person(event.Component): first_name = event.StringProp('John', settable=True) last_name = event.StringProp('Doe', settable=True)
class AppRoot(app.PyComponent): """ Root widget This class talks to the Laser Driver module and handles the connection to the ui It also saves the state of all variables """ gcode_file = event.StringProp() gcode_line = event.StringProp() raw_command = event.StringProp() current_mode = event.StringProp('file') settings = event.DictProp( ) #{'resolution': 150, 'serial_port': 1, 'serial_baudrate': 19200, 'x_steps_per_mm': 378, #'y_steps_per_mm': 12, 'fast_movement_speed': 10, 'engraving_movement_speed': 2, #'use_gcode_speeds': False} states = event.DictProp() simulation_states = event.DictProp() settings_types = { 'resolution': float, 'serial_port': str, 'serial_baudrate': int, 'x_steps_per_mm': float, 'y_steps_per_mm': float, 'fast_movement_speed': float, 'engraving_movement_speed': float, 'use_gcode_speeds': bool, 'simulation_mode': int, 'burnin_time': float } state_ = event.StringProp('idle', settable=True) def init(self): self._state = 'idle' self._simulation_state = 'idle' self.laser_driver = LaserDriver.LaserDriver() self.laser_driver.callback_function = self.low_level_parameter_changed self.view = View() self.initialize_UI() @property def state(self): if self.settings.get('simulation_mode') < 2: return self._state else: if self._simulation_state == 'idle': self.state = 'ready' return self._simulation_state @state.setter def state(self, new_state): if self.settings.get('simulation_mode') < 2: self._state = new_state else: self._simulation_state = new_state self.__state_changed(new_state) @event.action def __state_changed(self, new_state): self._mutate_state_(new_state) self.emit('state_', {'new_value': new_state}) @event.action def initialize_UI(self): try: self.laser_driver.logger.addHandler( StreamHandler( stream=StreamToInfoLabel(lambda text: event.loop.call_soon( self.update_info_label, text)))) except Exception as e: print(str(e)) settings = { 'resolution': self.laser_driver.resolution, 'serial_port': self.laser_driver.serial_port, 'serial_baudrate': self.laser_driver.serial_baudrate, 'x_steps_per_mm': self.laser_driver.x_steps_per_mm, 'y_steps_per_mm': self.laser_driver.y_steps_per_mm, 'fast_movement_speed': self.laser_driver.fast_movement_speed, 'engraving_movement_speed': self.laser_driver.engraving_movement_speed, 'use_gcode_speeds': self.laser_driver.use_gcode_speeds, 'simulation_mode': self.laser_driver.simulation_mode, 'burnin_time': self.laser_driver.burnin_time } states = { 'idle': [('start_button.text', 'Start plot'), ('start_button.disabled', True), ('abort_button.text', 'Abort plot'), ('abort_button.disabled', True), ('connect_button.text', 'Connect to plotter'), ('connect_button.disabled', False)], 'ready': [('start_button.text', 'Start plot'), ('start_button.disabled', False), ('abort_button.text', 'Abort plot'), ('abort_button.disabled', True), ('connect_button.text', 'Disconnect from plotter'), ('connect_button.disabled', False)], 'active': [('start_button.text', 'Pause plot'), ('start_button.disabled', False), ('abort_button.text', 'Abort plot'), ('abort_button.disabled', False), ('connect_button.text', 'Disconnect from plotter'), ('connect_button.disabled', True)], 'error': [('start_button.text', 'Resume plot'), ('start_button.disabled', False), ('abort_button.text', 'Abort plot'), ('abort_button.disabled', False), ('connect_button.text', 'Disconnect from plotter'), ('connect_button.disabled', True)], 'pause': [('start_button.text', 'Resume plot'), ('start_button.disabled', False), ('abort_button.text', 'Abort plot'), ('abort_button.disabled', False), ('connect_button.text', 'Disconnect from plotter'), ('connect_button.disabled', True)] } simulation_states = { 'idle': [('start_button.text', 'Start plot'), ('start_button.disabled', False), ('abort_button.text', 'Abort plot'), ('abort_button.disabled', True), ('connect_button.disabled', True)], 'ready': [('start_button.text', 'Start plot'), ('start_button.disabled', False), ('abort_button.text', 'Abort plot'), ('abort_button.disabled', True), ('connect_button.disabled', True)], 'active': [('start_button.text', 'Pause plot'), ('start_button.disabled', False), ('abort_button.text', 'Abort plot'), ('abort_button.disabled', False), ('connect_button.disabled', True)], 'error': [('start_button.text', 'Resume plot'), ('start_button.disabled', False), ('abort_button.text', 'Abort plot'), ('abort_button.disabled', False), ('connect_button.disabled', True)], 'pause': [('start_button.text', 'Resume plot'), ('start_button.disabled', False), ('abort_button.text', 'Abort plot'), ('abort_button.disabled', False), ('connect_button.disabled', True)] } self._mutate_settings(settings, 'set') self._mutate_states(states, 'set') self._mutate_simulation_states(simulation_states, 'set') @event.action def on_raw_text_changed(self, text): self._mutate_raw_command(text) @event.action def on_gcode_line_text_changed(self, text): self._mutate_gcode_line(text) @event.action def set_current_mode(self, mode): self._mutate_current_mode(mode) #self.update_info_label(self.current_mode) @event.action def handle_connect_clicked(self): self.update_info_label('') if self.state == 'idle': self.laser_driver.execute_command('start connection') #self.update_info_label('connected') elif self.state == 'ready': self.laser_driver.execute_command('close connection') #self.update_info_label('disconnected') @event.action def handle_abort_clicked(self): self.update_info_label('') self.laser_driver.abort() #self.update_info_label('abort') @event.action def handle_start_clicked(self): self.update_info_label('') # if self.current_mode == 'raw': # self.propagate_change(self.raw_command) if self.state == 'ready': contents = { 'file': self.gcode_file, 'line': self.gcode_line, 'raw': self.raw_command } self.laser_driver.execute_command( self.current_mode, content=contents[self.current_mode]) #self.update_info_label('start') elif self.state in {'pause', 'error'}: self.laser_driver.execute_command(self.current_mode) #self.update_info_label('resume') elif self.state == 'active': self.laser_driver.pause() #self.update_info_label('pause') @event.action def handle_use_gcode_speeds_clicked(self, checked): self._mutate_settings({'use_gcode_speeds': checked}, 'replace') #self.update_info_label('use gcode speeds {}'.format('ON' if checked else 'OFF')) @event.action def handle_new_gcode_file(self, file_content): self._mutate_gcode_file(file_content) #self.update_info_label('new gcode file arrived') @event.action def handle_setting_changed(self, setting_name, new_value): old_value = self.settings.get(setting_name) try: new_value = self.settings_types[setting_name](new_value) except ValueError as e: self.propagate_change('settings') self.update_info_label(str(e)) except KeyError as e: self.update_info_label(str(e)) else: if old_value is not None and new_value != old_value: self._mutate_settings({setting_name: new_value}, 'replace') @event.action def handle_state_changed(self, new_state): self.state = new_state @event.action def update_info_label(self, text): if hasattr(self, 'view'): self.view.update_info_label(text) @event.action def propagate_change(self, name_changed): if hasattr(self, 'view'): self.view.propagate_change(name_changed) def low_level_parameter_changed(self, description_dict): event.loop.call_soon(self.main_thread_callback, description_dict) @event.action def main_thread_callback(self, description_dict): if description_dict.get('action') == 'set': if description_dict.get('parameter') == 'state': self.state = description_dict.get('value') elif description_dict.get('action') == 'done': if description_dict.get('value') == 'raw' and description_dict.get( 'position') is not None: position = description_dict['position'] movement = 'move cursor:' if position.get('z', 0) > 0: movement = 'line to:' command = '{:s}{:g} {:g}'.format(movement, position.get('x', 0), position.get('y', 0)) self.propagate_change(command) if description_dict.get('done_event'): description_dict['done_event'].set() @event.reaction('gcode_file', 'gcode_line', 'raw_command', 'current_mode', 'settings', 'state_') def property_changed(self, *events): for ev in events: if ev.type == 'settings': if ev.mutation == 'replace': for key, value in ev.objects.items(): setattr(self.laser_driver, key, value) elif ev.mutation == 'set': for key, value in ev.new_value.items(): setattr(self.laser_driver, key, value) self.state = self.state self.propagate_change('settings') elif ev.type == 'state_': self.propagate_change('state_')
class TestOb(event.Component): children = event.TupleProp(settable=True) foo = event.StringProp(settable=True)
class SettingsTab(ui.Widget): """ Tab for settings """ title = event.StringProp('Settings') def init(self): with ui.VBox(): with ui.HFix(flex=1): with ui.VBox(flex=2): ui.Label(text='Resolution (dpi): ') ui.Label(text='Serial port: ') ui.Label(text='Serial baudrate: ') ui.Label(text='Steps per mm (x, y): ') ui.Label(text='Speed in mm/s (fast, engrave): ') ui.Label(text='Burnin time in ms: ') with ui.VBox(flex=1): self.resolution_widget = ui.LineEdit(title='resolution') self.serial_port_widget = ui.LineEdit(title='serial_port') self.serial_baudrate_widget = ui.LineEdit( title='serial_baudrate') with ui.HBox(): self.x_steps_widget = ui.LineEdit( title='x_steps_per_mm') self.y_steps_widget = ui.LineEdit( title='y_steps_per_mm') with ui.HBox(): self.fast_speed_widget = ui.LineEdit( title='fast_movement_speed') self.engrave_speed_widget = ui.LineEdit( title='engraving_movement_speed') self.burnin_time_widget = ui.LineEdit(title='burnin_time') ui.Widget(flex=1) @event.reaction('resolution_widget.submit', 'serial_port_widget.submit', 'serial_baudrate_widget.submit', 'x_steps_widget.submit', 'y_steps_widget.submit', 'fast_speed_widget.submit', 'engrave_speed_widget.submit') def _settings_changed(self, *events): ev = events[-1] self.root.handle_setting_changed(ev.source.title, ev.source.text) @event.action def propagate_change(self, name_changed): if name_changed == 'settings': self.resolution_widget.set_text( str( self.root.settings.get('resolution', self.resolution_widget.text))) self.serial_port_widget.set_text( str( self.root.settings.get('serial_port', self.serial_port_widget.text))) self.serial_baudrate_widget.set_text( str( self.root.settings.get('serial_baudrate', self.serial_baudrate_widget.text))) self.x_steps_widget.set_text( str( self.root.settings.get('x_steps_per_mm', self.x_steps_widget.text))) self.y_steps_widget.set_text( str( self.root.settings.get('y_steps_per_mm', self.y_steps_widget.text))) self.fast_speed_widget.set_text( str( self.root.settings.get('fast_movement_speed', self.fast_speed_widget.text))) self.engrave_speed_widget.set_text( str( self.root.settings.get('engraving_movement_speed', self.engrave_speed_widget.text))) self.burnin_time_widget.set_text( str( self.root.settings.get('burnin_time', self.burnin_time_widget.text)))