1

is there any way for a python gtk3 Window to catch every input from keyboard and/or mouse inside it?

Basically i am creating an application and it will launch something like screensaver when idle, to do so i need to know the time when keyboard or mouse event occured inside my window (application).

I tried to connect key-press-event to my window, but when i clicked on its child, like a button, the window doesn't get the event.

Thank you for any solution.

UPDATE As the accepted answer suggests, I connect the event-after signal to the main window, and determine if I need the signal received.

Lee
  • 3,259
  • 4
  • 21
  • 27
  • I've just tried with a simple example (in Vala) and the window keeps receiving the signal. I had a Window with a vertical box container with a entry and a button. The signal get emitted on the window, always. Must try on python but behavior would be same. Can u post the code (if small)? – José Fonte May 25 '17 at 10:34
  • I'm sorry, but the application is quite big, more than 30 files (of classes). I did try some some code testing, and YES, the window did receive the signal. In my case, which I'm not sure how to explain, it some times receive the signal, some times it doesn't, clicking on the very same button. – Lee May 26 '17 at 03:01
  • 1
    Probably not the case but sometimes it's just the return of some callbackt that stops the propagation of a signal. – José Fonte May 26 '17 at 09:45

2 Answers2

1

The key-press-event is for the keyboard only. A button creates a button-press-event. See the docs.

Always remember that the *-press-event gets run before the action is done. I would recommend using the *-release-event that way if you click the button by accident, you can slide off of the button before releasing. This is not as crucial in your case, but still...

theGtknerd
  • 3,647
  • 1
  • 13
  • 34
1

Maybe first recall that there are signals and events. Maybe somewhat over-simplifying, events are 'raw': They come, for example, a keyboard or a mouse. The topmost widget (considering the window being at the bottom) receives those events and decides if it is interesting in the event or not.

If the widget is interested in the event, like a button which is interested in the button-press-event and button-release-event, the widget then creates signals such as, in this case, the 'clicked', 'double-clicked' signals. Inside the event handler, the return value (True or False) determines if the event will propagate 'down' to the next widget ('under' the top one).

So, I suspect you can't easily monitor all events directly on the underlying window before they reach their target.

Maybe a solution for you would be to connect to the 'event-after' of the window. This gets called independently from the return value of the other handlers. Below is a small program where you can test the following:

A GtkGrid appears with a Label, a Button, and a SpinButton. The lower right cell is empty:

  • Click on the label and the events trickle down to the window, because Label doesn't capture any event

  • Click on the button, and the events (such as 'pressed') are captured by the handler, and signals such as 'clicked' are generated.

  • I connected to the 'event-after', and a button press anywhere is reported.

.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
#  test_events.py
#
#  Copyright 2017 John Coppens <john*at*jcoppens*dot*com>
#
#  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.
#
#


from gi.repository import Gtk

class EventsTest(Gtk.Grid):
    def __init__(self):
        super(EventsTest, self).__init__()

        btn = Gtk.Button("Top right")
        btn.connect("clicked", lambda x: print("Button clicked"))

        self.attach(Gtk.Label("Top left"), 0, 0, 1, 1)
        self.attach(btn, 1, 0, 1, 1)
        self.attach(Gtk.SpinButton(), 0, 1, 1, 1)


class MainWindow(Gtk.Window):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.connect("destroy", lambda x: Gtk.main_quit())
        self.connect("button-press-event", self.on_button_pressed)
        self.connect("event-after", self.on_event_after)

        evtest = EventsTest()

        self.add(evtest)
        self.show_all()

    def on_button_pressed(self, btn, event):
        print("Main window button pressed")
        return True

    def on_event_after(self, wdg, event):
        print("Event after")

    def run(self):
        Gtk.main()


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

    return 0

if __name__ == '__main__':
    import sys
    sys.exit(main(sys.argv))
jcoppens
  • 5,306
  • 6
  • 27
  • 47
  • yes, this send the signals I need to inform `event` or `user activity` to the main window. Thank you. – Lee May 27 '17 at 01:47
  • I think there is also the approach of registering callbacks rather than subclassing Gtk.Window. Namely you would instantiate a window like `window = Gtk.Window()` do things like `window.connect('button-press-event', ....)` rather than encapsulating your code within a subclassed Gtk.Window. Will likely bump into the pros and cons soon. – matanster Sep 26 '20 at 18:46
  • Yes, sure. This has been a long-standing argument for years. Instantiating (sometimes called composition) instead of subclassing - there are already plenty of discussions on this, both on stack overflow and elsewhere. – jcoppens Oct 05 '20 at 04:36