向gtk TreeStore(Treeview)添加不同长度的列

2024-09-28 01:30:06 发布

您现在位置:Python中文网/ 问答频道 /正文

我想使用gtk Treeview显示两个层次的数据(使用gtk Treestore模型)

数据格式如下:

**First(parent)** level
col_a, col_b, col_c, col_d, col_e
val_a, val_b, val_c, val_d, val_e

**Second(child)** level
col_x, col_y, col_z
val_x, val_y, val_z

数据层次如下:

^{pr2}$

下面的pygtk代码是我尝试过的(修改了gtk教程中的代码)

import pygtk
pygtk.require('2.0')
import gtk

data = [
    [('val_a1', 'val_b1', 'val_c1', 'val_d1', 'val_e1'), ('val_x1', 'val_y1', 'val_z1'), ('val_x2', 'val_y2', 'val_z2')],
    [('val_a2', 'val_b2', 'val_c2', 'val_d2', 'val_e2'), ('val_x3', 'val_y3', 'val_z3')],
    [('val_a3', 'val_b3', 'val_c3', 'val_d3', 'val_e3')],
    [('val_a4', 'val_b4', 'val_c4', 'val_d4', 'val_e4'), ('val_x4', 'val_y4', 'val_z4'), ('val_x5', 'val_y5', 'val_z5')],
]

class BasicTreeViewExample:

    def delete_event(self, widget, event, data=None):
        gtk.main_quit()
        return False

    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_title("Basic TreeView Example")
        self.window.set_size_request(200, 200)
        self.window.connect("delete_event", self.delete_event)
        self.treestore = gtk.TreeStore(str, str, str, str, str)
        for detail in data:
        for index, elem in enumerate(detail):
            if index == 0:
                piter = self.treestore.append(None, elem)
            else:
                self.treestore.append(piter, elem)

        self.treeview = gtk.TreeView(self.treestore)
        for i in range(5):
            tvcolumn = gtk.TreeViewColumn('Column %s' % (i))
            self.treeview.append_column(tvcolumn)
            cell = gtk.CellRendererText()
            tvcolumn.pack_start(cell, True)
            tvcolumn.add_attribute(cell, 'text', i)
        self.window.add(self.treeview)
        self.window.show_all()

def main():
    gtk.main()

if __name__ == "__main__":
    tvexample = BasicTreeViewExample()
    main()

但是,我在尝试运行上述代码时遇到以下错误:

Traceback (most recent call last):
  File "test.py", line 55, in <module>
    tvexample = BasicTreeViewExample()
  File "test.py", line 33, in __init__
    self.treestore.append(piter, detail[index])
ValueError: row sequence has wrong length

所以我的问题是:

  1. 如何在不同层次结构中使用不同列数向gtk TreeStore添加数据
  2. 另外,是否可以在gtk treestore中显示每行的列名

即在Treeview中,我希望看到如下输出:

  col_a,  col_b,  col_c,  col_d,  col_e
> val_a1, val_b1, val_c1, val_d1, val_e1
       col_x,  col_y,  col_z
       val_x1, val_y1, val_z1

       col_x,  col_y,  col_z
       val_x2, val_y2, val_z2

  col_a,  col_b,  col_c,  col_d,  col_e
> val_a2, val_b2, val_c2, val_s2, val_e2
       col_x,  col_y,  col_z
       val_x3, val_y3, val_z3

  col_a,  col_b,  col_c,  col_d,  col_e
> val_a3, val_b3, val_c3, val_d3, val_e3

  col_a,  col_b,  col_c,  col_d,  col_e
> val_a4, val_b4, val_c4, val_d4, val_e4
       col_x, col_y, col_z
       val_x4, val_y4, val_z4

       col_x, col_y, col_z
       val_x5, val_y5, val_z5

如果使用上面的任何方法都不可行,那么使用哪种方法可以实现?在


Tags: 数据代码inselfeventgtkmaincol
1条回答
网友
1楼 · 发布于 2024-09-28 01:30:06

简短回答和介绍

如何向添加数据gtk.TreeStore公司在不同层次结构中有不同数量的列?在

简单:你不能。GtkListStore和GtkTreeStore都是用来保存的 数据作为表。列是用索引和数据以固定方式定义的 类型。ListStore和TreeStore之间的唯一区别是 TreeStore,行有层次结构。更糟糕的是,GtkTreeView小部件也 期望数据以表的形式存储,因为每一行都将无条件获取 单元格使用其列索引,并期望在那里找到一些内容。 除非您编写自己的小部件,but you probably don't want to (天哪,这个文件有16570行长…)。在

但是,如果您不能编写自己的widget,您仍然可以编写自己的widget 模型。这会给你一些灵活性。在

另外,是否可以在gtk.TreeStore公司?在

在TreeView中显示数据涉及两个组件:GtkTreeView本身, 它在树状结构中获取数据并显示它们。TreeView小部件没有 具有显示每行标题的功能。但也有一些诀窍 处理模型和视图之间的数据,这可能会导致所需的结果 效果,虽然可能不是那么好。在

基础知识

所以,TreeView希望在一个数据表上工作,我们不能改变它。 好 啊。但我们仍然可以欺骗它,让它认为数据是一个表,实际上 不是。。。让我们从视图开始。我们至少需要五列来显示 父母的资料。孩子们只能使用 这五个,所以这很好。在

请注意,模型的列并不总是映射到树视图中的列。 它们实际上映射到单元渲染器的某些属性。例如,您可以 在模型中有一列定义行的背景色, 或定义要显示的图标的列。视图中的列只是一种方式 对齐单元格渲染器组,可能在标题下。但在这里,让我们 假设所有的值都是文本,它们应该进入 自己的专栏。在

父母将使用全部五列,而子女将只使用第2、3列 和4。然后,我们将欺骗模型在数据没有返回空文本时返回一个空文本 可用于目标单元格。在

创建新的TreeModel

关于在PyGTK中实现自定义GtkTreeModel的一些解释是 在this tutorial中提供。 这是一个sample implementation of it

import pygtk
pygtk.require('2.0')
import gtk

data = [
    [('val_a1', 'val_b1', 'val_c1', 'val_d1', 'val_e1'), ('val_x1', 'val_y1', 'val_z1'), ('val_x2', 'val_y2', 'val_z2')],
    [('val_a2', 'val_b2', 'val_c2', 'val_d2', 'val_e2'), ('val_x3', 'val_y3', 'val_z3')],
    [('val_a3', 'val_b3', 'val_c3', 'val_d3', 'val_e3')],
    [('val_a4', 'val_b4', 'val_c4', 'val_d4', 'val_e4'), ('val_x4', 'val_y4', 'val_z4'), ('val_x5', 'val_y5', 'val_z5')],
]

class MyTreeModel(gtk.GenericTreeModel):

    # The columns exposed by the model to the view
    column_types = (str, str, str, str, str)

    def __init__(self, data):
        gtk.GenericTreeModel.__init__(self)
        self.data = data

    def on_get_flags(self):
        """
            Get Model capabilities
        """
        return gtk.TREE_MODEL_ITERS_PERSIST

    def on_get_n_columns(self):
        """
            Get number of columns in the model
        """
        return len(self.column_types)

    def on_get_column_type(self, n):
        """
            Get data type of a specified column in the model
        """
        return self.column_types[n]

    def on_get_iter(self, path):
        """
            Obtain a reference to the row at path. For us, this is a tuple that
            contain the position of the row in the double list of data.
        """
        if len(path) > 2:
            return None # Invalid path
        parent_idx = path[0]
        if parent_idx >= len(self.data):
            return None # Invalid path
        first_level_list = self.data[parent_idx]
        if len(path) == 1:
            # Access the parent at index 0 in the first level list
            return (parent_idx, 0)
        else:
            # Access a child, at index path[1] + 1 (0 is the parent)
            child_idx = path[1] + 1
            if child_idx >= len(first_level_list):
                return None # Invalid path
            else:
                return (parent_idx, child_idx)

    def on_get_path(self, iter_):
        """
            Get a path from a rowref (this is the inverse of on_get_iter)
        """
        parent_idx, child_idx = iter_
        if child_idx == 0:
            return (parent_idx, )
        else:
            (parent_idx, child_idx-1)

    def on_get_value(self, iter_, column):
        """
            This is where the view asks for values. This is thus where we
            start mapping our data model to a fake table to present to the view
        """
        parent_idx, child_idx = iter_
        item = self.data[parent_idx][child_idx]
        # For parents, map columns 1:1 to data
        if child_idx == 0:
            return item[column]
        # For children, we have to fake some columns
        else:
            if column == 0 or column == 4:
                return "" # Fake empty text
            else:
                return item[column-1] # map 1, 2, 3 to 0, 1, 2.

    def on_iter_next(self, iter_):
        """
            Get the next sibling of the item pointed by iter_
        """
        parent_idx, child_idx = iter_
        # For parents, point to the next parent
        if child_idx == 0:
            next_parent_idx = parent_idx + 1
            if next_parent_idx < len(self.data):
                return (next_parent_idx, 0)
            else:
                return None
        # For children, get next tuple in the list
        else:
            next_child_idx = child_idx + 1
            if next_child_idx < len(self.data[parent_idx]):
                return (parent_idx, next_child_idx)
            else:
                return None

    def on_iter_has_child(self, iter_):
        """
            Tells if the row referenced by iter_ has children
        """
        parent_idx, child_idx = iter_
        if child_idx == 0 and len(self.data[parent_idx]) > 1:
            return True
        else:
            return False

    def on_iter_children(self, iter_):
        """
            Return a row reference to the first child row of the row specified
            by iter_. If iter_ is None, a reference to the first top level row
            is returned. If there is no child row None is returned.
        """
        if iter_ is None:
            return (0, 0)
        parent_idx, child_idx = iter_
        if self.on_iter_has_child(iter_):
            return (parent_idx, 1)
        else:
            return None

    def on_iter_n_children(self, iter_):
        """
            Return the number of child rows that the row specified by iter_
            has. If iter_ is None, the number of top level rows is returned.
        """
        if iter_ is None:
            return len(self.data)
        else:
            parent_idx, child_idx = iter_
            if child_idx == 0:
                return len(self.data[parent_idx]) - 1
            else:
                return 0

    def on_iter_nth_child(self, iter_, n):
        """
            Return a row reference to the nth child row of the row specified by
            iter_. If iter_ is None, a reference to the nth top level row is
            returned.
        """
        if iter_ is None:
            if n < len(self.data):
                return (n, 0)
            else:
                return None
        else:
            parent_idx, child_idx = iter_
            if child_idx == 0:
                if n+1 < len(self.data[parent_idx]):
                    return (parent_idx, n+1)
                else:
                    return None
            else:
                return None

    def on_iter_parent(self, iter_):
        """
            Get a reference to the parent
        """
        parent_idx, child_idx = iter_
        if child_idx == 0:
            return None
        else:
            return (parent_idx, 0)

class BasicTreeViewExample:

    def delete_event(self, widget, event, data=None):
        gtk.main_quit()
        return False

    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_title("Basic TreeView Example")
        self.window.set_size_request(200, 200)
        self.window.connect("delete_event", self.delete_event)
        # Create the model with data in it
        self.model = MyTreeModel(data)
        self.treeview = gtk.TreeView(self.model)
        for i in range(5):
            tvcolumn = gtk.TreeViewColumn('Column %s' % (i))
            self.treeview.append_column(tvcolumn)
            cell = gtk.CellRendererText()
            tvcolumn.pack_start(cell, True)
            tvcolumn.add_attribute(cell, 'text', i)
        self.window.add(self.treeview)
        self.window.show_all()

def main():
    gtk.main()

if __name__ == "__main__":
    tvexample = BasicTreeViewExample()
    main()

结果是:

Custom GenericTreeModel

在单元格中伪造标题

现在让我们使用模型在每个单元格中添加某种类型的标题 所需的数据。Full code is here。在

^{pr2}$

结果是:

{2美元^

使用set_cell_data_func或{}

如果您设法将数据放入ListStore或TreeStore中,则 就是说你找到一个诀窍,让父母和孩子分享相同的金额 和列类型,则可以使用 GtkTreeCellDataFunc 或者是GtkTreeModelFilter。在

PyGTK文档提供了Cell Data Functions的示例 和Tree Model Filters。在

例如,使用这些概念添加列标题可能比 创建完全自定义模型。在

这是code using TreeCellDataFunc。 请注意数据输入是如何格式化的,以便子级和父级 相同数量的数据。这是能够使用GtkTreeStore的条件。在

import pygtk
pygtk.require('2.0')
import gtk

data = [
    [('val_a1', 'val_b1', 'val_c1', 'val_d1', 'val_e1'), ('', 'val_x1', 'val_y1', 'val_z1', ''), ('', 'val_x2', 'val_y2', 'val_z2', '')],
    [('val_a2', 'val_b2', 'val_c2', 'val_d2', 'val_e2'), ('', 'val_x3', 'val_y3', 'val_z3', '')],
    [('val_a3', 'val_b3', 'val_c3', 'val_d3', 'val_e3')],
    [('val_a4', 'val_b4', 'val_c4', 'val_d4', 'val_e4'), ('', 'val_x4', 'val_y4', 'val_z4', ''), ('', 'val_x5', 'val_y5', 'val_z5', '')],
]

class BasicTreeViewExample:

    parent_headers = ("P.Col 1", "P.Col 2", "P.Col 3", "P.Col 4", "P.Col 5")
    child_headers = ("C.Col 1", "C.Col 2", "C.Col 3")

    def delete_event(self, widget, event, data=None):
        gtk.main_quit()
        return False

    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_title("Basic TreeView Example")
        self.window.set_size_request(200, 200)
        self.window.connect("delete_event", self.delete_event)
        self.treestore = gtk.TreeStore(str, str, str, str, str)
        for detail in data:
            for index, elem in enumerate(detail):
                if index == 0:
                    piter = self.treestore.append(None, elem)
                else:
                    self.treestore.append(piter, elem)

        self.treeview = gtk.TreeView(self.treestore)
        for i in range(5):
            tvcolumn = gtk.TreeViewColumn('Column %s' % (i))
            self.treeview.append_column(tvcolumn)
            cell = gtk.CellRendererText()
            tvcolumn.pack_start(cell, True)
            # Delegate data fetching to callback
            tvcolumn.set_cell_data_func(cell, self.cell_add_header, i)
        self.window.add(self.treeview)
        self.window.show_all()

    def cell_add_header(self, treeviewcolumn, cell, model, iter_, column):
        text = model.get_value(iter_, column)
        if model.iter_parent(iter_) is None:
            # This is a parent
            title = self.parent_headers[column]
            markup = "<b>%s</b>\n%s"%(title, text)
        else:
            # We have a child
            if column == 0 or column == 4:
                # Cell is not used by child, leave it empty
                markup = ""
            else:
                title = self.child_headers[column-1]
                markup = "<b>%s</b>\n%s"%(title, text)
        cell.set_property('markup', markup)

def main():
    gtk.main()

if __name__ == "__main__":
    tvexample = BasicTreeViewExample()
    main()

gtktreemodelpilter导致了几乎相同的结果。结果是一样的 而不是在单元格中伪造标题(除了我忘记将标题设置为不可见):

Faking headers with CellDataFunc

我希望这对你有帮助,其他人也会有同样的问题!在

相关问题 更多 >

    热门问题