How can I make a gtk.Entry
widget focusable or editable within a gtk.TreeViewColumn
header/title? I've tried this:
# 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)
But the Entry widget in the column header is not editable and will not focus. I've tried setting 'clickable' to both True
and False
. I'm using pygtk 2.21.0-0ubuntu1 and libgtk 2.22.0-0ubuntu1 on Ubuntu 10.04. Any help would be greatly appreciated.
EDIT:
The issue stems from how a GtkTreeViewColumn
header is displayed. The header widget is placed inside a GtkAlignment
whose parent is a GtkHBox
whose parent is a GtkButton
whose parent is finally the GtkTreeView
. The GtkButton
is intercepting and preventing my GtkEntry
from being focused and receiving mouse input.
In order to make a GtkEntry
focusable within a GtkTreeView
header I had to:
1) Find the header 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) Propagate the button-press-event
signal from the header GtkButton
to the 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) Propagate the focus (i.e., focus-in-event
signal) from the header GtkButton
to the 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
Example:
# 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)