2

I'm creating a Gtk4 app in python 3 and I want to trigger a state change when the user mouses over the ApplicationWindow but I'm banging my head against a wall trying to figure out what signal string to use (or any documentation or examples of another approach required for that type of signal/event). Everything I try throws an unknown signal error something like this:

    Traceback (most recent call last):
      File "/home/nick/projects/Sandbox/main.py", line 24, in on_activate
        self.window = MainWindow(application=app)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/nick/projects/Sandbox/main.py", line 49, in __init__
        self.connect("enter-notify-event", self.on_enter)
    TypeError: <__main__.MainWindow object at 0x7033043054c0
    (__main__+MainWindow at 0x22856930)>: unknown signal name: enter-notify-event

NOTE: The last two lines were one long line in the terminal.

I found EventControllerMotion with enter and leave signals in the GNOME Gtk4 Python APIs, but it doesn't seem to correlate to any of the documentation and examples I've found. Still, I tried a bunch of random variations of calling the class and using strings with and without enter and leave connected.

I also found enter-notify-event and leave-notify-event through Glade, but they don't work in my code either. However, if I add an ApplicationWindow to a new Glade project and assign a handler to one or the other signal then Preview snapshot the enter and leave events trigger at the expected moments (they throw an error but that seems reasonable since the handlers don't exist). I tried looking at the app's output but it's XML for parsing by a builder (if I understand correctly) and not executable code.

Here's minimal python that demonstrates the approach and causes the error:

#!/usr/bin/env python3
import gi
gi.require_version("Gtk", "4.0")
from gi.repository import Gtk

class MyApp(Gtk.Application):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.connect('activate', self.on_activate)
        self.window: MainWindow

    def on_activate(self, app):
        self.window = MainWindow(application=app)
        self.window.present()


class MainWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Window Configuration
        self.set_default_size(200, 200)
        
        label_box = Gtk.Box()
        self.away_button = Gtk.Button(label="mouse away", hexpand=True)
        self.here_button = Gtk.Button(label="mouse here", hexpand=True,
                                      visible=False)
        label_box.append(self.away_button)
        label_box.append(self.here_button)
        self.set_child(label_box)

        # These Connections Work...
        # self.away_button.connect("clicked", self.on_enter)
        # self.here_button.connect("clicked", self.on_leave)
        
        # These Connections throw the type error
        self.connect("enter-notify-event", self.on_enter)
        self.connect("leave-notify-event", self.on_leave)

    def on_enter(self, widget) -> bool:
        self.away_button.set_visible(False)
        self.here_button.set_visible(True)
        return True

    def on_leave(self, widget) -> bool:
        self.away_button.set_visible(True)
        self.here_button.set_visible(False)
        return True


# Run Application
main_app = MyApp(application_id="com.coldnight.sandbox")
main_app.run()

1 Answer 1

2

After some more investigation, it turns out that you instantiate the EventControllerMotion class, connect to its signals, then add the controller to your window:

        event_controller = Gtk.EventControllerMotion.new()
        event_controller.connect("enter", self.on_enter)
        event_controller.connect("leave", self.on_leave)
        self.add_controller(event_controller)

Also, your handlers need to accept two arguments (self, and the event controller), then any arguments the signal sends. For example, the enter signal sends the x and y coordinates of the pointer.

def on_enter(self, event_controller, x, y) -> bool:

Here is a fully working version of the example code from the question:

#!/usr/bin/env python3
import gi
gi.require_version("Gtk", "4.0")
from gi.repository import Gtk


class MyApp(Gtk.Application):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.connect('activate', self.on_activate)
        self.window: MainWindow

    def on_activate(self, app):
        self.window = MainWindow(application=app)
        self.window.present()


class MainWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Window Configuration
        self.set_default_size(200, 200)

        label_box = Gtk.Box()
        self.away_button = Gtk.Button(label="mouse away", hexpand=True)
        self.here_button = Gtk.Button(label="mouse here", hexpand=True,
                                      visible=False)
        label_box.append(self.away_button)
        label_box.append(self.here_button)
        self.set_child(label_box)

        # You must instantiate the event controller class and, use it to
        # to create the connections then add the controller to the
        # window.
        event_controller = Gtk.EventControllerMotion.new()
        event_controller.connect("enter", self.on_enter)
        event_controller.connect("leave", self.on_leave)
        self.add_controller(event_controller)

    def on_enter(self, event_controller, x, y) -> bool:
        self.away_button.set_visible(False)
        self.here_button.set_visible(True)
        return True

    def on_leave(self, event_controller) -> bool:
        self.away_button.set_visible(True)
        self.here_button.set_visible(False)
        return True


# Run Application
main_app = MyApp(application_id="com.coldnight.sandbox")
main_app.run()
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.