PyGTK input widget in TreeViewColumn header

How can I make the gtk.Entry widget oriented or editable in the gtk.TreeViewColumn header? I 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 tried setting the "clickable" to True and False . I am using pygtk 2.21.0-0ubuntu1 and libgtk 2.22.0-0ubuntu1 on Ubuntu 10.04. Any help would be greatly appreciated.

EDIT:

The problem is with the way the GtkTreeViewColumn header is GtkTreeViewColumn . The header widget is placed inside GtkAlignment , the parent of which is GtkHBox , the parent of which is GtkButton , the parent of which is finally GtkTreeView . GtkButton intercepts and prevents the focus of my GtkEntry and mouse input.

+7
python gtk pygtk gtktreeview gtkentry
source share
3 answers

To make GtkEntry included in the GtkTreeView header, I had to:

1) Find the GtkButton header.

 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) Distribution of the button-press-event signal from the GtkButton header to 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) Place the focus (i.e. focus-in-event signal) from the GtkButton header 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) 
+11
source share

If this is not your answer, but I suggest you use a standard and common method: using treeview.set_search_column(COLUMN_INDEX) , the record will not be displayed by default (and makes the UI cleaner), but when the user starts typing (with emphasis on this treeview) a pop-up entry entry will be displayed, and GTK itself will automatically search and filter.

If you keep the search entry always visible, remove the treeview header with treeview.set_headers_visible(False) and add a custom HBox (containing the label and entry) above the tree.

0
source share

The API has evolved since this question was asked, so I decided to post an updated answer. (I came across this while dealing with a similar problem, although in my case I tried to put two buttons in the column heading, not the record).

First, a little background. As mentioned in the question editing, the problem is with the TreeViewColumn structure. The column heading is a button, and when you set_widget , this widget becomes a child of the button. (This can be easily skipped, because the header does not respond like a button, unless you set the column to click with the mouse. Also, the documentation does not help, as it seems that everyone already knows this.) Another cause of the problem How buttons collect events . Unlike most widgets that respond to events, the button does not have its own place in the Gdk.Window hierarchy. Instead, it creates a special event window when it is implemented. The access method for this window depends on the button: get_event_window (differs from the more general get_window and get_parent_window ). This event window is discreetly located above the button, collecting events before they go to the descendants of the button. Therefore, the widget that you put in the column header does not receive the events necessary for interactivity.

The decision made is one way to get around this obstacle, and it was a worthy answer at that time. However, there is now an easier way. (I should mention that this is a GTK + issue, regardless of the language binding used. Personally, I used the C ++ binding. I also looked into the GTK + source files - in C - to confirm that this is the main GTK + behavior, and not some kind of binding artifact .)

1) Find the title button.

If column is a TreeViewColumn, the API to get the button is now simple:

 header_button = column.get_button() 

The get_button method was added in version 3.0, which was tagged about six months after this question was asked. So close.

2) Propagation of events from the Button to the Entrance.

It took another four years (version 3.18) to simplify this step. A key development was set_pass_through , which can tell the event window to skip events. As the documentation says: "In Internet terminology, this will be called" event-pointer: none. "

 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) 

The remaining trick is timing. The event window is not created until the button is implemented, so the button is connected to the realize signal of the button in order.

 header_button.connect('realize', pass_through_event_window) 

And this is it (no step 3). Events will now propagate to Entry or any widget that you place in the column heading.

My apologies if I messed up the syntax; I am translating from a C ++ binding. If there are errors, I would ask the kind Python guru to fix them.

0
source share

All Articles