Initial prototype

Basic UI outline with keyboard control. Python database
object is exposed to QML and data can be queried from its methods.
This should provide a generic structure that different UI themes
can be built around.
This commit is contained in:
2022-08-15 17:50:09 +00:00
commit 7944e4e21b
14 changed files with 1309 additions and 0 deletions
+87
View File
@@ -0,0 +1,87 @@
from PyQt5.QtCore import QUrl, QSize, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QOpenGLFramebufferObject
from PyQt5.QtOpenGL import QGLContext
from PyQt5.QtQuick import QQuickFramebufferObject
from mpv import MPV, MpvRenderContext, OpenGlCbGetProcAddrFn
def get_process_address(_, name):
glctx = QGLContext.currentContext()
if glctx is None:
return 0
return int(glctx.getProcAddress(name.decode('utf-8')))
class MpvObject(QQuickFramebufferObject):
"""MpvObject:
This is a QML widget that can be used to embed the output of a mpv instance.
It extends the QQuickFramebufferObject class to implement this functionality."""
# This signal allows triggers the update function to run on the correct thread
onUpdate = pyqtSignal()
def __init__(self, parent=None):
print("Creating MpvObject")
super(MpvObject, self).__init__(parent)
self.mpv = MPV(ytdl=True)
self.mpv_gl = None
self._proc_addr_wrapper = OpenGlCbGetProcAddrFn(get_process_address)
self.onUpdate.connect(self.doUpdate)
def on_update(self):
"""Function for mpv to call to trigger a framebuffer update"""
self.onUpdate.emit()
@pyqtSlot()
def doUpdate(self):
"""Slot for receiving the update event on the correct thread"""
self.update()
def createRenderer(self) -> 'QQuickFramebufferObject.Renderer':
"""Overrides the default createRenderer function to create a
MpvRenderer instance"""
print("Calling overridden createRenderer")
return MpvRenderer(self)
@pyqtSlot(str)
def play(self, url):
"""Temporary adapter fuction that allowing playing media from QML"""
self.mpv.play(url)
class MpvRenderer(QQuickFramebufferObject.Renderer):
"""MpvRenderer:
This class implements the QQuickFramebufferObject's Renderer subsystem.
It augments the base renderer with an instance of mpv's render API."""
def __init__(self, parent=None):
print("Creating MpvRenderer")
super(MpvRenderer, self).__init__()
self.obj = parent
self.ctx = None
def createFramebufferObject(self, size: QSize) -> QOpenGLFramebufferObject:
"""Overrides the base createFramebufferObject function, augmenting it to
create an MpvRenderContext using opengl"""
if self.obj.mpv_gl is None:
print("Creating mpv gl")
self.ctx = MpvRenderContext(self.obj.mpv, 'opengl',
opengl_init_params={
'get_proc_address': self.obj._proc_addr_wrapper
})
self.ctx.update_cb = self.obj.on_update
return QQuickFramebufferObject.Renderer.createFramebufferObject(self, size)
def render(self):
"""Overrides the base render function, calling mpv's render functions instead"""
if self.ctx:
factor = self.obj.scale()
rect = self.obj.size()
# width and height are floats
width = int(rect.width() * factor)
height = int(rect.height() * factor)
fbo = int(self.framebufferObject().handle())
self.ctx.render(flip_y=False, opengl_fbo={'w': width, 'h': height, 'fbo': fbo})