rawpython / remi

Python REMote Interface library. Platform independent. In about 100 Kbytes, perfect for your diet.
Apache License 2.0
3.53k stars 403 forks source link

highlight of table cells in front end. #516

Open WAvdBeek opened 1 year ago

WAvdBeek commented 1 year ago

I wrote some code on the backend to change color of a tab item. I copy paste the original table & table item and added the code to it via mouseover & mouseout function. i think this also should be possible on the front end. any idea how?

here is the code:

from remi import App, gui, start

class HighLightTableItem(gui.TableItem):
    ''' Hightlight the item when hovered over
    '''

    def __init__(self, *arg, **kwargs):
        """ init, like before """
        super(HighLightTableItem, self).__init__(*arg, **kwargs)
        self.onmouseout.connect(self.mouseout)
        self.onmouseover.connect(self.mouseover)
        self.hover_over = False

    def mouseover(self, _emitter):
        """ mouse over """
        self.hover_over = True
        #print("item over")
        # css background: rgba(4, 90, 188, 0.26);
        #outline: 1px solid red;
        self.style['background'] = "rgba(4, 90, 188, 0.26)"
        #self.style['outline'] = "1px solid red"

    def mouseout(self, _emitter):
        """ mouse out """
        self.hover_over = False
        #print("item out")
        self.style['background'] = "white"
        #self.style['outline'] = "white"
        #outline: 1px solid red;

class HighLightTableWidget(gui.Table):
    ''' A subclass of gui.Table that has the feature
        that the last selected row is highlighted
        and that allows for multi_row selection.
    '''
    @property
    @gui.editor_attribute_decorator("WidgetSpecific", '''Table colum count.''', int, {'possible_values': '', 'min': 0, 'max': 100, 'default': 1, 'step': 1})
    def column_count(self): return self.__column_count
    @column_count.setter
    def column_count(self, value): self.set_column_count(value)

    @property
    @gui.editor_attribute_decorator("WidgetSpecific", '''Table row count.''', int, {'possible_values': '', 'min': 0, 'max': 100, 'default': 1, 'step': 1})
    def row_count(self): return len(self.children)
    @row_count.setter
    def row_count(self, value): self.set_row_count(value)

    @property
    @gui.editor_attribute_decorator("WidgetSpecific", '''Table use title.''', bool, {})
    def use_title(self): return self.__use_title
    @use_title.setter
    def use_title(self, value): self.set_use_title(value)

    def __init__(self, n_rows=2, n_columns=2, use_title=True, editable=False, use_selection=False, *arg, **kwargs):
        self.__column_count = 0
        self.__use_title = use_title 
        super(HighLightTableWidget, self).__init__(*arg, **kwargs)
        self._editable = editable
        self.set_use_title(use_title)
        self.set_column_count(n_columns)
        self.set_row_count(n_rows)
        self.css_display = 'table'
        # the additions below
        self.attributes['tabindex'] = '1'
        self.onkeydown.connect(self.keydown)
        self.onkeyup.connect(self.keyup)
        #self.onmouseout.connect(self.mouseout)
        #self.onmouseover.connect(self.mouseover)
        self.multi_selection_enabled = False
        self.hover_over = False
        self.selected_row_list = []
        self.use_selection = use_selection

    def set_use_title(self, use_title):
        """Returns the TableItem instance at row, column cordinates

        Args:
            use_title (bool): enable title bar.
        """
        self.__use_title = use_title
        self._update_first_row()

    def _update_first_row(self):
        cl = HighLightTableItem
        if self.__use_title:
            cl = gui.TableTitle
        if len(self.children) > 0:
            for c_key in self.children['0'].children.keys():
                instance = cl(self.children['0'].children[c_key].get_text())
                self.children['0'].append(instance, c_key)
                # here the cells of the first row are overwritten
                # and aren't appended by the standard Table.append
                # method. We have to restore de standard on_click
                #internal listener in order to make it working
                # the Table.on_table_row_click functionality
                instance.onclick.connect(self.children['0'].on_row_item_click)

    def item_at(self, row, column):
        """Returns the TableItem instance at row, column cordinates

        Args:
            row (int): zero based index
            column (int): zero based index
        """
        return self.children[str(row)].children[str(column)]

    def item_coords(self, table_item):
        """Returns table_item's (row, column) cordinates.
        Returns None in case of item not found.

        Args:
            table_item (TableItem): an item instance
        """
        for row_key in self.children.keys():
            for item_key in self.children[row_key].children.keys():
                if self.children[row_key].children[item_key] == table_item:
                    return (int(row_key), int(item_key))
        return None

    def set_row_count(self, count):
        """Sets the table row count.

        Args:
            count (int): number of rows
        """
        current_row_count = self.row_count
        current_column_count = self.column_count
        if count > current_row_count:
            cl = HighLightTableItem
            for i in range(current_row_count, count):
                tr = gui.TableRow()
                for c in range(0, current_column_count):
                    tr.append(cl(), str(c))
                    if self._editable:
                        tr.children[str(c)].onchange.connect(
                            self.on_item_changed, int(i), int(c))
                self.append(tr, str(i))
            self._update_first_row()
        elif count < current_row_count:
            for i in range(count, current_row_count):
                self.remove_child(self.children[str(i)])

    def set_column_count(self, count):
        """Sets the table column count.

        Args:
            count (int): column of rows
        """
        current_row_count = self.row_count
        current_column_count = self.column_count
        if count > current_column_count:
            cl = HighLightTableItem
            for r_key in self.children.keys():
                row = self.children[r_key]
                for i in range(current_column_count, count):
                    row.append(cl(), str(i))
                    if self._editable:
                        row.children[str(i)].onchange.connect(
                            self.on_item_changed, int(r_key), int(i))
            self._update_first_row()
        elif count < current_column_count:
            for row in self.children.values():
                for i in range(count, current_column_count):
                    row.remove_child(row.children[str(i)])
        self.__column_count = count

    @gui.decorate_set_on_listener("(self, emitter, item, new_value, row, column)")
    @gui.decorate_event
    def on_item_changed(self, item, new_value, row, column):
        """Event for the item change.

        Args:
            emitter (TableWidget): The emitter of the event.
            item (TableItem): The TableItem instance.
            new_value (str): New text content.
            row (int): row index.
            column (int): column index.
        """
        return (item, new_value, row, column)

    @gui.decorate_event
    def on_table_row_click(self, row, item):
        ''' Highlight selected row(s)
            and put the result of a muti_row selection
            in the list "self.selected_row_list".
        '''
        if not self.multi_selection_enabled:
            self.remove_selection()
        if self.use_selection:
            if row not in self.selected_row_list:
                self.selected_row_list.append(row)
                row.style['outline'] = "2px dotted blue"
        return (row, item)

    def keydown(self, _emitter, _key, ctrl, _shift, _alt):
        """ key down function """
        # print("key:%d ctrl:%d shift:%d alt:%d", (key, ctrl, shift, alt))
        if self.use_selection:
            if ctrl.lower() == 'true':
                self.multi_selection_enabled = True

    def keyup(self, _emitter, _key, _ctrl, _shift, _alt):
        """ key up function """
        self.multi_selection_enabled = False

    def remove_selection(self):
        """ remove selection """
        for r in self.selected_row_list:
            del r.style['outline']
        self.selected_row_list = []

    def mouseover(self, _emitter):
        """ mouse over """
        self.hover_over = True
        print("table over")
        # css background: rgba(4, 90, 188, 0.26);
        #outline: 1px solid red;
        self.style['background'] = "rgba(4, 90, 188, 0.26)"
        self.style['outline'] = "1px solid red"

    def mouseout(self, _emitter):
        """ mouse out """
        self.hover_over = False
        print("table out")
        self.style['background'] = "rgba(4, 90, 188, 0.26)"
        self.style['outline'] = "white"
        #outline: 1px solid red;
dddomodossola commented 1 year ago

Hello @WAvdBeek,

You specified style['background'] but it should be style['background-color']. Here is the working code:

from remi import App, gui, start

class HighLightTableItem(gui.TableItem):
    ''' Hightlight the item when hovered over
    '''
    def __init__(self, *arg, **kwargs):
        """ init, like before """
        super(HighLightTableItem, self).__init__(*arg, **kwargs)
        self.onmouseout.do(self.mouseout, js_stop_propagation = True, js_prevent_default = True)
        self.onmouseleave.do(self.mouseout, js_stop_propagation = True, js_prevent_default = True)
        self.onmouseover.do(self.mouseover, js_stop_propagation = True, js_prevent_default = True)
        self.hover_over = False

    def mouseover(self, _emitter):
        """ mouse over """
        self.hover_over = True
        self.style['background-color'] = "rgba(4, 90, 188, 0.26)"

    def mouseout(self, _emitter):
        """ mouse out """
        self.hover_over = False
        del self.style['background-color']

class HighLightTableWidget(gui.Table):
    ''' A subclass of gui.Table that has the feature
        that the last selected row is highlighted
        and that allows for multi_row selection.
    '''
    @property
    @gui.editor_attribute_decorator("WidgetSpecific", '''Table colum count.''', int, {'possible_values': '', 'min': 0, 'max': 100, 'default': 1, 'step': 1})
    def column_count(self): return self.__column_count
    @column_count.setter
    def column_count(self, value): self.set_column_count(value)

    @property
    @gui.editor_attribute_decorator("WidgetSpecific", '''Table row count.''', int, {'possible_values': '', 'min': 0, 'max': 100, 'default': 1, 'step': 1})
    def row_count(self): return len(self.children)
    @row_count.setter
    def row_count(self, value): self.set_row_count(value)

    @property
    @gui.editor_attribute_decorator("WidgetSpecific", '''Table use title.''', bool, {})
    def use_title(self): return self.__use_title
    @use_title.setter
    def use_title(self, value): self.set_use_title(value)

    def __init__(self, n_rows=2, n_columns=2, use_title=True, editable=False, use_selection=False, *arg, **kwargs):
        self.__column_count = 0
        self.__use_title = use_title 
        super(HighLightTableWidget, self).__init__(*arg, **kwargs)
        self._editable = editable
        self.set_use_title(use_title)
        self.set_column_count(n_columns)
        self.set_row_count(n_rows)
        self.css_display = 'table'
        # the additions below
        self.attributes['tabindex'] = '1'
        self.onkeydown.do(self.keydown)
        self.onkeyup.do(self.keyup)
        #self.onmouseout.do(self.mouseout)
        #self.onmouseover.do(self.mouseover)
        self.multi_selection_enabled = False
        self.hover_over = False
        self.selected_row_list = []
        self.use_selection = use_selection

    def set_use_title(self, use_title):
        """Returns the TableItem instance at row, column cordinates

        Args:
            use_title (bool): enable title bar.
        """
        self.__use_title = use_title
        self._update_first_row()

    def _update_first_row(self):
        cl = HighLightTableItem
        if self.__use_title:
            cl = gui.TableTitle
        if len(self.children) > 0:
            for c_key in self.children['0'].children.keys():
                instance = cl(self.children['0'].children[c_key].get_text())
                self.children['0'].append(instance, c_key)
                # here the cells of the first row are overwritten
                # and aren't appended by the standard Table.append
                # method. We have to restore de standard on_click
                #internal listener in order to make it working
                # the Table.on_table_row_click functionality
                instance.onclick.do(self.children['0'].on_row_item_click)

    def append_from_list(self, content, fill_title=False):
        """
        Appends rows created from the data contained in the provided
        list of tuples of strings. The first tuple of the list can be
        set as table title.

        Args:
            content (list): list of tuples of strings. Each tuple is a row.
            fill_title (bool): if true, the first tuple in the list will
                be set as title.
        """
        row_index = 0
        for row in content:
            tr = gui.TableRow()
            column_index = 0
            for item in row:
                if row_index == 0 and fill_title:
                    ti = gui.TableTitle(item)
                else:
                    ti = HighLightTableItem(item)
                tr.append(ti, str(column_index))
                column_index = column_index + 1
            self.append(tr, str(row_index))
            row_index = row_index + 1

    def set_row_count(self, count):
        """Sets the table row count.

        Args:
            count (int): number of rows
        """
        current_row_count = self.row_count
        current_column_count = self.column_count
        if count > current_row_count:
            cl = HighLightTableItem
            for i in range(current_row_count, count):
                tr = gui.TableRow()
                for c in range(0, current_column_count):
                    tr.append(cl(), str(c))
                    if self._editable:
                        tr.children[str(c)].onchange.do(
                            self.on_item_changed, int(i), int(c))
                self.append(tr, str(i))
            self._update_first_row()
        elif count < current_row_count:
            for i in range(count, current_row_count):
                self.remove_child(self.children[str(i)])

    def set_column_count(self, count):
        """Sets the table column count.

        Args:
            count (int): column of rows
        """
        current_row_count = self.row_count
        current_column_count = self.column_count
        if count > current_column_count:
            cl = HighLightTableItem
            for r_key in self.children.keys():
                row = self.children[r_key]
                for i in range(current_column_count, count):
                    row.append(cl(), str(i))
                    if self._editable:
                        row.children[str(i)].onchange.do(
                            self.on_item_changed, int(r_key), int(i))
            self._update_first_row()
        elif count < current_column_count:
            for row in self.children.values():
                for i in range(count, current_column_count):
                    row.remove_child(row.children[str(i)])
        self.__column_count = count

    @gui.decorate_event
    def on_item_changed(self, item, new_value, row, column):
        """Event for the item change.

        Args:
            emitter (TableWidget): The emitter of the event.
            item (TableItem): The TableItem instance.
            new_value (str): New text content.
            row (int): row index.
            column (int): column index.
        """
        return (item, new_value, row, column)

    @gui.decorate_event
    def on_table_row_click(self, row, item):
        ''' Highlight selected row(s)
            and put the result of a muti_row selection
            in the list "self.selected_row_list".
        '''
        if not self.multi_selection_enabled:
            self.remove_selection()
        if self.use_selection:
            if row not in self.selected_row_list:
                self.selected_row_list.append(row)
                row.style['outline'] = "2px dotted blue"
        return (row, item)

    def keydown(self, _emitter, _key, ctrl, _shift, _alt):
        """ key down function """
        # print("key:%d ctrl:%d shift:%d alt:%d", (key, ctrl, shift, alt))
        if self.use_selection:
            if ctrl.lower() == 'true':
                self.multi_selection_enabled = True

    def keyup(self, _emitter, _key, _ctrl, _shift, _alt):
        """ key up function """
        self.multi_selection_enabled = False

    def remove_selection(self):
        """ remove selection """
        for r in self.selected_row_list:
            del r.style['outline']
        self.selected_row_list = []

class MyApp(App):
    def main(self):
        #creating a container VBox type, vertical (you can use also HBox or Widget)
        main_container = gui.VBox(width=300, height=200, style={'margin':'0px auto'})

        self.table = HighLightTableWidget.new_from_list([('ID', 'First Name', 'Last Name'),    
                                   ('101', 'Danny', 'Young'),
                                   ('102', 'Christine', 'Holand'),
                                   ('103', 'Lars', 'Gordon'),
                                   ('104', 'Roberto', 'Robitaille'),
                                   ('105', 'Maria', 'Papadopoulos')], width=300, height=200, margin='10px')

        main_container.append(self.table)

        # returning the root widget
        return main_container

if __name__ == "__main__":
    start(MyApp, address='0.0.0.0', port=0, start_browser=True, update_interval = 0)