TreeViewColumn标头中的PyGTK Entry小部件

时间:2011-03-07 18:41:01

标签: python gtk pygtk gtktreeview gtkentry

如何在gtk.Entry标题/标题中使gtk.TreeViewColumn窗口小部件可关注或可编辑?我试过这个:

# Create tree-view.
treeview = gtk.TreeView()

#...

# Create column.
renderer = gtk.CellRendererText()
column = gtk.TreeViewColumn(None, renderer, text=0)

# Set column header.
header = gtk.VBox()

title = gtk.Label("Column")
header.pack_start(title)

filter = gtk.Entry()
#...
header.pack_start(filter)

header.show_all()
column.set_widget(header)

# Add column
treeview.append_column(column)

但是列标题中的Entry小部件不可编辑且不会聚焦。我已尝试将“可点击”设置为TrueFalse。我在Ubuntu 10.04上使用pygtk 2.21.0-0ubuntu1和libgtk 2.22.0-0ubuntu1。任何帮助将不胜感激。

编辑:

问题源于如何显示GtkTreeViewColumn标头。标题小部件位于GtkAlignment内,其父级为GtkHBox,其父级为GtkButton,其父级最终为GtkTreeViewGtkButton拦截并阻止我的GtkEntry被关注并接收鼠标输入。

3 个答案:

答案 0 :(得分:11)

为了使GtkEntryGtkTreeView标题内成为焦点,我必须:

1)找到标题GtkButton

def find_closest_ancestor(widget, ancestor_class):
    if not isinstance(widget, gtk.Widget):
        raise TypeError("%r is not a gtk.Widget" % widget)
    ancestor = widget.get_parent()
    while ancestor is not None:
        if isinstance(ancestor, ancestor_class):
            break;
        ancestor = ancestor.get_parent() if hasattr(ancestor, 'get_parent') and callable(ancestor.get_parent) else None
    return ancestor

2)将标题button-press-event中的GtkButton信号传播到GtkEntry

def propagate_button_press_event(parent, event, *data):
    parent_alloc = parent.get_allocation()
    x = parent_alloc.x + int(event.x)
    y = parent_alloc.y + int(event.y)
    children = parent.get_children()
    print "Propagating event:%r" % event
    print "- from parent:%r" % parent
    while children:
        for child in children:
            child_alloc = child.get_allocation()
            if child_alloc.x <= x <= child_alloc.x + child_alloc.width and child_alloc.y <= y <= child_alloc.y + child_alloc.height:
                print "- to child:%r" % child
                if child.get_property('can-focus'):
                    event.send_event = True
                    child.grab_focus()
                    child.emit('button-press-event', event, *data)
                    return True
                else:
                    children = child.get_children() if hasattr(child, 'get_children') and callable(child.get_children) else None
                    break;
        else:
            children = None
    return False

3)将焦点(即focus-in-event信号)从标题GtkButton传播到GtkEntry

def propagate_focus_in_event(parent, event, *data):
    print 'focus-in', parent, event
    child = parent.get_child()
    if child.get_property('can-focus'):
        child.grab_focus()
    else:
        if not child.child_focus(gtk.DIR_TAB_FORWARD):
            parent.get_toplevel().child_focus(gtk.DIR_TAB_FORWARD)
    return True

示例:

# Fix style glitches
_gtk_styles = """
    # Use the default GtkEntry style for GtkEntry widgets in treeview headers.
    widget "*.treeview-header-entry" style "entry" 
"""
gtk.rc_parse_string(_gtk_styles)

# Columns
_columns = [
    (0, "Title"),
    (1, "Description")
    # etc.
]

# Create tree-view.
items_view = gtk.TreeView(self.items_store)
items_view.show()

# Setup treeview columns.
renderer = gtk.CellRendererText()
for column in _columns:
    column_index, column_title, column_filter = column
    column_view = gtk.TreeViewColumn(None, renderer, text=column_index)
    column_view.set_clickable(True)

    column_widget = gtk.VBox()
    column_widget.show()

    column_align = gtk.Alignment(0, 0, 0, 0)
    column_align.show()
    column_widget.pack_start(column_align)
    column_label = gtk.Label(column_title)
    column_label.show()
    column_align.add(column_label)

    column_entry = gtk.Entry()
    column_entry.set_name('treeview-header-entry')
    column_entry.show()
    column_widget.pack_start(column_entry)

    column_view.set_widget(column_widget)
    items_view.append_column(column_view)

# Setup column headers.
columns = items_view.get_columns()
for column in columns:
    column_widget = column.get_widget()
    column_header = find_closest_ancestor(column_widget, gtk.Button)
    if column_header:
        column_header.connect('focus-in-event', propagate_focus_in_event)
        column_header.connect('button-press-event', propagate_button_press_event)
        column_header.set_focus_on_click(False)

答案 1 :(得分:1)

自从提出此问题以来,API一直在发展,所以我想我会发布更新的答案。 (虽然在我遇到类似问题时遇到了偶然发现,但就我而言,我试图在列标题中放置两个Button,而不是Entry)。

首先,有一些背景知识。正如问题的编辑中提到的那样,问题源于TreeViewColumn的结构方式。列的标题是一个Button,当您set_widget时,该小部件将成为Button的后代。 (这很容易被忽略,因为除非将列设置为可单击,否则标题不会像按钮一样响应。而且,文档似乎也无济于事,因为它似乎假定每个人都已经知道这一点。)问题的另一个原因是Button收集事件的方式。与大多数响应事件的小部件不同,Button在Gdk.Window层次结构中没有自己的位置。而是在实现时创建一个特殊事件窗口。访问此窗口的方法是特定于按钮的:get_event_window(与更通用的get_windowget_parent_window不同)。该事件窗口不可见地位于Button上方,在事件滴入到Button的任何后代之前收集事件。因此,您放置在列标题中的窗口小部件不会收到交互所需的事件。

公认的解决方案是解决这一障碍的一种方法,在当时是一个值得回答的问题。但是,现在有一种更简单的方法。 (我应该提到,这是一个GTK +问题,与所使用的语言绑定无关。我个人使用的是C ++绑定。我还查看了C中的GTK +源文件,以确认这是GTK +的核心行为,而不是绑定的一些伪像。)

1)找到标题按钮。

如果column是有问题的TreeViewColumn,则获取按钮的API现在很简单:

header_button = column.get_button()

在版本3.0中添加了get_button方法,该方法在提出此问题大约六个月后被标记。如此接近。

2)将事件从按钮传播到条目。

此步骤简化又花了四年时间(3.18版)。关键的开发是set_pass_through,它可以告诉事件窗口让事件通过。正如文档所述:“在网络术语中,这称为“指针事件:无”。”

def pass_through_event_window(button, event):
    if not isinstance(button, gtk.Button):
        raise TypeError("%r is not a gtk.Button" % button)
    event_window = button.get_event_window()
    event_window.set_pass_through(True)

剩下的把戏是时机之一。在实现按钮之前,不会创建事件窗口,因此必须按顺序连接到按钮的realize信号。

header_button.connect('realize', pass_through_event_window)

就是这样(没有步骤3)。现在,事件将传播到Entry或您在列标题中放入的任何小部件。

如果我弄乱了语法,我深表歉意;我正在从C ++绑定进行翻译。如果有错误,我会请一位善良的Python专家来纠正。

答案 2 :(得分:0)

除非这不是您的答案,但我建议您使用标准和常用方法:使用treeview.set_search_column(COLUMN_INDEX),默认情况下不会显示任何条目(并使UI更清晰)但是当用户开始输入时(使用专注于那个树视图)将显示一个弹出输入条目,搜索和过滤将由GTK自动完成。

如果您坚持认为搜索条目应始终可见,请使用treeview.set_headers_visible(False)删除treeview标头,并在树视图上方添加自定义HBox(包含标签和条目)。