使用Python编写的GtkTreeView多列和单自定义类型的GtkListStore

7

我正在尝试在不同的列上显示对象的属性 Gtk.TreeView。假设我有以下内容:

class MyClass(GObject.GObject):
    def __init__(self, first, last, age):
        self.first = first
        self.last = last
        self.age = age

我希望将实例存储在 Gtk.ListStore 中,如下所示。

store = Gtk.ListStore(MyClass)

现在,在创建Gtk.TreeView时,我不知道如何指定必须呈现2列,一列用于first属性,另一列用于age属性。
view = Gtk.TreeView(model=store)
# Columns for first and age added here
...

这些帖子(1)(2)有些解释如何使用自定义类型,但仅使用1列(然后store中的列数与view中的列数相匹配)。听起来我正在尝试的事情是一件常见的事情,不需要任何解决方法。也许这与子类化Gtk.ListStore有关,使其告诉view它有几列以及如何获取每个值?
此外,我如何使在store中的MyClass实例的更改自动通知并反映在view上?

请注意,您的构造函数应使用 self.first = "John" 等。当前版本是无操作的。 - user4815162342
我的早期评论没有用(因此删除了) - 这里有很多问题,但我想它们都适合标题。编写自己的TreeModel是可能的,但并不容易(我认为以通用方式将GObject与存储匹配并不像您想象的那么容易)。有一些例子,但不幸的是,许多Python示例的质量非常低...我的示例在这里:https://github.com/01org/dleyna-control/blob/master/src/msd/msd_generic_model.py -- 那里有很多你不需要的东西,但看一下do_*()函数:它们实现了TreeModel。您将不得不实现这些功能。 - Jussi Kukkonen
@user4815162342 已更新。感谢。 - jpcgt
1
@jku OP可能不需要一个完整的自定义模型 - 一个简单的ListStore就可以满足他的需求,只要有一种方法从其中的对象中提取任意数据即可。奇怪命名的set_cell_data_func正是为这个目的而有用的,而ListStore的gobject.TYPE_PYOBJECT可以用来存储任意的Python实例。将两者结合起来可以相当优雅地解决问题。 - user4815162342
@user4815162342 你说得有道理。我假设这并不能满足自动反映对象属性更改在视图上的(暗示的)要求? - Jussi Kukkonen
@jku 你说得对,确实没有。我想我可能错过了最后一段,那里详细说明了这个要求。但是,如果数据更改相对不频繁,实现这个要求就非常简单 - 一个通知机制,连接到执行 tree_view.queue_draw() 函数的方法即可解决问题。一个更智能的机制,只重新绘制树的一部分(或者如果没有可见变化,则不重新绘制任何内容)确实需要自定义 TreeModel 实现。 - user4815162342
1个回答

7
你需要做两件事情:首先,使用单个TYPE_PYOBJECT列设置一个ListStore,然后在树视图列上使用set_cell_data_func来从相应的对象属性中设置单元格文本。
以下是一个示例,演示了如何实现这一点:
from gi.repository import Gtk, GObject

class MyClass(object):
    def __init__(self, first, last, age):
        self.first = first
        self.last = last
        self.age = age

tree_store = Gtk.ListStore(GObject.TYPE_PYOBJECT)
tree_store.append([MyClass("foo", "bar", 15)])
tree_store.append([MyClass("baz", "qux", 100)])

def make_column(caption, getter):
    rend = Gtk.CellRendererText()
    col = Gtk.TreeViewColumn(caption, rend)
    def _set_cell_text(column, cell, model, it, ignored):
        obj = model.get_value(it, 0)
        cell.set_property('text', getter(obj))
    col.set_cell_data_func(rend, _set_cell_text)
    return col

view = Gtk.TreeView(tree_store)
view.append_column(make_column("First", lambda obj: obj.first))
view.append_column(make_column("Last", lambda obj: obj.last))
view.append_column(make_column("Age", lambda obj: '%d' % obj.age))

w = Gtk.Window()
w.add(view)
w.show_all()
Gtk.main()

1
这很有前途。我在 Gtk3 上几乎照着你的示例做到了,除了 _set_cell_text() 多接收了一个 data 参数。不过我还是遇到了 3 个错误/警告:Warning: g_object_set_property: assertion 'G_IS_VALUE (value)' failedWarning: g_value_unset: assertion 'G_IS_VALUE (value)' failedgtk_list_store_get_value: assertion 'column < priv->n_columns' failed。最后一个错误在与视图交互时经常出现。 - jpcgt
1
@jpcgt 我现在已经使用GTK3测试了代码(并相应地更新了答案),但是我在我的机器上使用GTK 3.10.6时没有看到任何警告。当您运行此最小示例或较大的程序时,您是否会收到警告? - user4815162342
顺便说一下,我做事情有点不同:class MyClass(GObject.GObject, object): 然后 tree_store = Gtk.ListStore(MyClass)。这在我的程序中可以工作,但由于某种原因,这个更改会破坏你的示例。它抱怨 lambda 函数得到了 None:view.append_column(make_column("First", lambda obj: obj.first)) \n AttributeError: 'NoneType' object has no attribute 'first' - jpcgt
如果您从GObject继承并使用ListStore(MyClass),则需要定义MyClass的gobject可见“属性”并将它们挂钩到树视图列中。这比您的用例要复杂,这就是为什么答案使用TYPE_PYOBJECT列表存储的原因。这样,您可以将任何Python对象放入存储中,而无需定义gobject属性。除此之外,使用set_cell_data_func使得可以从对象中获取任意Python属性。您能否挽救您的示例,还是转而使用答案提供的方案? - user4815162342
@jpcgt 如果我理解正确的话,它在引入set_cell_data_func之前是有效的。另一方面,如果您没有在对象上定义gobject属性或发出信号等,则从GObject继承似乎实际上并没有为您带来任何好处。 - user4815162342
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接