Gtk4 Gtk.ListView() Python example

I’m having a hard time trying to wrap my head around the new Gtk4 ListView model / paradigm. It seems to be completely different, at least syntactically, from the Gtk.TreeView(ListStore).append_column(column) system which was already not trivial - and quite different from the Gtk2 model, but so much time has passed I can’t really remember. The concept of “item factory” in particular is really puzzling me, but I’ll surely understand once I see one practical example, I found one example but it’s quite cryptic, looks overly convoluted, and uses unusual imports, custom classes and methods that I did not manage to understand, let alone apply to my project. I making a single column list that complicated?

Last try:

<object class="GtkScrolledWindow" id="playListWindow">
<child>
<object class="GtkTreeView" id="treeview">
  <property name="model">listStore</property>
  <child>
    <object class="GtkTreeViewColumn" id="test-column">
      <property name="title">Test</property>
      <child>
        <object class="GtkCellRendererText" id="test-renderer"/>

      </child>
    </object>
  </child>
  <child internal-child="selection">
    <object class="GtkTreeSelection" id="selection">
      <signal name="changed" handler="on_treeview_selection_changed"/>
    </object>
  </child>
</object>
<!-- (...) -->
  <object class="GtkListStore" id="listStore"/>
    <object class="GtkTreeModelSort" id="treeModel">
    <property name="model">listModel</property>
  </object>

And, naively, in I tried to append() things to the listStore, the closest I got was “row has the wrong number of items” but that’s it. It really feels close, though… could it be that simple?

Sometimes I’m able to port / translate the C examples to Python, but in this case I really don’t know, is it me or is is really complex?

It’s been two days now, I just need a list… Can a kind spirit guide me towards a simple, 2 colums - 2 rows Python example?

Yes, it is not that easy. I was able to create some Nim examples, see GTK4 for Graphical User Interfaces

My guess is that it should work very similar for Python. Generally, what works in Nim, does work of course in C, and should work with Python. Unfortunately not the other way round, the gintro Nim bindings are a bit restricted still :slight_smile:

I spent a lot of time on these examples, I guess some weeks fulltime.

[EDIT] For Python, during my research I found this one: Reorder rows in a list (GTK4) - #4 by SoongNoonien

1 Like

[EDIT] For Python, during my research I found this one: Reorder rows in a list (GTK4) - #4 by SoongNoonien

Oooh nice one ; I’m going to study it, and your examples (that indeed look like a lot of labor) thanks again, Stefan.

1 Like

I found this one

Wow. this is complex bordering on insanity. You would think that the drag & drop logic would be conveniently hidden behind the scene instead of being left to the developer…? I mean, you drag, you drop, it’s a new list, I mean it’s 15 lines of JS/ HTML tops, sorry, I’m rambling :wink:

Still, everything is here, the Store, the Factory, and it works.
Still, I’d love to see an example of a simple string list implementation from a simple array of strings.

I think GTK4 for Graphical User Interfaces is exactly that. And when you know some Python, it should be not difficult to port to Python. I can not do that currently. Initially I was thinking about making additional a C and Python version of the book, but now I am not sure if I will ever finish the Nim one. There are just too few possible customers. DND is easy in GTK4, see the examples in my book.

You may also look at Tutorials - GNOME Developer Documentation, I think that is new, and has Python example code. Or for inspiration, consult the Rust GTK book.

1 Like

I think GTK4 for Graphical User Interfaces is exactly that.

You are right. I completely overlooked it, I guess I was looking for complexity :wink: I think I’m going to buy your book ; Do you have a Liberapay, Ko-fi, or that sort of page?

The GTK4 book is still in an very early stage, maybe 35% complete, and I have really now hope that I will ever be able to sell it, even with C and Python versions. My other Nim Programming book is 85 % complete, but again, chances to being able to sell it is not good. Advertising costs, and salaries for a final professional proofreader would fully eat all profits. Books are at GitHub, and the Nim book has its own page already:

GitHub - StefanSalewski/NimProgrammingBook: Computer Programming with the Nim Programming Language -- A gentle Introduction

GitHub - StefanSalewski/GtkProgrammingBook: GTK GUI creation with the Nim programming language

https://nimprogrammingbook.com/

2 Likes

It’s not incredibly complex, but it’s different than the existing TreeView/ComboBox API, as it incorporates the lessons learned over nearly 20 years of development.

The mechanism is the same as TreeView/TreeModel: use the model-view-controller design pattern to split the data model from the view (the controller, in this case, is subsumed by the model object).

For list widgets, the idea is taken further:

  • the widgets that represent the data are created on demand until the data set gets arbitrarily large
  • the widgets are “recycled”: their contents are updated when the slice of visible rows in the model moves

This means that you need an additional object that is responsible for creating and updating row widgets—the GtkListItemFactory.

I have two simple examples as snippets on GitLab:

The former shows a list of rows with a single column; the latter shows two columns and multiple rows.

In general, though, you do not need columns unless you’re presenting some large tabular dataset. If you’re just presenting a list of things you can use a GtkListView. GtkTreeView only had tabular lists, and decomposed the model into unrelated columns that had to map to cells; the list view widgets are mapped to GListModel implementations, which means lists of objects. Since objects can have multiple properties, you can map each single object to a composite widget that represents a row.

GtkStringList is mostly a C convenience object, because C does not have string objects, and GListModel can only deal with GObject instances. The examples I linked above use a simple Python class that has a string property, which is essentially what GtkStringObject is; it’s easier to do this in Python than it is in C, and you don’t need an ad hoc model for it.

2 Likes

Ok, here is where I am right now: I used this example and the one in your book (ListView2.nim - “The builder way”) and Emmanuele’s explanations (and a lot of other docs as well, it’s been a busy weekend) to learn about the whole list widgets factory concept.

And now I do have a list view, but it’s just a list of placeholders, somehow the item names get lost on the factory floor:

class ModelObject(GObject.GObject):
    def __init__(self, name):
        super().__init__()
        self._name = name

    @GObject.Property(type=str)
    def name(self):
        return self._name       # <= This never fires, why?

@Gtk.Template(resource_path='/org/yphil/matinee/window.ui')
class MatineeWindow(Adw.ApplicationWindow):
    __gtype_name__ = 'MatineeWindow'

    video = Gtk.Template.Child()
    scrolledWindow = Gtk.Template.Child()
    openDialog = Gtk.Template.Child()
    aboutDialog = Gtk.Template.Child()
    model = Gio.ListStore.new(ModelObject)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.model.append(ModelObject("one"))
        self.model.append(ModelObject("two"))
        self.model.append(ModelObject("three"))

        self.selection = Gtk.SingleSelection.new(self.model)
        self.factory = Gtk.BuilderListItemFactory.new_from_resource(None, '/org/yphil/matinee/listitem.ui')

        listView = Gtk.ListView.new(self.selection, self.factory)
        self.scrolledWindow.set_child(listView)

And here is my UI definition:

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <template class="GtkListItem">
    <property name="child">
      <object class="GtkLabel">
        <property name="xalign">0</property>
        <binding name="label">
          <lookup name="string" type="GtkStringObject">
            <lookup name="item">GtkListItem</lookup>
          </lookup>
        </binding>
      </object>
    </property>
  </template>
</interface>

This produces a 3 rows list of empty labels.

It feels really close now, I just need to understand where my names go… I know that this GtkStringObject type does not represent a real string, more like “wrapping” it… But how can I really manipulate it is still a bit beyond me. Help?

Unfortunately I do not know enough about Python to really help you. My feeling is, that the UI file, which we took from the blog of Mr. Clasen does work well with the StringObject provided by GTK, but not with your class ModelObject. You ModelObject is a GObject subclass with a name. But Gtk.StringObject has a “string” property, that is a property with name string and a getString() method. Is the GtkStringObject not available for Python?

Actually, I am surprised how difficult it is to use the GTK Python bindings. Using the gintro Nim bindings is indeed much easier, as they follow close the C code shape. Unfortunately the gintro Nim bindings are missing some functionality, e.g. we can currently not create new real GObject subclasses with new properties.

1 Like

Actually, I am surprised how difficult it is to use the GTK Python bindings

I see what you mean, and indeed your Nim code is surprisingly clear and concise (although in your examples I fail to see how I would access the list model to add individual items to it) but yeah, this list model in Python (even the C examples are somewhat clearer) is a bit of a back pain.

And when I get it to work, I still got to find a way to split my list view into 3 models : the list of URIs that have been played, the URI item being played, and the list of URIs to play. Lots of fun to be had :expressionless:

Easy. Function newStringList() gives us a GtkStringLIst, for which we have Gtk.StringList.append available, so we can add more strings.

  let sl = newStringList("zero", "one", "two", "three", "four", "five", "six", "seven")
  sl.append("eight")

But indeed your remark is good, I am not sure if creating an empty string list with let sl = newStringList() works. Would be easy to fix in the bindings, if we need it.

1 Like

Thanks to your help I now have a working ListView :slight_smile:

1 Like

It feels really close now, I just need to understand where my names go…

I have a similar problem using the UI xml with ColumnView and the factory to build out the row from a GObject (similar to yours) but with multiple properties instead of your single “name”.

@yPhil what did you discover was the reason your labels were empty?

I had forgotten the type of my custom class:

          <lookup name="name" type="ModelObject">
            <lookup name="item">GtkListItem</lookup>
          </lookup>

It works now :slight_smile: just a few things left to set up (drag & drop, specifically) and then I’ll post a full example somewhere, for documentation’s sake.