Open davFaithid opened 4 years ago
I don't know Qt that well myself, but I guess the problem is that Qt catches all input events and they never get forwarded to mpv. My first idea on how to make this work without rewriting all of libmpv's input bindings would be to catch all key press events on the player QWidget and forward them into mpv using the keypress command. There is also a mouse
command if you want mouse input as well.
The reason wait_for_playback
doesn't work is that you were calling it in the constructor of your Qt window. This means it would wait before even displaying the window. Since Qt has its own main loop (the app.exec()
), there shouldn't be any need for you to call wait_for_playback
. Either have the user close the window through user interaction, or you register an event callback with mpv using MPV.register_event_callback
that looks for event['event_id'] in (MpvEventID.SHUTDOWN, MpvEventID.END_FILE)
and tells qt to exit when it sees one.
Thank you very much @jaseg for your helpful answer. I have tried using the keypress and keybind commands yet they do not seem to work
#First I tried this
player.command('keybind esc stop')
player.command('keybind right seek 1 relative')
#And then I tried
player.keybind('esc stop')
player.keybind('right seek 1 relative')
Example errors I get:
Traceback (most recent call last):
File "main.py", line 131, in <module>
play(url, title)
File "main.py", line 127, in play
win = MainWin()
File "main.py", line 119, in __init__
player.keybind('esc stop')
File "C:\Users\davfa\AppData\Local\Programs\Python\Python38-32\lib\site-packages\mpv.py", line 1335, in __getattr__
return self._get_property(_py_to_mpv(name), lazy_decoder)
File "C:\Users\davfa\AppData\Local\Programs\Python\Python38-32\lib\site-packages\mpv.py", line 1313, in _get_property
cval = _mpv_get_property(self.handle, name.encode('utf-8'), fmt, out)
File "C:\Users\davfa\AppData\Local\Programs\Python\Python38-32\lib\site-packages\mpv.py", line 126, in raise_for_ec
raise ex(ec, *args)
AttributeError: ('mpv property does not exist', -8, (<MpvHandle object at 0x03BD1E38>, b'keybind', 6, <ctypes.c_char_Array_16 object at 0x03BD1E80>))
Traceback (most recent call last):
File "main.py", line 135, in <module>
play(url, title)
File "main.py", line 131, in play
win = MainWin()
File "main.py", line 119, in __init__
@player.command('keybind esc stop')
File "C:\Users\davfa\AppData\Local\Programs\Python\Python38-32\lib\site-packages\mpv.py", line 719, in command
_mpv_command(self.handle, (c_char_p*len(args))(*args))
File "C:\Users\davfa\AppData\Local\Programs\Python\Python38-32\lib\site-packages\mpv.py", line 126, in raise_for_ec
raise ex(ec, *args)
ValueError: ('Invalid value for mpv parameter', -4, (<MpvHandle object at 0x03B21E80>, <mpv.c_char_p_Array_2 object at 0x03B21EC8>))
And when I tried using @player
it errored
File "main.py", line 121
player.play(url)
^
SyntaxError: invalid syntax
Do you have any further suggestions? Thank you.
@jaseg I have tried your recommendations and have written
...
from pynput import keyboard
def play(url, title):
#From https://stackoverflow.com/q/9377914/
class TitleBar(QWidget):
def __init__(self, parent):
super(MyBar, self).__init__()
self.parent = parent
print(self.parent.width())
self.layout = QHBoxLayout()
self.layout.setContentsMargins(0,0,0,0)
btn_size = 35
self.btn_close = QPushButton("x")
self.btn_close.clicked.connect(self.btn_close_clicked)
self.btn_close.setFixedSize(btn_size,btn_size)
self.btn_close.setStyleSheet("background-color: red;")
self.btn_min = QPushButton("-")
self.btn_min.clicked.connect(self.btn_min_clicked)
self.btn_min.setFixedSize(btn_size, btn_size)
self.btn_min.setStyleSheet("background-color: gray;")
self.btn_max = QPushButton("+")
self.btn_max.clicked.connect(self.btn_max_clicked)
self.btn_max.setFixedSize(btn_size, btn_size)
self.btn_max.setStyleSheet("background-color: gray;")
self.title.setFixedHeight(35)
self.title.setAlignment(Qt.AlignCenter)
self.layout.addWidget(self.btn_min)
self.layout.addWidget(self.btn_max)
self.layout.addWidget(self.btn_close)
self.setLayout(self.layout)
self.start = QPoint(0, 0)
self.pressing = False
def resizeEvent(self, QResizeEvent):
super(MyBar, self).resizeEvent(QResizeEvent)
self.title.setFixedWidth(self.parent.width())
def mousePressEvent(self, event):
self.start = self.mapToGlobal(event.pos())
self.pressing = True
def mouseMoveEvent(self, event):
if self.pressing:
self.end = self.mapToGlobal(event.pos())
self.movement = self.end-self.start
self.parent.setGeometry(self.mapToGlobal(self.movement).x(),
self.mapToGlobal(self.movement).y(),
self.parent.width(),
self.parent.height())
self.start = self.end
def mouseReleaseEvent(self, QMouseEvent):
self.pressing = False
def btn_close_clicked(self):
self.parent.close()
def btn_max_clicked(self):
self.parent.showMaximized()
def btn_min_clicked(self):
self.parent.showMinimized()
class MainWin(QMainWindow):
def on_press(self,key):
print('{0} pressed'.format(
key))
if key == keyboard.Key.right:
self.player.command('seek', '0.2', 'absolute+keyframes')
def on_release(self, key):
print('{0} released'.format(
key))
if key == keyboard.Key.esc:
try:
print("Quitting")
sys.exit('quit')
except KeyboardInterrupt:
sys.exit('quit')
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)
self.setWindowTitle(title)
self.resize(1000, 563)
self.player = mpv.MPV(wid=str(int(self.container.winId())), ytdl=True, player_operation_mode='pseudo-gui',
script_opts='osc-layout=box,osc-seekbarstyle=bar,osc-deadzonesize=0,osc-minmousemove=3',
input_default_bindings=True,
input_vo_keyboard=True,
osc=True) #
#player.play(url)
# Collect events until released
with keyboard.Listener(
on_press=self.on_press,
on_release=self.on_release) as listener:
Thread(target = self.player.play, args=(url,)).start()
listener.join()
#Process(target=player.play, args=(url,)).start()
#player.wait_for_playback()
del self.player
#@player.command('keybind esc stop')
#def passthru():
# pass
#@player.command('keybind right seek 1 relative')
#def passthru():
# pass
app = QApplication(sys.argv)
import locale
locale.setlocale(locale.LC_NUMERIC, 'C')
win = MainWin()
win.show()
sys.exit(app.exec_())
play(url, title)
it registers commands, but does them all in a seperate process, which upon exiting plays the video. I couldn't seem to get keypress or keybind to give me anything but my prior error.
File "main.py", line 137, in __init__
self.player.command('keybind', 'right', 'seek', '0.2', 'absolute+keyframes')
File "C:\Users\davfa\AppData\Local\Programs\Python\Python38-32\lib\site-packages\mpv.py", line 719, in command
_mpv_command(self.handle, (c_char_p*len(args))(*args))
File "C:\Users\davfa\AppData\Local\Programs\Python\Python38-32\lib\site-packages\mpv.py", line 126, in raise_for_ec
raise ex(ec, *args)
ValueError: ('Invalid value for mpv parameter', -4, (<MpvHandle object at 0x03256850>, <mpv.c_char_p_Array_6 object at 0x03256898>))
Do you have suggestions? Thank you.
Are you on windows? For me the inbuilt libmpv controls work on Linux but not on Windows. This is the output on windows: Mpv Output when I run the mpv pyqt example from the readme (without the vo='x11' option of course) PyQt Mpv Code. It seems strange that qt would be catching the input on windows but not on linux but who knows. Do you have some ideas how this could be investigated further? I would like to have a more in-depth look at this.
I found a stackoverflow post on keypresses in PyQt, I will try to tweak it to input to mpv. https://stackoverflow.com/q/38507011/
player commands aren't working
self.player.command('seek', '0.2', 'absolute+keyframes')
AttributeError: 'MainWin' object has no attribute 'player'
def __init__(self, parent=None): #, parent=None
super(MainWin, self).__init__(parent) #parent
self.container = QWidget(self)
self.setCentralWidget(self.container)
self.container.setAttribute(Qt.WA_DontCreateNativeAncestors)
self.container.setAttribute(Qt.WA_NativeWindow)
self.setWindowTitle(title)
self.resize(1000, 563)
self.player = mpv.MPV(wid=str(int(self.container.winId())), ytdl=True, player_operation_mode='pseudo-gui',
script_opts='osc-layout=box,osc-seekbarstyle=bar,osc-deadzonesize=0,osc-minmousemove=3',
input_default_bindings=True,
input_vo_keyboard=True,
osc=True) #
self.player.play(url)
#player.wait_for_playback()
del self.player
def keyPressEvent(self, event):
if event.key() == Qt.Key_Q:
print("Quitting")
sys.exit('quit')
elif event.key() == Qt.Key_Right:
self.player.command('seek', '0.2', 'absolute+keyframes')
event.accept()
Sorry in my gist I forgot the options: player_operation_mode='pseudo-gui' , input_default_bindings=True, input_vo_keyboard=True,osc=True
.
But on Linux (Manjaro Linux x86_64, 5.3.18-1-MANJARO), mpv 0.32.0, ffmpeg version: n4.2.2
the inbuilt gui of libmpv is shown without problem and I can interact with it without forwarding any events to mpv. Therefore this might rather be a problem with the libmpv dll on Windows or what do you think?
That seems very likely, but I cannot run any VM because of credential guard (whatever that is).
I'll continue tweaking the code; and if I cannot find a solution then I can mark it up to libmpv on windows
Would love to have an update on this, I really like this module
@reaganiwadha All I can say is that on my (Linux) machine, the example from the README works for me with OP's code, both for keyboard and for mouse input. I only needed to change their custom key binding handler a bit to mesh with both libmpv and PyQt's respective event loops.
If things don't work for you, please post an example script that I can run to reproduce the behavior, post your Python, PyQt and libmpv versions, and tell us what operating system you're running things on. At this point, I suspect that this is a Windows-only issue.
#!/usr/bin/env python3
import mpv
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
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='x11', # You may not need this
loglevel='debug',
script_opts='osc-layout=box,osc-seekbarstyle=bar,osc-deadzonesize=0,osc-minmousemove=3',
input_default_bindings=True,
input_vo_keyboard=True,
osc=True)
@player.on_key_press('esc')
def my_key_binding(*args):
print('ESC pressed')
# Note: We can't just call sys.exit() here because that would only terminate the python interpreter, but not libmpv or PyQt.
player.quit() # Tell libmpv to terminate itself
QApplication.instance().quit() # Tell PyQt to terminate. This closes the window, and makes PyQt return from app.exec below.
player.loop = 'inf'
player.play('tests/test.webm')
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_())
Take this code for instance.
It works pretty well, it plays video in the window and it read the video from the
url
variable and the window title fromtitle
. Yet despite me having set not only the pseudo-gui and keyboard bindings, neither of them initiate so inside my window is video playing with no keyboard or in-window controls. I am relatively new to PyQt, so maybe this is a simple to fix issue. Another oddity with the PyQt integration is that wait_for_playback() is automatic, and actually trying to call it prevents the video from playing.I really like this module, and it is very handy for several of my video projects (ie this)