How to simultaneously display keypress for 2 separate Kivy windows

Viewed 22

I have a case where I need 2 Kivy windows each on a separate monitor to display keyboard press events simultaneously. I have a simple toy example of the code for 'app1.py' and 'app2.py' below.

I was able to successfully use 'subprocess' and kivy.config so that when you press the 'Press to open second window' button in my main application (i.e., app1) a second window (i.e., app2) opens on my second monitor. However, I am stuck on to how I can simultaneously display keyboard events to both application windows when the spacebar is pressed.

Any ideas on how this can be achieved, is it even possible in Kivy?

Best, Tom

File name: app1.py

from kivy.app import App
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
from kivy.properties import StringProperty
from subprocess import Popen

Window.size = (1920, 1080)
Window.borderless = True
Builder.load_string('''
<FirstWindow>:
    id: _FirstWindow
    FloatLayout:
        Label:
            text: "First Window"
            size_hint: None, None
            font_size: 50
            pos: (900,940)
        Label:
            text: _FirstWindow.key_down
            size_hint: None, None
            font_size: 30
            pos: (900,800)
        Button:
            text: "Press to open second window"
            size_hint: None, None
            font_size: 30
            size: (450, 60)
            pos: (720, 600)
            on_press: root.OpenSecondWindow()
''')

class FirstWindow(FloatLayout):
    key_down = StringProperty()  # perform button state

    def __init__(self, **kwargs):
        super(FirstWindow, self).__init__(**kwargs)
        self._keyboard = Window.request_keyboard(None, self)
        self._keyboard.bind(on_key_down=self.on_keyboard_down, on_key_up=self.on_keyboard_up)

    def OpenSecondWindow(self):
        p = Popen(['python ./app2.py'], shell=True)

    def on_keyboard_down(self, keyboard, keycode, text, modifiers):
        if keycode[1] == 'spacebar':
            self.key_down = 'spacebar pressed!'

    def on_keyboard_up(self, keyboard, keycode):
        if keycode[1] == 'spacebar':
            self.key_down = ''

class App1(App):
    def build(self):
        return FirstWindow()

if __name__ == '__main__':
    App1().run()

File name: app2.py

from kivy.config import Config
Config.set('graphics', 'resizable', '0')
Config.set('graphics', 'position', 'custom')
Config.set('graphics', 'top', '-900')
Config.set('graphics', 'left', '0')
Config.set('graphics', 'borderless', '1')
Config.set('graphics', 'fullscreen', 'auto')

from kivy.app import App
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import StringProperty
from kivy.lang import Builder

Window.size = (1920, 1080)
Window.borderless = True
Builder.load_string('''
<SecondWindow>:
    id: _SecondWindow
    FloatLayout:
        Label:
            text: "Second Window"
            size_hint: None, None
            font_size: 50
            pos: (900,940)
        Label:
            text: _SecondWindow.key_down
            size_hint: None, None
            font_size: 30
            pos: (900,800)
''')

class SecondWindow(FloatLayout):
    key_down = StringProperty()  # perform button state

    def __init__(self, **kwargs):
        super(SecondWindow, self).__init__(**kwargs)
        self._keyboard = Window.request_keyboard(None, self)
        self._keyboard.bind(on_key_down=self.on_keyboard_down, on_key_up=self.on_keyboard_up)

    def on_keyboard_down(self, keyboard, keycode, text, modifiers):
        if keycode[1] == 'spacebar':
            self.key_down = 'spacebar pressed!'

    def on_keyboard_up(self, keyboard, keycode):
        if keycode[1] == 'spacebar':
            self.key_down = ''


class App2(App):

    def build(self):
        return SecondWindow()


if __name__ == '__main__':
    App2().run()
1 Answers

From app1, you can write to the stdin of app2. Here are the changes to make that happen. In your OpenSecondWindow() method add stdin=PIPE and save a reference to the Popen object:

def OpenSecondWindow(self):
    self.p = Popen(['python3 ./app2.py'], shell=True, stdin=PIPE, universal_newlines=True)

Then, in your on_keyboard_down() method, write to the stdin of app2:

def on_keyboard_down(self, keyboard, keycode, text, modifiers):
    if keycode[1] == 'spacebar':
        self.key_down = 'spacebar pressed!'
        self.p.stdin.write('spacebar pressed\n')
        self.p.stdin.flush()

And in your app2, you must listen for input on stdin. Add this code to your SecondWindow:

def __init__(self, **kwargs):
    super(SecondWindow, self).__init__(**kwargs)
    self._keyboard = Window.request_keyboard(None, self)
    self._keyboard.bind(on_key_down=self.on_keyboard_down, on_key_up=self.on_keyboard_up)

    Thread(target=self.listen_for_input).start()

def listen_for_input(self):
    while True:
        data = input()
        self.set_label(data)

@mainthread
def set_label(self, data):
    self.ids.label.text = data

And the above requires an id in the kv for app2:

    Label:
        id: label

And you can add stdout=PIPE to the Popen, and use a similar construct to allow communication in the opposite direction.

Related