1

I need to write a custom Gtk.CellRenderer (let's call it CellRendererToogleWithNone) that behaves similar to Gtk.CellRendererToggle, but instead of only supporting True and False I also want it to support None which should be displayed as inconsistent state like this: inconsistent state pygtk

On toggle I want to rotate in some defined order, for example like so: True -> False -> None (But that's probably the easy part, I still thought I mention that)

I also don't know how to make it work with TreeStore because if I do

self.treestore = Gtk.TreeStore.new(types = (bool,))
iter = self.treestore.append(parent = None, row = (None,))

it will convert any None value to False because bool seems not to allow for None values

I failed to find any helpful custom Gtk.CellRenderer examples online.

I want to do it by inheriting from Gtk.CellRenderer and NOT from Gtk.CellRendererToggle because this should also serve me as a helpful example to implement more cell renderers like this.

I can only guess that I have to define some custom data type for the TreeStore, something like bool_or_none instead of bool (no idea how to do that either) and hold my own Gtk.ToggleButton inside of my custom CellRendererToogleWithNone.

Edit 0:

This post about how to write custom Gtk.CellRenderer gives a couple of hints which maybe can be reused but does not solve my problem. It doesn't show how to make Gtk.TreeStore accept None values for bool variables and I don't understand everything there. It doesn't even use a Gtk.Button, instead it seems to paint a box inside of a widget that I'm guessing may be the parent. I don't want to paint my own Toggle, I want to reuse Gtk.ToggleButton and its inconsistent property

Edit 1:

Since it seems custom cell renderers are not easy, I think it would be especially useful to see a minimal working example in python. I should also mention that I want to display the data as compactly as possible which excludes suggested workarounds such as having two toggles for one value.

evolution
  • 593
  • 6
  • 20
  • Does this answer your question? [How to write custom Gtk.CellRenderer in python and GTK 3?](https://stackoverflow.com/questions/14998298/how-to-write-custom-gtk-cellrenderer-in-python-and-gtk-3) – stovfl Dec 30 '19 at 00:56
  • 1
    1) You can store your values in a GVariant or create a custom class with 3 possible values, after that you can create a column with type `object`. I'm not sure whether it must be a GObject subclass. 2) Writing custom CellRenderers is not an easy thing as they are not traditonal widgets, as you have to render everything manually. Take a look at ['CellRendererToggle source code`](https://gitlab.gnome.org/GNOME/gtk/blob/master/gtk/gtkcellrenderertoggle.c) for hints. – Alexander Dmitriev Dec 30 '19 at 11:17
  • 1
    3) Another approach would be using `GtkCellRendererToggle` and 2 columns: one for `"active"` property and one for `"inconsistent"` – Alexander Dmitriev Dec 30 '19 at 11:18
  • @Alexander Thanks for the GVariant hint. That's exactly why I'm asking here. I doesn't seem very easy to implement cell renderers so I was hoping someone would have experience with them. About your second comment, one of my goals is to display data as compact as possible, so doubling the amount of toggles isn't really a possibility for me. – evolution Dec 30 '19 at 15:41

1 Answers1

3

Question: Custom Gtk.CellRendererToggle with a Toggle cycling between True, None (displayed as inconsistent) and False.

You are misguided, it's not the part of a Gtk.CellRendererToggle object to set the inconsistant flag. It's implemented allready there. You have to implement a Gtk.TreeCellDataFunc instead.

I also want it to support None

I don't get it to work with a None value,
therefore i use type int with values: [False, True, 2]

Note:

  • In def on_toggle(...) the model values get changed to 0 == False and 1 == True. If you want it to be stay of type boolean, implement accordingly.
  • You should be aware, that the value 2, evaluates to True also.

App


Reference:

  • Gtk.TreeViewColumn.set_cell_data_func

    This function is used instead of the standard attributes mapping for setting the column value, and should set the value of self’s cell renderer as appropriate.

  • Gtk.TreeCellDataFunc

    A function to set the properties of a cell instead of just using the straight mapping between the cell and the model. This is useful for customizing the cell renderer


Implementation: Core point: .set_cell_data_func(cell_renderer, .set_status)

class TreeViewColumnTriState(Gtk.TreeViewColumn):

    def __init__(self, title='', model=None, **attributes):
        cell_renderer = Gtk.CellRendererToggle()
        cell_renderer.connect("toggled", self.on_toggle, model)

        self.inconsistent = attributes.pop('inconsistent', None)
        super().__init__(title, cell_renderer, active=0, **attributes)
        self.set_cell_data_func(cell_renderer, self.set_status)

    def set_status(self, column, cell, model, _iter, *ignore):    
        if model.get_value(_iter, 0) == self.inconsistent:
            cell.set_property('inconsistent', True)
        else:
            cell.set_property('inconsistent', False)

    def on_toggle(self, cell, path, model, *ignore):
        if path is not None:
            model = model[model.get_iter(path)]    
            model[0] += 1
            if model[0] == 3:
                model[0] = 0

Usage:

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib


class App(Gtk.ApplicationWindow):
    def __init__(self):
        super().__init__()
        self.connect("destroy", Gtk.main_quit)

        model = Gtk.ListStore(int)

        #  Some initial data
        for n in [False, True, 2]:
            model.append([n])

        col = TreeViewColumnTriState("Foo", model, inconsistent=2)

        tv = Gtk.TreeView(model)
        tv.append_column(col)
        self.add(tv)

        self.show_all()


if __name__ == "__main__":
    main = App()
    Gtk.main()

Tested with Python: 3.5 - gi.__version__: 3.22.0

stovfl
  • 14,998
  • 7
  • 24
  • 51
  • 1
    After looking closer and playing around with the code I accepted your answer. You are right, I misunderstood a few things. I don't need to implement a CellRenderer and I can use CellRendererToggle the way you did in your example. Implementing CellRenderer would mean that I would need to draw my own toggle which contradicts my desire to reuse the existing Toggle. My TreeStore still doesn't accept None values but I feel like I should make that a separate question at this point. For now instead taking int, I sticked with bool, but did some trickery so I wouldn't need to rely on TreeStore on None. – evolution Dec 31 '19 at 03:52