Search code examples
pythonuser-interfacelocalepyside6mpv

Keybinding does not work in python-mpv when using it together with pyside6


I use the library https://github.com/jaseg/python-mpv to control the mpv player, but when using it together with pyside6, keybindings do not work (player doesn't accept input totally). What am I doing wrong? or is it impossible to use them when embedding in pyside6? (If I run the player with the same arguments without embedding, everything works fine)

import os
os.add_dll_directory(os.getcwd())
import mpv
from PySide6.QtWidgets import *
from PySide6.QtCore import *
mpvfolderpath = f"mpv.net/portable_config/"
import sys
class Test(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.container = QWidget(self)
        self.setCentralWidget(self.container)
        self.container.setAttribute(Qt.WA_DontCreateNativeAncestors)
        self.container.setAttribute(Qt.WA_NativeWindow)
        player = mpv.MPV(wid=str(int(self.container.winId())),
                         vo="gpu",  # You may not need this
                         log_handler=print,
                         loglevel='debug',
                         input_default_bindings=True,
                         input_vo_keyboard=True)

        @player.on_key_press('f')
        def my_f_binding():
            print("f работает!")
        player.play('test.mp4')

app = QApplication(sys.argv)

# This is necessary since PyQT stomps over the locale settings needed by libmpv.
# This needs to happen after importing PyQT before creating the first mpv.MPV instance.
import locale
locale.setlocale(locale.LC_NUMERIC, 'C')
win = Test()
win.show()
sys.exit(app.exec_())

Solution

  • If the keyboard is not handled (which, in my tests, only happened when the mouse is not hovering the video), the key events are propagated to the Qt window. This means that we can handle those events in the keyPressEvent() override and then create a proper mpv command, which is already mapped to the keypress() function. Obviously, a reference to the player must exist, so you need to make it an instance attribute.

    For standard literal keys, it's usually enough to use the event's text(), but for other keys such as arrows you need to map the event with mpv's key names. Using a dictionary is certainly simpler:

    MpvKeys = {
        Qt.Key.Key_Backspace:   'BS', 
        Qt.Key.Key_PageUp:      'PGUP', 
        Qt.Key.Key_PageDown:    'PGDWN', 
        Qt.Key.Key_Home:        'HOME', 
        Qt.Key.Key_End:         'END', 
        Qt.Key.Key_Left:        'LEFT', 
        Qt.Key.Key_Up:          'UP', 
        Qt.Key.Key_Right:       'RIGHT', 
        Qt.Key.Key_Down:        'DOWN', 
        # ...
    }
    
    class Test(QMainWindow):
        def __init__(self, parent=None):
            # ...
            self.player = mpv.MPV(...)
    
        def keyPressEvent(self, event):
            # look up for the key in our mapping, otherwise use the event's text
            key = MpvKeys.get(event.key(), event.text())
            self.player.keypress(key)
    

    Note: in my tests I had to use the vo='x11' flag to have a properly embedded window, and osc=True is also required to use the native OSD.