将CellRendererToggle的单行设置为不一致
在Gtk+3中,我使用一个TreeModel
来存储嵌套的信息,并用CellRendererText
和CellRendererToggle
来显示这些信息。用户可以点击每个切换按钮,当有嵌套的按钮状态不一致时,我希望上层的按钮能显示出“状态不一致”。我该如何为一个元素设置这个属性呢?
为了更清楚地说明,我想实现的效果可以参考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 个回答
我最近也遇到了同样的问题,发现如果你想直接从模型中的某一列设置属性,就不需要使用单元格数据函数。要让代码按照问题中的描述工作,需要做的更新如下:
self.treeModel = Gtk.TreeStore(str, bool, bool)
...
column_toggle = Gtk.TreeViewColumn("Installer", renderer_toggle, active=1, inconsistent=2)
然后根据需要在新列中设置不一致的值。
关于何时选择使用单元格数据函数或在模型中添加新列,可以考虑以下几点(Gtk+ 示例):
每次渲染该(渲染器)列中的单元格时,都会调用你的单元格数据函数。如果你在程序中使用了这个函数,去检查一下它被调用的频率。如果在单元格数据函数中进行耗时的操作,事情就不会很快,尤其是当你有很多行数据的时候。
你可以通过一个单元格数据函数来设置某个元素的属性。因为我只在C语言中做过这件事,所以只能给你提供PyGTK的文档链接,至于PyGObject的相关文档我还没看到过。
关于PyGTK的文档可以在这里找到,这个页面底部有个示例,另外在这个文档中也有关于PyGTK使用单元格数据函数的介绍。
举个例子,如果你想做和Transmission一样的事情,你可以这样做:因为你在单元格数据函数中会收到当前的迭代器作为参数,你可以在这里循环遍历它的所有子元素,检查这些子元素的状态。这样你就能知道应该在父节点上设置什么状态。关键是,在单元格数据函数中设置属性只会影响这个单独的单元格,而不会影响整个树视图中的所有元素。
我还可以给你一个我自己应用的视觉示例:
在这个树视图中,我有一个“值”列。如果“菜单元素”列的值被设置为“启用”,而“类型”列的值是“选项”,那么就会显示一个复选框而不是文本(我已经高亮了这样的行)。示例图片还展示了一个活跃的搜索,突出显示了搜索结果。这两者都是通过单元格数据函数来设置某个元素的属性,正如你在问题中所询问的那样。
编辑
我写了一些示例代码。关键是单元格属性总是为每个单元格设置的。所以不是简单的“如果...,那么设置属性”,而是“如果,像这样设置属性,否则,像那样设置属性”。(这基于旧的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()