Strange behaviour for Gtk.TreeView keypress events

135 views Asked by At

I have a Gtk.Treeview which shows rows with items. I want to select lines with button 1 (default behaviour), and, on clicking with button 3 (right), a context menu should appear. Here are the results I get:

1 - Using connect to connect to the button-press-event, works fine, but, as this handler is called before the default (treeview) handler, the line selection was not changed yet, and reading the selected row gives the previously selected line.

2 - Use connect-after to connect to the button-press-event. But now my handler is not called anymore... As if the internal handler exits with return True. Strangely, double-clicking does call to my handler.

How can I have button-3 change the selection first, then call my handler?

Here's a short version of the program, uncomment one of the marked lines to test 1 or 2.

BTW: I thought maybe set_activate_on_single_click might help - no luck.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
#  test_keypress.py
#
#  Copyright 2021 John Coppens <[email protected]>
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA 02110-1301, USA.
#
#


import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GooCanvas', '2.0')
from gi.repository import Gtk, GooCanvas

class MainWindow(Gtk.Window):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.connect("destroy", lambda x: Gtk.main_quit())
        self.set_default_size(400, 300)

        store = Gtk.ListStore(str)
        view = Gtk.TreeView(model = store)
        view.connect('button-press-event', self.on_button_pressed)         # Select one of these
        # view.connect_after('button-press-event', self.on_button_pressed) # ....
        renderer = Gtk.CellRendererText()
        col = Gtk.TreeViewColumn('Column', renderer, text = 0)
        view.append_column(col)

        store.append(('Alpha', ))
        store.append(('Beta', ))
        store.append(('Gamma', ))

        scroller = Gtk.ScrolledWindow()
        scroller.add(view)

        self.add(scroller)
        self.show_all()

    def on_button_pressed(self, view, event):
        sel = view.get_selection()
        store, selected = sel.get_selected()
        item = store[selected]
        print(event.button, item[0])

    def run(self):
        Gtk.main()


def main(args):
    mainwdw = MainWindow()
    mainwdw.run()

    return 0

if __name__ == '__main__':
    import sys
    sys.exit(main(sys.argv))
2

There are 2 answers

2
jcoppens On BEST ANSWER

Thanks @theGtknerd - I've noticed this method being used in several programs, but somehow it doesn't feel entirely right. I like things to happen when I take action, not when I finish it.

Anyway, thanks to a suggestion of @Company on IRC, I solved the issue like this (and the code length is about the same):

  • Using the x, y coordinates from the button-press-event it's easy to identify the correct row in the Gtk.TreeView. After processing the event, I let the default handler change the selection. The code is something like:
    def on_button_pressed(self, view, event):
        path, _, _, _ = view.get_path_at_pos(int(event.x), int(event.y))
        if path:
            item = self.store[self.store.get_iter(path)]
            print(event.button, item[0])

(not sure the int()s are necessary, but get_path_at_pos specifies int arguments.

The _s are dummy variables, 'cause get_path_at_pos returns a 4-tuple and I only need the first element.

0
theGtknerd On

You need to use the button-release-event event, so that Gtk can process the first event. Example:

view.connect('button-release-event', self.on_button_pressed)