import random import string import gi gi.require_version("Gtk", "4.0") from gi.repository import Gio, GLib, GObject, Gtk # noqa class DataObject(GObject.GObject): __gtype_name__ = "DataObject" text = GObject.Property(type=GObject.TYPE_STRING, default="") number = GObject.Property(type=GObject.TYPE_FLOAT, default=0) truefalse = GObject.Property(type=GObject.TYPE_BOOLEAN, default=False) def __init__(self, text, number, truefalse=False): super().__init__() self.text = text self.number = number self.truefalse = truefalse class MyApp(Gtk.Application): def __init__(self): super().__init__(application_id="org.gtk.Example") self.list_view = Gtk.ColumnView() self.store = Gio.ListStore.new(DataObject) self.sort_model = Gtk.SortListModel.new(self.store) self.selection = Gtk.SingleSelection.new(self.sort_model) self.list_view.set_model(self.selection) self.update_timer = None @staticmethod def to_str(bind, from_value): return str(from_value) def update_data(self): print("Updating data...") for item in self.store: random_text = "".join( random.choices(string.ascii_uppercase + string.digits, k=5) ) random_number = random.randint(5555, 9999) item.set_property("text", random_text) item.set_property("number", random_number) item.set_property("truefalse", random.choice([True, False])) # Return True to keep the timeout return True def init_columnview(self): properties = [prop.name for prop in DataObject.list_properties()] for x in range(10): self.store.append(DataObject("entry", 0.0)) for i, property_name in enumerate(properties): factory = Gtk.SignalListItemFactory() factory.connect("setup", self.setup, property_name) factory.connect("bind", self.bind, property_name) column = Gtk.ColumnViewColumn.new(f"column{i+1:02}", factory) # Create a Gtk.Expression for the property property_expression = Gtk.PropertyExpression.new(DataObject, None, property_name) # Create a Gtk.Sorter based on the property type property_type = DataObject.find_property(property_name).value_type.fundamental if property_type == GObject.TYPE_STRING: sorter = Gtk.StringSorter.new(property_expression) elif property_type == GObject.TYPE_FLOAT: sorter = Gtk.NumericSorter.new(property_expression) elif property_type == GObject.TYPE_BOOLEAN: sorter = Gtk.NumericSorter.new(property_expression) # Set the sorter on the column column.set_sorter(sorter) self.list_view.append_column(column) self.update_timer = GLib.timeout_add_seconds(1, self.update_data) def setup(self, widget, item, property_name): """Setup the widget to show in the Gtk.Listview""" def setup_when_idle(): obj = item.get_item() property_type = obj.find_property(property_name).value_type if property_type == GObject.TYPE_BOOLEAN: widget_type = Gtk.CheckButton else: widget_type = Gtk.Label widget = widget_type() item.set_child(widget) GLib.idle_add(setup_when_idle) def bind(self, widget, item, property_name): """bind data from the store object to the widget""" def bind_when_idle(): child = item.get_child() obj = item.get_item() property_type = obj.find_property(property_name).value_type if property_type == GObject.TYPE_BOOLEAN: widget_property = "active" obj.bind_property( property_name, child, widget_property, GObject.BindingFlags.SYNC_CREATE, ) else: widget_property = "label" # child.bind_property(widget_property, obj, property_name, # GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL) obj.bind_property( property_name, child, widget_property, GObject.BindingFlags.SYNC_CREATE, self.to_str, ) GLib.idle_add(bind_when_idle) def do_activate(self): win = Gtk.ApplicationWindow( application=self, title="Gtk4 is Awesome !!!", default_height=400, default_width=400, ) sw = Gtk.ScrolledWindow() self.init_columnview() sw.set_child(self.list_view) win.set_child(sw) win.present() app = MyApp() app.run(None)