Виджет PyGTK Entry в заголовке TreeViewColumn - PullRequest
7 голосов
/ 07 марта 2011

Как сделать фокусируемый или редактируемый виджет 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)

Но виджет «Запись» в заголовке столбца недоступен для редактирования и не будет фокусироваться. Я попытался установить «кликабельность» для True и False. Я использую pygtk 2.21.0-0ubuntu1 и libgtk 2.22.0-0ubuntu1 в Ubuntu 10.04. Любая помощь будет принята с благодарностью.

EDIT:

Проблема связана с отображением заголовка GtkTreeViewColumn. Виджет заголовка находится внутри GtkAlignment, чей родитель - GtkHBox, чей родитель - GtkButton, чей родитель, наконец, GtkTreeView. GtkButton перехватывает и не позволяет моему GtkEntry быть сфокусированным и получать ввод от мыши.

Ответы [ 3 ]

11 голосов
/ 09 марта 2011

Чтобы сделать фокусировку GtkEntry в заголовке GtkTreeView, мне нужно было:

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 голос
/ 09 апреля 2019

API развился с тех пор, как был задан этот вопрос, поэтому я решил опубликовать обновленный ответ.(Я наткнулся на это, когда сталкивался с подобной проблемой, хотя в моем случае я пытался поместить две кнопки в заголовок столбца, а не запись.)

Сначала немного фона.Как упомянуто в редактировании вопроса, проблема связана со структурой TreeViewColumn.Заголовок столбца - это кнопка, и когда вы set_widget, этот виджет становится потомком кнопки.(Это может быть легко пропущено, поскольку заголовок не реагирует как кнопка, если вы не установите для столбца возможность щелкать мышью. Кроме того, документация не помогает, так как кажется, что все это уже знают.) Еще одна причина проблемыКак кнопки собирать события.В отличие от большинства виджетов, отвечающих на события, у кнопки нет своего места в иерархии Gdk.Window.Вместо этого он создает специальное окно события, когда оно реализовано.Метод доступа к этому окну зависит от кнопки: get_event_window (отличается от более общих get_window и get_parent_window).Это окно событий незаметно расположено над кнопкой, собирая события, прежде чем они перейдут к потомкам кнопки.Следовательно, виджет, который вы помещаете в заголовок столбца, не получает события, необходимые для интерактивности.

Принятое решение является одним из способов обойти это препятствие, и в то время это был достойный ответ.Однако теперь есть более простой способ.(Я должен отметить, что это проблема GTK +, независимо от используемой языковой привязки. Лично я использовал привязку C ++. Я также посмотрел на исходные файлы GTK + - в C -, чтобы подтвердить, что это основное поведение GTK +, а ненекоторый артефакт привязки.)

1) Найдите заголовок Button.

Если column - это рассматриваемый TreeViewColumn, API для получения кнопки теперьпросто:

header_button = column.get_button()

Метод get_button был добавлен в версию 3.0, которая была помечена примерно через шесть месяцев после того, как был задан этот вопрос.Настолько близко.

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 исправить их.

0 голосов
/ 09 марта 2011

Если это не ваш ответ, но я предлагаю вам использовать стандартный и общий метод: при использовании treeview.set_search_column(COLUMN_INDEX) запись не будет отображаться по умолчанию (и делает пользовательский интерфейс чище), но когда пользователь начинает печатать (с акцентом наэто древовидное представление) будет отображаться всплывающая входная запись, и поиск и фильтрация будут выполняться самим GTK автоматически.

Если вы настаиваете, что запись поиска должна быть всегда видимой, удалите заголовок древовидного представления, используя treeview.set_headers_visible(False), идобавьте пользовательский HBox (содержащий метку и запись) над древовидным списком.

...