How do I make my python-kivy window become the focus when the mouse cursor is over the window?

Question:

python 3.7.8
kivy 2.0.0

Modern programs are able to make changes in a window even when the focus is not True for the app.

Example: Two windows are set side by side (Chrome and Excel). I click inside the Chrome window and it becomes focus True. When I then take my cursor into the Excel window I can click into a cell directly or scroll up and down. This action in Excel does not require me to first click in the Excel window to make it focus True.

I have not been able to replicate this with python-kivy.

Window.on_cursor_enter(do_action())

does not fire when the cursor enters the window.

def on_start(self):
        Window.bind(mouse_pos=self.my_callback)

def my_callback(self, instance, value):
        Window.focus = True

Throws:

File "kivyproperties.pyx", line 498, in kivy.properties.Property.__set__
   File "kivyproperties.pyx", line 1527, in kivy.properties.AliasProperty.set
   File "kivyproperties.pyx", line 1485, in kivy.properties.AliasProperty.__read_only
 AttributeError: "WindowSDL.focus" property is readonly

UPDATE:

Window.on_cursor_enter(do_action())

is saddled with a pass in the kivy source. That clears up why it doesn’t work.

I think the true correction is going to come from a call to ctypes.windll.user32…setFocus…something like that
but I am a Python hobbyist, and my understanding of this is limited.

Asked By: Paman90

||

Answers:

I think I found a work-around but you maybe will not be satisfied with it.

Anyway , here is my solution :

import kivy
from kivy.uix.widget import Widget
from kivy.app import App
from kivy.clock import Clock
from kivy.core.window import Window

import pyautogui  # for mouse click

kivy.lang.Builder.load_string("""
#:kivy 2.0.0

<Main>:
    
    ScrollView:
        do_scroll_x: True
        do_scroll_y: True
        size: root.size
        
        BoxLayout:
            size_hint: 1, None
            width: 50
            orientation: "vertical"
                
            Label:
                text: "Hello World !"
                font_size: 25
        
            Button:
                id: my_button
                text: "click me !"
                font_size: 25
                
                on_release:
                    root.button_callback()
        
""")


class Main(Widget):

    def button_callback(self):  # fired when button is clicked
        print("clicked on button !")

    def window_callback(self, instance):  # fired when cursor entered window
        Window.restore()  # seems like doesn't work
        Window.show()  # this also

        # so I do this when the mouse is hover on the window
        # import pyautogui first
        if Window.focus == False:
            pyautogui.click()  # a mouse click for focusing the window

            print(True)  # just to clarify the above line got executed

        print(f"window is focus ? {Window.focus}")  # although it may says the windows is not focused , it should be

    def update(self, dt):
        Window.bind(on_cursor_enter = self.window_callback)  # bind when on_cursor_enter


class MyKivyApp(App):

    def build(self):
        Clock.schedule_interval(Main().update, 1/60)

        return Main()


if __name__ == "__main__":
    MyKivyApp().run()

How it works : when mouse is hovered on the app window , it will have one mouse click by pyautogui to force focus to the app window programmatically.

The only line of code inside the update func and the window_callback func will fired when the mouse is hover on the window ( i.e. like when the mouse leaves and moves back to the app window , the window_callback will be fired ) then I use pyautogui to do a mouse click by itself so that the window can be focused by itself. This way , the kivy app window can be focused when the mouse moves back to the window.

Moreover , according to this stackoverflow answer , the SLD2 on desktop app can hide the window but to restore / show it. It can scroll even when the app window is not focused ( I tested )

Answered By: Paul Lam

Just add the following at the top of your code:

import os
os.environ["SDL_MOUSE_FOCUS_CLICKTHROUGH"] = '1'

See discussion at: https://groups.google.com/g/kivy-users/c/b1wXKpjzjkg/m/5HF6WxSyAwAJ

Answered By: Bill Bridge
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.