Skip to content

PyGTK Eintragswidget in TreeViewColumn Kopfzeile

Wir überprüfen jede Aussage auf unserer Website gründlich mit dem Ziel, Ihnen jederzeit wahre und aktuelle Informationen zu zeigen.

Lösung:

Um eine GtkEntry zu fokussieren innerhalb eines GtkTreeView Überschrift zu machen, musste ich das tun:

1) Die Überschrift finden 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) Die Überschrift button-press-event Signal von der Kopfzeile GtkButton an die 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) Übertragen Sie den Fokus (d.h., focus-in-event Signal) von der Kopfzeile GtkButton an die 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

Beispiel:

# 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)

Die API hat sich weiterentwickelt, seit diese Frage gestellt wurde, daher dachte ich, ich würde eine aktualisierte Antwort veröffentlichen. (Ich war darüber gestolpert, als ich mich mit einem ähnlichen Problem befasste, obwohl ich in meinem Fall versuchte, zwei Schaltflächen in die Spaltenüberschrift zu setzen, nicht einen Eintrag).

Zunächst einige Hintergrundinformationen. Wie in der Bearbeitung der Frage erwähnt, liegt das Problem in der Art und Weise, wie eine TreeViewColumn aufgebaut ist. Die Überschrift der Spalte ist ein Button, und wenn man set_widgetwird dieses Widget ein Nachkomme des Buttons. (Dies kann leicht übersehen werden, da die Kopfzeile nicht wie eine Schaltfläche reagiert, es sei denn, Sie legen fest, dass die Spalte anklickbar ist. Auch die Dokumentation hilft nicht weiter, da sie davon auszugehen scheint, dass jeder dies bereits weiß.) Eine weitere Ursache für das Problem ist die Art und Weise, wie Schaltflächen Ereignisse sammeln. Anders als die meisten Widgets, die auf Ereignisse reagieren, hat eine Schaltfläche keinen eigenen Platz in der Gdk.Window-Hierarchie. Stattdessen erstellt er ein spezielles Ereignisfenster, wenn er realisiert wird. Die Methode für den Zugriff auf dieses Fenster ist Button-spezifisch: get_event_window (im Unterschied zu der allgemeineren get_window und get_parent_window). Dieses Ereignisfenster befindet sich unsichtbar über der Schaltfläche und sammelt die Ereignisse, bevor sie an die Nachkommen der Schaltfläche weitergegeben werden. Daher erhält das Widget, das Sie in der Spaltenüberschrift platzieren, nicht die für die Interaktivität erforderlichen Ereignisse.

Die akzeptierte Lösung ist eine Möglichkeit, dieses Hindernis zu umgehen, und sie war seinerzeit eine gute Lösung. Jetzt gibt es jedoch einen einfacheren Weg. (Ich sollte erwähnen, dass dies ein Problem von GTK+ ist, unabhängig von der verwendeten Sprachbindung. Ich persönlich habe die C++-Sprachbindung verwendet. Ich habe auch einen Blick in die GTK+-Quelldateien - in C - geworfen, um zu bestätigen, dass dies ein Kernverhalten von GTK+ ist und nicht ein Artefakt der Bindung).

1) Finden Sie den Header Button.

Wenn column die betreffende TreeViewColumn ist, ist die API für das Erhalten des Buttons jetzt einfach:

header_button = column.get_button()

Die get_button Methode wurde in Version 3.0 hinzugefügt, die etwa sechs Monate nach dieser Frage veröffentlicht wurde. So nah dran.

2) Ereignisse von der Schaltfläche an den Eintrag weiterleiten.

Es dauerte weitere vier Jahre (Version 3.18), bis dieser Schritt vereinfacht wurde. Die wichtigste Entwicklung war set_pass_throughdie das Ereignisfenster anweisen kann, Ereignisse durchzulassen. In der Dokumentation heißt es dazu: "In der Web-Terminologie würde dies als 'pointer-events: none' bezeichnet."

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)

Der verbleibende Trick ist eine Frage des Timings. Das Ereignisfenster wird erst erstellt, wenn der Button realisiert wird, so dass eine Verbindung mit dem Button's realize Signal des Buttons zu verbinden, ist in Ordnung.

header_button.connect('realize', pass_through_event_window)

Und das war's (es gibt keinen Schritt 3). Die Ereignisse werden nun an den Eintrag oder das Widget weitergegeben, das Sie in die Spaltenüberschrift setzen.

Ich entschuldige mich, wenn ich die Syntax durcheinander gebracht habe; ich übersetze aus der C++-Bindung. Wenn es Fehler gibt, würde ich einen freundlichen Python-Guru bitten, sie zu korrigieren.

Abschnitt Rezensionen und Bewertungen

Sie haben die Möglichkeit, unsere Informationen aufzuwerten, indem Sie Ihre Erfahrungen in Bewertungen einbringen.



Nutzen Sie unsere Suchmaschine

Suche
Generic filters

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.