buhtz
buhtz

Reputation: 12152

Make only some rows bold in a Gtk.TreeView

I want to display the status read or unread of a row by makeing the text (of a column in the row) bold or not.

The status of the row (or the data in the model) can be part of the model or somewhere else. I am flexible at this point.

How can I make the text of a specific cell bold depending on the underlying (dynamic) data, which is in this example the value 1?

#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class View(Gtk.TreeView):
    def __init__(self, model):
        Gtk.TreeView.__init__(self, model)
        col = Gtk.TreeViewColumn('Text',
                                 Gtk.CellRendererText(),
                                 text=0)
        self.append_column(col)
        # bold text
        # Gtk.CellRendererText(weight_set=True, weight=700),


class Model(Gtk.ListStore):
    def __init__(self):
        Gtk.ListStore.__init__(self, str, int)
        self.append(['foo 1', 0])
        self.append(['foo 2', 1])  # bold
        self.append(['foo 3', 0])
        self.append(['foo 4', 0])
        self.append(['foo 5', 1])  # bold

class Window(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        self.set_default_size(100, 200)

        # model & view
        self.model = Model()
        self.view = View(self.model)

        # layout
        self.layout = Gtk.Grid()
        self.add(self.layout)
        self.layout.attach(self.view, 0, 0, 1, 2)

        self.connect('destroy', Gtk.main_quit)
        self.show_all()

if __name__ == '__main__':
    win = Window()
    Gtk.main()

Upvotes: 3

Views: 1133

Answers (2)

Azsgy
Azsgy

Reputation: 3307

After a short discussion with the Gtk maintainers, I've found that there is another, cleaner way to do this. Since both answers work, I've decided to put this as separate answer.

The problem with the other answer is that the cell_data_func gets called for every row. Over time, this will get really slow if you have many rows.

The solution here is to add another column to your model that you then bind to the relevant attribute(s).

Currently, you do the following:

col = Gtk.TreeViewColumn(
            'Text', renderer, text=0, weight_set=True)

This binds the text property of the CellRenderer to column 0 of your model.

Now, we can bind this to any other property too. For example, bind column 1 to the weight property:

col = Gtk.TreeViewColumn(
            'Text', renderer, text=0, weight=1, weight_set=True)

You can then set this column to different Pango.Weight values to change the weight of the text.

class Model(Gtk.ListStore):
    def __init__(self):
        ...
        self.append(['foo 1', Pango.Weight.BOLD])
        self.append(['foo 2', Pango.Weight.BOOK])
        ...

If you want to set additional properties, you can also set the markup property (which parses the string as pango markup and allows you to change the font, color, etc of some parts of the text) and the attributes property, which you can use to set many style attributes at once with the pango.AttrList type.

Upvotes: 4

Azsgy
Azsgy

Reputation: 3307

As is often in Gtk, this is very very simple, just not very obvious. You really need to know where to look and read the related docs carefully.

class View(Gtk.TreeView):
    def __init__(self, model):
        Gtk.TreeView.__init__(self, model)
        renderer = Gtk.CellRendererText()
        col = Gtk.TreeViewColumn(
            'Text', renderer, text=0, weight_set=True)
        col.set_cell_data_func(renderer, datafunc)
        self.append_column(col)

The first key is the TreeViewColumn.set_cell_data_func method. This allows you to set an intercept function that you can use to modify the properties of the cell before it is rendered. Here's an example one I wrote which does what you want:

def datafunc(col, renderer, model, titer, data):
    val = model.get_value(titer, 1)

    if val:
        renderer.set_property("weight", 700)

As you can see, it receives the TreeViewColumn, CellRenderer, TreeModel and TreeIter involved, as well as custom data, which I've ommited.

The first step is to get the values of the current column. For that, we give the model the treeiter (which saves the "current row", kinda), and the column id we want (in this case the second column, 1).

The rest is pretty simple, we use that value to decide if we need to set the "weight" property on the CellRenderer.

You used 700 here, but I'd recommend you to use Pango.Weight.BOLD instead for clarity. This requires from gi.repository import Pango of course.

Upvotes: 5

Related Questions