add main window; webview; webchannel; pdf.js; wip sidebar

This commit is contained in:
olari
2021-06-11 21:47:05 +03:00
commit 1c24573b34
6 changed files with 274 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.vscode

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "pdf.js"]
path = pdf.js
url = https://github.com/mozilla/pdf.js.git

40
controller.js Normal file
View File

@@ -0,0 +1,40 @@
const h = (name, props = {}, ...children) => {
const element = document.createElement(name);
for (const [key, value] of Object.entries(props))
key.startsWith('on')
? element.addEventListener(key.substring(2), value)
: element.setAttribute(key, value);
for (const child of children)
element.appendChild(
typeof(child) === 'string'
? document.createTextNode(child)
: child);
return element;
};
const qs = s => document.querySelector(s);
const qsa = s => Array.from(document.querySelectorAll(s))
const on_command = command => {
switch(command.type) {
case 'alert': alert(command.message); break;
case 'load_pdf': pdfjsLib.getDocument(command.url).promise.then(pdf => PDFViewerApplication.load(pdf)); break;
}
};
let call_python;
new QWebChannel(qt.webChannelTransport, channel => {
call_python = (argument, callback) =>
channel.objects.bridge.call_python(JSON.stringify(argument),
result => callback && callback(JSON.parse(result)));
channel.objects.bridge.call_javascript.connect(
result => on_command(JSON.parse(result)));
call_python({'type': 'ready'});
});

229
main.py Normal file
View File

@@ -0,0 +1,229 @@
import sys
import json
from dataclasses import dataclass
from pathlib import Path
from typing import Any
from PySide2.QtCore import QAbstractItemModel, QModelIndex, QObject, QUrl, Qt, Signal, Slot
from PySide2.QtGui import QIcon, QImage, QPicture, QPixmap
from PySide2.QtWebChannel import QWebChannel
from PySide2.QtWebEngineWidgets import QWebEngineView
from PySide2.QtWidgets import (
QApplication,
QDockWidget,
QFileDialog,
QMainWindow,
QPushButton,
QTreeView,
QVBoxLayout,
QWidget
)
def qurl_from_local(fpath):
return QUrl.fromLocalFile(str(Path(fpath).absolute()))
def file_url_from_local(fpath):
return f'file://{Path(fpath).absolute()}'
class Bridge(QObject):
@Slot(str, result=str)
def call_python(self, data):
return json.dumps(self.handler(json.loads(data)))
call_javascript = Signal(str)
@dataclass
class TreeItem:
name: str
parent: "TreeItem"
children: list["TreeItem"]
def row(self):
return self.parent.children.index(self) if self.parent else 0
@staticmethod
def load(value, parent=None):
name, children = value
item = TreeItem(name, parent, [])
for child in children:
if len(child) == 2:
item.children.append(TreeItem.load(child, item))
return item
class LibraryModel(QAbstractItemModel):
def __init__(self):
super().__init__()
self.root = TreeItem.load(
['root', [
['first', [
['second', [
['third', []]]]]]]])
def data(self, index: QModelIndex, role: Qt.ItemDataRole) -> Any:
if not index.isValid():
return None
if role == Qt.DisplayRole or role == Qt.EditRole:
item = index.internalPointer()
return item.name
elif role == Qt.DecorationRole:
img = QImage()
img.load('test.png')
return QIcon(QPixmap.fromImage(img))
def insertRows(self, row: int, count: int, parent: QModelIndex) -> bool:
item: TreeItem = parent.internalPointer()
self.beginInsertRows(parent, row, row)
item.children.insert(row, TreeItem('', item, []))
self.endInsertRows()
return True
def setData(self, index: QModelIndex, value: str, role: Qt.ItemDataRole):
if not index.isValid():
return False
item: TreeItem = index.internalPointer()
item.name = value
self.dataChanged.emit(index, index, Qt.EditRole)
return True
def index(self, row: int, column: int, parent=QModelIndex()) -> QModelIndex:
if not self.hasIndex(row, column, parent):
return QModelIndex()
if not parent.isValid():
parentItem = self.root
else:
parentItem = parent.internalPointer()
if len(parentItem.children) > row:
return self.createIndex(row, column, parentItem.children[row])
else:
return QModelIndex()
def parent(self, index: QModelIndex) -> QModelIndex:
if not index.isValid():
return QModelIndex()
childItem = index.internalPointer()
parentItem = childItem.parent
if parentItem == self.root:
return QModelIndex()
return self.createIndex(parentItem.row(), 0, parentItem)
def rowCount(self, parent=QModelIndex()):
if not parent.isValid():
parentItem = self.root
else:
parentItem = parent.internalPointer()
return len(parentItem.children)
def columnCount(self, parent=QModelIndex()):
return 1
def flags(self, index: QModelIndex) -> Qt.ItemFlags:
flags = super().flags(index)
return Qt.ItemIsEditable | flags
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.widget = QWidget()
self.setCentralWidget(self.widget)
self._layout = QVBoxLayout()
self.widget.setLayout(self._layout)
self.web_view = QWebEngineView()
self._layout.addWidget(self.web_view)
self.library_model = LibraryModel()
self.library_view = QTreeView()
self.library_view.setHeaderHidden(True)
self.library_view.setModel(self.library_model)
def on_insert_row(p: QModelIndex, f, l):
self.library_view.setExpanded(p, True)
self.library_view.edit(p.child(f, 0))
self.library_model.rowsInserted.connect(on_insert_row)
self.dock_widget = QDockWidget('Library')
self.dock_qwidget = QWidget()
self.dock_widget.setWidget(self.dock_qwidget)
self.dock_layout = QVBoxLayout()
self.dock_qwidget.setLayout(self.dock_layout)
self.new_node = QPushButton("New node")
self.dock_layout.addWidget(self.library_view)
self.dock_layout.addWidget(self.new_node)
self.new_node.clicked.connect(lambda:
self.library_model.insertRow(0, self.library_view.currentIndex())
)
self.addDockWidget(Qt.LeftDockWidgetArea, self.dock_widget)
self.web_view.load(qurl_from_local('pdf.js/build/generic/web/viewer.html'))
def on_load():
def load_javascript_file(filepath):
self.web_view.page().runJavaScript(
'var s = document.createElement("script");'
f's.src="{filepath}";'
'document.body.appendChild(s);'
)
load_javascript_file('qrc:///qtwebchannel/qwebchannel.js')
load_javascript_file(file_url_from_local('controller.js'))
self.web_view.loadFinished.connect(on_load)
self.channel = QWebChannel()
self.web_view.page().setWebChannel(self.channel)
self.bridge = Bridge()
self.channel.registerObject('bridge', self.bridge)
def on_command(command):
if command['type'] == 'ready':
print('webchannel ready')
self.bridge.handler = on_command
def call_javascript(command):
self.bridge.call_javascript.emit(json.dumps(command))
self.button = QPushButton('Load PDF')
self._layout.addWidget(self.button)
self.button.clicked.connect(lambda:
call_javascript({'type': 'load_pdf', 'url': file_url_from_local(QFileDialog.getOpenFileName()[0])}))
def main():
app = QApplication()
window = MainWindow()
availableGeometry = app.desktop().availableGeometry(window)
window.resize(availableGeometry.width() * 2 / 3, availableGeometry.height() * 2 / 3)
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

1
pdf.js Submodule

Submodule pdf.js added at 7b4fa0a038

BIN
test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB