matrix-nio backend start, QGuiApplication class
- Started work on the matrix-nio backend, which will be used instead of matrix-python-sdk for greater control and cleaner design - Have an Application (QGuiApplication) class to habdle argument parsing and setting some Qt properties like application name
This commit is contained in:
parent
3b47fee77d
commit
4f9a47027c
|
@ -3,10 +3,11 @@
|
||||||
|
|
||||||
"""<SHORTDESC>"""
|
"""<SHORTDESC>"""
|
||||||
|
|
||||||
__pkg_name__ = "harmonyqml"
|
__pkg_name__ = "harmonyqml"
|
||||||
__version__ = "0.1.0"
|
__pretty_name__ = "Harmony QML"
|
||||||
__status__ = "Development"
|
__version__ = "0.1.0"
|
||||||
# __status__ = "Production"
|
__status__ = "Development"
|
||||||
|
# __status__ = "Production"
|
||||||
|
|
||||||
__author__ = "miruka"
|
__author__ = "miruka"
|
||||||
__email__ = "miruka@disroot.org"
|
__email__ = "miruka@disroot.org"
|
||||||
|
|
|
@ -3,20 +3,10 @@
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from PyQt5.QtGui import QGuiApplication
|
from . import app
|
||||||
|
|
||||||
from .engine import Engine
|
|
||||||
|
|
||||||
# logging.basicConfig(level=logging.INFO)
|
# logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
def run() -> None:
|
def run() -> None:
|
||||||
try:
|
_ = app.Application(sys.argv)
|
||||||
sys.argv.index("--debug")
|
|
||||||
debug = True
|
|
||||||
except ValueError:
|
|
||||||
debug = False
|
|
||||||
|
|
||||||
app = QGuiApplication(sys.argv)
|
|
||||||
engine = Engine(app, debug=debug)
|
|
||||||
engine.show_window()
|
|
||||||
|
|
26
harmonyqml/app.py
Normal file
26
harmonyqml/app.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# Copyright 2019 miruka
|
||||||
|
# This file is part of harmonyqml, licensed under GPLv3.
|
||||||
|
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from PyQt5.QtGui import QGuiApplication
|
||||||
|
|
||||||
|
from . import __about__
|
||||||
|
|
||||||
|
|
||||||
|
class Application(QGuiApplication):
|
||||||
|
def __init__(self, args: Optional[List[str]] = None) -> None:
|
||||||
|
try:
|
||||||
|
args.index("--debug") # type: ignore
|
||||||
|
debug = True
|
||||||
|
except (AttributeError, ValueError):
|
||||||
|
debug = False
|
||||||
|
|
||||||
|
super().__init__(args or [])
|
||||||
|
|
||||||
|
self.setApplicationName(__about__.__pkg_name__)
|
||||||
|
self.setApplicationDisplayName(__about__.__pretty_name__)
|
||||||
|
|
||||||
|
from .engine import Engine
|
||||||
|
engine = Engine(self, debug=debug)
|
||||||
|
engine.show_window()
|
|
@ -4,28 +4,19 @@ from typing import Any, Dict, List, Mapping, Optional, Sequence, Tuple, Union
|
||||||
|
|
||||||
from namedlist import namedlist
|
from namedlist import namedlist
|
||||||
from PyQt5.QtCore import (
|
from PyQt5.QtCore import (
|
||||||
QAbstractListModel, QModelIndex, Qt, pyqtProperty, pyqtSlot, pyqtSignal
|
QAbstractListModel, QModelIndex, Qt, pyqtProperty, pyqtSlot
|
||||||
)
|
)
|
||||||
|
|
||||||
NewValue = Union[Mapping[str, Any], Sequence]
|
NewValue = Union[Mapping[str, Any], Sequence]
|
||||||
|
|
||||||
|
|
||||||
class _QtListModel(QAbstractListModel):
|
class _QtListModel(QAbstractListModel):
|
||||||
updated = pyqtSignal()
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._ref_namedlist = None
|
self._ref_namedlist = None
|
||||||
self._roles: Tuple[str, ...] = ()
|
self._roles: Tuple[str, ...] = ()
|
||||||
self._list: list = []
|
self._list: list = []
|
||||||
|
|
||||||
self._update_counter: int = 0
|
|
||||||
|
|
||||||
for sig in (self.dataChanged, self.layoutChanged, self.modelReset,
|
|
||||||
self.rowsInserted, self.rowsMoved, self.rowsRemoved):
|
|
||||||
sig.connect(self.updated.emit)
|
|
||||||
|
|
||||||
|
|
||||||
def roleNames(self) -> Dict[int, bytes]:
|
def roleNames(self) -> Dict[int, bytes]:
|
||||||
return {Qt.UserRole + i: bytes(f, "utf-8")
|
return {Qt.UserRole + i: bytes(f, "utf-8")
|
||||||
for i, f in enumerate(self._roles, 1)}
|
for i, f in enumerate(self._roles, 1)}
|
||||||
|
@ -70,7 +61,7 @@ class _QtListModel(QAbstractListModel):
|
||||||
|
|
||||||
|
|
||||||
@pyqtSlot(int, result="QVariantMap")
|
@pyqtSlot(int, result="QVariantMap")
|
||||||
def get(self, index: int) -> Optional[Dict[str, Any]]:
|
def get(self, index: int) -> Dict[str, Any]:
|
||||||
return self._list[index]._asdict()
|
return self._list[index]._asdict()
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,7 +73,7 @@ class _QtListModel(QAbstractListModel):
|
||||||
self.endInsertRows()
|
self.endInsertRows()
|
||||||
|
|
||||||
|
|
||||||
@pyqtProperty(int, constant=True)
|
@pyqtProperty(int)
|
||||||
def count(self) -> int:
|
def count(self) -> int:
|
||||||
return self.rowCount()
|
return self.rowCount()
|
||||||
|
|
||||||
|
@ -151,13 +142,6 @@ class _QtListModel(QAbstractListModel):
|
||||||
self.endRemoveRows()
|
self.endRemoveRows()
|
||||||
|
|
||||||
|
|
||||||
@pyqtProperty(int, notify=updated)
|
|
||||||
def reloadThis(self) -> int:
|
|
||||||
# http://www.mardy.it/blog/2016/11/qml-trick-force-re-evaluation-of.html
|
|
||||||
self._update_counter += 1
|
|
||||||
return self._update_counter
|
|
||||||
|
|
||||||
|
|
||||||
class ListModel(MutableSequence):
|
class ListModel(MutableSequence):
|
||||||
def __init__(self, initial_data: Optional[List[NewValue]] = None) -> None:
|
def __init__(self, initial_data: Optional[List[NewValue]] = None) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
4
harmonyqml/backend/matrix_nio/__init__.py
Normal file
4
harmonyqml/backend/matrix_nio/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Copyright 2019 miruka
|
||||||
|
# This file is part of harmonyqml, licensed under GPLv3.
|
||||||
|
|
||||||
|
from .backend import MatrixNioBackend
|
27
harmonyqml/backend/matrix_nio/backend.py
Normal file
27
harmonyqml/backend/matrix_nio/backend.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# Copyright 2019 miruka
|
||||||
|
# This file is part of harmonyqml, licensed under GPLv3.
|
||||||
|
|
||||||
|
from typing import Any, DefaultDict, Dict, NamedTuple, Optional
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QDateTime, QObject, pyqtProperty, pyqtSlot
|
||||||
|
|
||||||
|
from matrix_client.user import User as MatrixUser
|
||||||
|
|
||||||
|
from ..base import Backend, User
|
||||||
|
from .client_manager import ClientManager
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixNioBackend(Backend):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self._client_manager = ClientManager()
|
||||||
|
|
||||||
|
# a = self._client_manager
|
||||||
|
# from PyQt5.QtCore import pyqtRemoveInputHook as PRI; import pdb; PRI(); pdb.set_trace()
|
||||||
|
|
||||||
|
self._client_manager.configLoad()
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtProperty("QVariant")
|
||||||
|
def clientManager(self):
|
||||||
|
return self._client_manager
|
85
harmonyqml/backend/matrix_nio/client.py
Normal file
85
harmonyqml/backend/matrix_nio/client.py
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
# Copyright 2019 miruka
|
||||||
|
# This file is part of harmonyqml, licensed under GPLv3.
|
||||||
|
|
||||||
|
import functools
|
||||||
|
from concurrent.futures import Future, ThreadPoolExecutor
|
||||||
|
from threading import Event
|
||||||
|
from typing import Callable, DefaultDict
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QObject, pyqtSlot
|
||||||
|
|
||||||
|
import nio
|
||||||
|
import nio.responses as nr
|
||||||
|
|
||||||
|
# One pool per hostname/remote server;
|
||||||
|
# multiple Client for different accounts on the same server can exist.
|
||||||
|
_POOLS: DefaultDict[str, ThreadPoolExecutor] = \
|
||||||
|
DefaultDict(lambda: ThreadPoolExecutor(max_workers=6))
|
||||||
|
|
||||||
|
|
||||||
|
def futurize(func: Callable) -> Callable:
|
||||||
|
@functools.wraps(func)
|
||||||
|
def wrapper(*args, **kwargs) -> Future:
|
||||||
|
return args[0].pool.submit(func, *args, **kwargs) # args[0] = self
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class Client(QObject):
|
||||||
|
def __init__(self, hostname: str, username: str, device_id: str = ""
|
||||||
|
) -> None:
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
host, *port = hostname.split(":")
|
||||||
|
self.host: str = host
|
||||||
|
self.port: int = int(port[0]) if port else 443
|
||||||
|
|
||||||
|
self.nio: nio.client.HttpClient = \
|
||||||
|
nio.client.HttpClient(self.host, username, device_id)
|
||||||
|
|
||||||
|
self.pool: ThreadPoolExecutor = _POOLS[self.host]
|
||||||
|
|
||||||
|
from .net import NetworkManager
|
||||||
|
self.net: NetworkManager = NetworkManager(self)
|
||||||
|
|
||||||
|
self._stop_sync: Event = Event()
|
||||||
|
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return "%s(host=%r, port=%r, user_id=%r)" % \
|
||||||
|
(type(self).__name__, self.host, self.port, self.nio.user_id)
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtSlot(str)
|
||||||
|
@pyqtSlot(str, str)
|
||||||
|
@futurize
|
||||||
|
def login(self, password: str, device_name: str = "") -> None:
|
||||||
|
self.net.write(self.nio.connect())
|
||||||
|
self.net.talk(self.nio.login, password, device_name)
|
||||||
|
self.startSyncing()
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtSlot(str, str, str)
|
||||||
|
@futurize
|
||||||
|
def resumeSession(self, user_id: str, token: str, device_id: str
|
||||||
|
) -> None:
|
||||||
|
self.net.write(self.nio.connect())
|
||||||
|
response = nr.LoginResponse(user_id, device_id, token)
|
||||||
|
self.nio.receive_response(response)
|
||||||
|
self.startSyncing()
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
@futurize
|
||||||
|
def logout(self) -> None:
|
||||||
|
self._stop_sync.set()
|
||||||
|
self.net.write(self.nio.disconnect())
|
||||||
|
|
||||||
|
|
||||||
|
@futurize
|
||||||
|
def startSyncing(self) -> None:
|
||||||
|
while True:
|
||||||
|
print(self, self.net.talk(self.nio.sync, timeout=10))
|
||||||
|
|
||||||
|
if self._stop_sync.is_set():
|
||||||
|
self._stop_sync.clear()
|
||||||
|
break
|
147
harmonyqml/backend/matrix_nio/client_manager.py
Normal file
147
harmonyqml/backend/matrix_nio/client_manager.py
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
# Copyright 2018 miruka
|
||||||
|
# This file is part of harmonyqt, licensed under GPLv3.
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import threading
|
||||||
|
from concurrent.futures import Future
|
||||||
|
from typing import Dict, Optional
|
||||||
|
|
||||||
|
from atomicfile import AtomicFile
|
||||||
|
from PyQt5.QtCore import QObject, QStandardPaths, pyqtProperty, pyqtSlot
|
||||||
|
|
||||||
|
from harmonyqml import __about__
|
||||||
|
|
||||||
|
from .client import Client
|
||||||
|
|
||||||
|
AccountConfig = Dict[str, Dict[str, str]]
|
||||||
|
|
||||||
|
_CONFIG_LOCK = threading.Lock()
|
||||||
|
# _CRYPT_DB_LOCK = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
|
class ClientManager(QObject):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self._clients: Dict[str, Client] = {}
|
||||||
|
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"{type(self).__name__}(clients={self.clients!r})"
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtProperty("QVariantMap")
|
||||||
|
def clients(self):
|
||||||
|
return self._clients
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def configLoad(self) -> None:
|
||||||
|
for user_id, info in self.configAccounts().items():
|
||||||
|
cli = Client(info["hostname"], user_id)
|
||||||
|
|
||||||
|
def on_done(_: Future, cli=cli) -> None:
|
||||||
|
self._clients[cli.nio.user_id] = cli
|
||||||
|
|
||||||
|
cli.resumeSession(user_id, info["token"], info["device_id"])\
|
||||||
|
.add_done_callback(on_done)
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtSlot(str, str, str)
|
||||||
|
@pyqtSlot(str, str, str)
|
||||||
|
def new(self, hostname: str, username: str, password: str,
|
||||||
|
device_id: str = "") -> None:
|
||||||
|
|
||||||
|
cli = Client(hostname, username, device_id)
|
||||||
|
|
||||||
|
def on_done(_: Future, cli=cli) -> None:
|
||||||
|
self._clients[cli.nio.user_id] = cli
|
||||||
|
|
||||||
|
cli.login(password, self.defaultDeviceName).add_done_callback(on_done)
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtSlot(str)
|
||||||
|
def delete(self, user_id: str) -> None:
|
||||||
|
client = self._clients.pop(user_id, None)
|
||||||
|
if client:
|
||||||
|
client.logout()
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtProperty(str)
|
||||||
|
def defaultDeviceName(self) -> str: # pylint: disable=no-self-use
|
||||||
|
os_ = f" on {platform.system()}".rstrip()
|
||||||
|
os_ = f"{os_} {platform.release()}".rstrip() if os_ != " on" else ""
|
||||||
|
return f"{__about__.__pretty_name__}{os}"
|
||||||
|
|
||||||
|
|
||||||
|
# Standard file paths
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_standard_path(kind: QStandardPaths.StandardLocation,
|
||||||
|
file: str,
|
||||||
|
initial_content: Optional[str] = None) -> str:
|
||||||
|
relative_path = file.replace("/", os.sep)
|
||||||
|
|
||||||
|
path = QStandardPaths.locate(kind, relative_path)
|
||||||
|
if path:
|
||||||
|
return path
|
||||||
|
|
||||||
|
base_dir = QStandardPaths.writableLocation(kind)
|
||||||
|
path = f"{base_dir}{os.sep}{relative_path}"
|
||||||
|
os.makedirs(os.path.split(path)[0], exist_ok=True)
|
||||||
|
|
||||||
|
if initial_content is not None:
|
||||||
|
with AtomicFile(path, "w") as new:
|
||||||
|
new.write(initial_content)
|
||||||
|
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def getAccountConfigPath(self) -> str:
|
||||||
|
return self._get_standard_path(
|
||||||
|
QStandardPaths.AppConfigLocation, "accounts.json", "[]"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def getCryptDBPath(self, user_id: str) -> str:
|
||||||
|
safe_filename = hashlib.md5(user_id.encode("utf-8")).hexdigest()
|
||||||
|
return self._get_standard_path(
|
||||||
|
QStandardPaths.AppDataLocation, f"encryption/{safe_filename}.db"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Config file operations
|
||||||
|
|
||||||
|
def configAccounts(self) -> AccountConfig:
|
||||||
|
with open(self.getAccountConfigPath(), "r") as file:
|
||||||
|
return json.loads(file.read().strip()) or {}
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtSlot(Client)
|
||||||
|
def configAdd(self, client: Client) -> None:
|
||||||
|
self._write_config({
|
||||||
|
**self.configAccounts(),
|
||||||
|
**{client.nio.user_id: {
|
||||||
|
"hostname": client.nio.host,
|
||||||
|
"token": client.nio.access_token,
|
||||||
|
"device_id": client.nio.device_id,
|
||||||
|
}}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtSlot(str)
|
||||||
|
def configDelete(self, user_id: str) -> None:
|
||||||
|
self._write_config({
|
||||||
|
uid: info
|
||||||
|
for uid, info in self.configAccounts().items() if uid != user_id
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def _write_config(self, accounts: AccountConfig) -> None:
|
||||||
|
js = json.dumps(accounts, indent=4, ensure_ascii=False, sort_keys=True)
|
||||||
|
|
||||||
|
with _CONFIG_LOCK:
|
||||||
|
with AtomicFile(self.getAccountConfigPath(), "w") as new:
|
||||||
|
new.write(js)
|
95
harmonyqml/backend/matrix_nio/net.py
Normal file
95
harmonyqml/backend/matrix_nio/net.py
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
# Copyright 2019 miruka
|
||||||
|
# This file is part of harmonyqml, licensed under GPLv3.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import socket
|
||||||
|
import ssl
|
||||||
|
import time
|
||||||
|
from typing import Callable, Optional, Tuple
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
import nio.responses as nr
|
||||||
|
|
||||||
|
from .client import Client
|
||||||
|
|
||||||
|
OptSock = Optional[ssl.SSLSocket]
|
||||||
|
NioRequestFunc = Callable[..., Tuple[UUID, bytes]]
|
||||||
|
|
||||||
|
|
||||||
|
class NioErrorResponse(Exception):
|
||||||
|
def __init__(self, response: nr.ErrorResponse) -> None:
|
||||||
|
self.response = response
|
||||||
|
super().__init__(str(response))
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkManager:
|
||||||
|
def __init__(self, client: Client) -> None:
|
||||||
|
self.client = client
|
||||||
|
self._ssl_context: ssl.SSLContext = ssl.create_default_context()
|
||||||
|
self._ssl_session: Optional[ssl.SSLSession] = None
|
||||||
|
|
||||||
|
|
||||||
|
def _get_socket(self) -> ssl.SSLSocket:
|
||||||
|
sock = self._ssl_context.wrap_socket( # type: ignore
|
||||||
|
socket.create_connection((self.client.host, self.client.port)),
|
||||||
|
server_hostname = self.client.host,
|
||||||
|
session = self._ssl_session,
|
||||||
|
)
|
||||||
|
self._ssl_session = self._ssl_session or sock.session
|
||||||
|
return sock
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _close_socket(sock: socket.socket) -> None:
|
||||||
|
sock.shutdown(how=socket.SHUT_RDWR)
|
||||||
|
sock.close()
|
||||||
|
|
||||||
|
|
||||||
|
def read(self, with_sock: OptSock = None) -> nr.Response:
|
||||||
|
sock = with_sock or self._get_socket()
|
||||||
|
|
||||||
|
response = None
|
||||||
|
while not response:
|
||||||
|
self.client.nio.receive(sock.recv(4096))
|
||||||
|
response = self.client.nio.next_response()
|
||||||
|
|
||||||
|
if isinstance(response, nr.ErrorResponse):
|
||||||
|
raise NioErrorResponse(response)
|
||||||
|
|
||||||
|
if not with_sock:
|
||||||
|
self._close_socket(sock)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def write(self, data: bytes, with_sock: OptSock = None) -> None:
|
||||||
|
sock = with_sock or self._get_socket()
|
||||||
|
sock.sendall(data)
|
||||||
|
|
||||||
|
if not with_sock:
|
||||||
|
self._close_socket(sock)
|
||||||
|
|
||||||
|
|
||||||
|
def talk(self, nio_func: NioRequestFunc, *args, **kwargs) -> nr.Response:
|
||||||
|
while True:
|
||||||
|
to_send = nio_func(*args, **kwargs)[1]
|
||||||
|
sock = self._get_socket()
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.write(to_send, sock)
|
||||||
|
response = self.read(sock)
|
||||||
|
|
||||||
|
except NioErrorResponse as err:
|
||||||
|
logging.error("read bad response: %s", err)
|
||||||
|
self._close_socket(sock)
|
||||||
|
time.sleep(10)
|
||||||
|
|
||||||
|
except Exception as err:
|
||||||
|
logging.error("talk exception: %r", err)
|
||||||
|
break
|
||||||
|
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
self._close_socket(sock)
|
||||||
|
return response
|
|
@ -24,10 +24,10 @@ Controls1.SplitView {
|
||||||
}
|
}
|
||||||
|
|
||||||
id: "pageStack"
|
id: "pageStack"
|
||||||
initialItem: Chat.Root {
|
// initialItem: Chat.Root {
|
||||||
user: Backend.accountsModel.get(0)
|
//user: Backend.accountsModel.get(0)
|
||||||
room: Backend.roomsModel[Backend.accountsModel.get(0).user_id].get(0)
|
//room: Backend.roomsModel[Backend.accountsModel.get(0).user_id].get(0)
|
||||||
}
|
//}
|
||||||
|
|
||||||
onCurrentItemChanged: currentItem.forceActiveFocus()
|
onCurrentItemChanged: currentItem.forceActiveFocus()
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ ApplicationWindow {
|
||||||
visible: true
|
visible: true
|
||||||
width: 640
|
width: 640
|
||||||
height: 700
|
height: 700
|
||||||
title: "Harmony QML"
|
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
|
@ -7,23 +7,23 @@ from pathlib import Path
|
||||||
from typing import Generator, Optional
|
from typing import Generator, Optional
|
||||||
|
|
||||||
from PyQt5.QtCore import QFileSystemWatcher, QObject, QTimer
|
from PyQt5.QtCore import QFileSystemWatcher, QObject, QTimer
|
||||||
from PyQt5.QtGui import QGuiApplication
|
|
||||||
from PyQt5.QtQml import QQmlApplicationEngine
|
from PyQt5.QtQml import QQmlApplicationEngine
|
||||||
|
|
||||||
from .__about__ import __doc__
|
from .__about__ import __doc__
|
||||||
from .backend import DummyBackend
|
from .app import Application
|
||||||
|
from .backend.matrix_nio.backend import MatrixNioBackend as CurrentBackend
|
||||||
|
|
||||||
# logging.basicConfig(level=logging.INFO)
|
# logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
class Engine(QQmlApplicationEngine):
|
class Engine(QQmlApplicationEngine):
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
app: QGuiApplication,
|
app: Application,
|
||||||
debug: bool = False,
|
debug: bool = False,
|
||||||
parent: Optional[QObject] = None) -> None:
|
parent: Optional[QObject] = None) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.app = app
|
self.app = app
|
||||||
self.backend = DummyBackend()
|
self.backend = CurrentBackend()
|
||||||
self.app_dir = Path(sys.argv[0]).resolve().parent
|
self.app_dir = Path(sys.argv[0]).resolve().parent
|
||||||
|
|
||||||
# Set QML properties
|
# Set QML properties
|
||||||
|
|
Loading…
Reference in New Issue
Block a user