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
+1
View File
@@ -0,0 +1 @@
__version__ = "0.1.0"
+80
View File
@@ -0,0 +1,80 @@
from typing import Type
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQml import qmlRegisterType, QQmlApplicationEngine
from PyQt5.QtQuick import QQuickView
from PyQt5.QtCore import QObject, QUrl, pyqtProperty
from .qtmpv import MpvObject
import sys
class Provider(QObject):
def __init__(self, name, parent=None):
super().__init__(parent)
self.name = name
# self.ip = ip
# self.port = port
@pyqtProperty("QString")
def Name(self):
return self.name
class DataSource:
def __init__(self, providers=[]):
self.providers = [Provider(name) for name in providers]
def DatabaseType(data_source) -> Type:
class Database(QObject):
def __init__(self, parent=None):
super().__init__(parent)
self.data_source = data_source
@pyqtProperty(list)
def Providers(self):
return self.data_source.providers
return Database
def main():
app = QApplication(sys.argv)
data_source = DataSource(["Anime", "TV", "Movies", "Settings"])
qmlRegisterType(DatabaseType(data_source), "Ikinuki.Client", 1, 0, "Database")
# qmlRegisterType(Provider, "Ikinuki.Client", 1, 0, "Provider")
engine = QQmlApplicationEngine()
engine.load("layouts/ikinuki-default.qml")
win = QObject()
win = engine.rootObjects()[0]
win.show()
sys.exit(app.exec_())
#
#
#
# app = QApplication([])
#
# qmlRegisterType(MpvObject, 'mpvtest', 1, 0, "MpvObject")
#
# window = QQmlApplicationEngine("layouts/mpv.qml")
# window.run
#
# view = QQuickView()
# url = QUrl("layouts/mpv.qml")
#
# import locale
#
# locale.setlocale(locale.LC_NUMERIC, 'C')
#
# view.setSource(url)
# view.show()
# app.exec_()
+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})