【问题标题】:PyGTK Entry widget in TreeViewColumn headerTreeViewColumn 标题中的 PyGTK 条目小部件
【发布时间】:2019-04-09 12:01:57
【问题描述】:

如何使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 获得焦点并接收鼠标输入。

【问题讨论】:

    标签: python gtk pygtk gtktreeview gtkentry


    【解决方案1】:

    为了使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)
    

    【讨论】:

      【解决方案2】:

      自从提出这个问题以来,API 已经发展,所以我想我会发布一个更新的答案。 (我在处理类似问题时偶然发现了这一点,尽管在我的情况下,我试图在列标题中放置两个按钮,而不是条目。)

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

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

      1) 找到标题按钮。

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

      header_button = column.get_button()
      

      get_button 方法是在 3.0 版中添加的,该方法在提出此问题大约六个月后被标记。这么近。

      2) 将事件从 Button 传播到 Entry。

      又花了四年时间(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)
      

      剩下的技巧是时机。直到 Button 实现后才会创建事件窗口,因此连接到 Button 的realize 信号是有顺序的。

      header_button.connect('realize', pass_through_event_window)
      

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

      如果我弄乱了语法,我深表歉意;我正在翻译 C++ 绑定。如果有错误,我会请求一位好心的 Python 大师来纠正它们。

      【讨论】:

      • 语法看起来正确。使用 GTK 3,我知道 Python 绑定从 pygtk 更改为 pygobject 我没有使用过。我希望 API 能够匹配。
      【解决方案3】:

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

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

      【讨论】:

      • 我需要能够搜索多个列。此外,我不能使用 GTK 的标准搜索/过滤,因为数据是从数据库中获取的子集,因此当设置过滤器时,我会使用过滤器重新查询数据的子集。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多