将CellRendererToggle的单行设置为不一致

5 投票
2 回答
3015 浏览
提问于 2025-04-17 15:10

在Gtk+3中,我使用一个TreeModel来存储嵌套的信息,并用CellRendererTextCellRendererToggle来显示这些信息。用户可以点击每个切换按钮,当有嵌套的按钮状态不一致时,我希望上层的按钮能显示出“状态不一致”。我该如何为一个元素设置这个属性呢?

为了更清楚地说明,我想实现的效果可以参考Transmission这个BitTorrent客户端的界面:在这里输入图片描述

我知道如何通过myCellRendererToggle.set_properties(inconsistent=True)将所有的按钮设置为不一致状态,但似乎我无法从这里访问单个元素;

我知道如何访问我的TreeStore模型中的特定行,但我只能设置“真”和“假”的值。

我的代码和官方文档非常接近,所以你可以帮我看看:https://python-gtk-3-tutorial.readthedocs.org/en/latest/cellrenderers.html#cellrenderertoggle(我使用的是treeStore而不是listStore)

这是我的代码:

class HelloMyApp:

def __init__(self):

    # Set the Glade file
    self.builder = Gtk.Builder()
    self.builder.add_from_file(GLADEFILE)


    dic = {
        "on_button1_clicked" : self.btnValidate_clicked,
        "on_MainWindow_destroy" : self.quit,
        "on_window1_delete_event" : self.quit,

        }

    self.builder.connect_signals(dic)

    window = self.builder.get_object("window1")

    treeview1 = self.builder.get_object("treeview1")

    ######## This is my model : it stores a string and a boolean. #########
    self.treeModel = Gtk.TreeStore(str, bool)

    # Example on how to insert data in the model
    treeIter = self.treeModel.append(None, ['example one', True])
    self.treeModel.append(treeIter, [' simple elt', True])
    treeIter = self.treeModel.append(treeIter, ['example two', False])
    self.treeModel.append(treeIter, ['under example two', True])


    select = treeview1.get_selection()
    select.set_mode(Gtk.SelectionMode.BROWSE)

    select.connect("changed", self.on_tree_selection_changed, buf) 

    # Using one column of text and another column with the toggle buttons
    renderer = Gtk.CellRendererText()
    column = Gtk.TreeViewColumn("Title", renderer, text=0)
    treeview1.append_column(column)

    ######  Here is the CellRendereToggle  ################
    renderer_toggle = Gtk.CellRendererToggle()
    renderer_toggle.connect("toggled", self.on_cell_toggled)
    column_toggle = Gtk.TreeViewColumn("Installer", renderer_toggle, active=1)
    treeview1.append_column(column_toggle)

    treeview1.set_model(self.treeModel)


    window.show_all()


if __name__ == "__main__":

    HelloMyApp = HelloMyApp()
    Gtk.main()

谢谢!

编辑:为了回答Marcus:我搞不清楚,当我在函数中改变CellRendererToggle的属性时,它会改变每一行。

编辑以提供解决方案:正如Marcu指出的,我们必须在每种情况下都设置属性,这就是我添加else部分的原因。

def cellRenderer_func(column, cellRenderer, treeModel, treeIter, userData):
    if 'cat' in treeModel.get_value(treeIter, 0): 
        # it happens only ones in my model, 
        # so here I am in a row I want to change to inconsistent.
        cellRenderer.set_property('inconsistent',True) 
        # I was expecting that changes the box of that row but it affects every row. 

    else: 
        cellRenderer.set_property('inconsistent', False) # and that's ok now.

我得再试一次。 我在想……我们真的需要手动去做吗?难道这不是treeView的一个现成功能吗?

2 个回答

1

我最近也遇到了同样的问题,发现如果你想直接从模型中的某一列设置属性,就不需要使用单元格数据函数。要让代码按照问题中的描述工作,需要做的更新如下:

self.treeModel = Gtk.TreeStore(str, bool, bool)
...
column_toggle = Gtk.TreeViewColumn("Installer", renderer_toggle, active=1, inconsistent=2)

然后根据需要在新列中设置不一致的值。

关于何时选择使用单元格数据函数或在模型中添加新列,可以考虑以下几点(Gtk+ 示例):

每次渲染该(渲染器)列中的单元格时,都会调用你的单元格数据函数。如果你在程序中使用了这个函数,去检查一下它被调用的频率。如果在单元格数据函数中进行耗时的操作,事情就不会很快,尤其是当你有很多行数据的时候。

4

你可以通过一个单元格数据函数来设置某个元素的属性。因为我只在C语言中做过这件事,所以只能给你提供PyGTK的文档链接,至于PyGObject的相关文档我还没看到过。

关于PyGTK的文档可以在这里找到,这个页面底部有个示例,另外在这个文档中也有关于PyGTK使用单元格数据函数的介绍。

举个例子,如果你想做和Transmission一样的事情,你可以这样做:因为你在单元格数据函数中会收到当前的迭代器作为参数,你可以在这里循环遍历它的所有子元素,检查这些子元素的状态。这样你就能知道应该在父节点上设置什么状态。关键是,在单元格数据函数中设置属性只会影响这个单独的单元格,而不会影响整个树视图中的所有元素。

我还可以给你一个我自己应用的视觉示例:

enter image description here

在这个树视图中,我有一个“值”列。如果“菜单元素”列的值被设置为“启用”,而“类型”列的值是“选项”,那么就会显示一个复选框而不是文本(我已经高亮了这样的行)。示例图片还展示了一个活跃的搜索,突出显示了搜索结果。这两者都是通过单元格数据函数来设置某个元素的属性,正如你在问题中所询问的那样。

编辑

我写了一些示例代码。关键是单元格属性总是为每个单元格设置的。所以不是简单的“如果...,那么设置属性”,而是“如果,像这样设置属性,否则,像那样设置属性”。(这基于旧的PyGTK文档,但应该能帮助你理解)。

#!/usr/bin/env python

# example basictreeview.py

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

class BasicTreeViewExample:

    def set_status(self, column, cell, model, iter):
        if 'inconsistent' in model.get_value(iter, 0):
            cell.set_property('inconsistent',True) 
        else:
            cell.set_property('inconsistent',False)
        return

    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)

        for parent in range(4):
            piter = self.treestore.append(None, ['parent %i' % parent])
            for child in range(3):
                if child == 1:
                    self.treestore.append(piter, ['consistent'])
                else:
                    self.treestore.append(piter, ['inconsistent'])

        self.treeview = gtk.TreeView(self.treestore)

        self.tvcolumn0 = gtk.TreeViewColumn('Column 0')
        self.tvcolumn1 = gtk.TreeViewColumn('Column 1')

        self.treeview.append_column(self.tvcolumn0)
        self.treeview.append_column(self.tvcolumn1)

        self.text = gtk.CellRendererText()
        self.toggle = gtk.CellRendererToggle()

        self.tvcolumn0.pack_start(self.text, True)
        self.tvcolumn1.pack_start(self.toggle, True)

        self.tvcolumn0.add_attribute(self.text, 'text', 0)
        self.tvcolumn1.set_cell_data_func(self.toggle, self.set_status)

        self.window.add(self.treeview)

        self.window.show_all()

def main():
    gtk.main()

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

撰写回答