librarymodel progress
This commit is contained in:
225
main.py
225
main.py
@@ -1,13 +1,19 @@
|
|||||||
import sys
|
|
||||||
import json
|
import json
|
||||||
|
import sys
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from PySide2.QtCore import QAbstractItemModel, QModelIndex, QObject, QUrl, Qt, Signal, Slot
|
from PySide2.QtCore import (
|
||||||
from PySide2.QtGui import QIcon, QImage, QPicture, QPixmap
|
QAbstractItemModel,
|
||||||
from PySide2.QtWebChannel import QWebChannel
|
QModelIndex,
|
||||||
from PySide2.QtWebEngineWidgets import QWebEngineView
|
QObject,
|
||||||
|
QUrl,
|
||||||
|
Qt,
|
||||||
|
Signal,
|
||||||
|
Slot,
|
||||||
|
)
|
||||||
|
|
||||||
from PySide2.QtWidgets import (
|
from PySide2.QtWidgets import (
|
||||||
QApplication,
|
QApplication,
|
||||||
QDockWidget,
|
QDockWidget,
|
||||||
@@ -16,14 +22,21 @@ from PySide2.QtWidgets import (
|
|||||||
QPushButton,
|
QPushButton,
|
||||||
QTreeView,
|
QTreeView,
|
||||||
QVBoxLayout,
|
QVBoxLayout,
|
||||||
QWidget
|
QWidget,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from PySide2.QtGui import QIcon, QImage, QPixmap
|
||||||
|
from PySide2.QtWebChannel import QWebChannel
|
||||||
|
from PySide2.QtWebEngineWidgets import QWebEngineView
|
||||||
|
|
||||||
|
|
||||||
def qurl_from_local(fpath):
|
def qurl_from_local(fpath):
|
||||||
return QUrl.fromLocalFile(str(Path(fpath).absolute()))
|
return QUrl.fromLocalFile(str(Path(fpath).absolute()))
|
||||||
|
|
||||||
|
|
||||||
def file_url_from_local(fpath):
|
def file_url_from_local(fpath):
|
||||||
return f'file://{Path(fpath).absolute()}'
|
return f"file://{Path(fpath).absolute()}"
|
||||||
|
|
||||||
|
|
||||||
class Bridge(QObject):
|
class Bridge(QObject):
|
||||||
@Slot(str, result=str)
|
@Slot(str, result=str)
|
||||||
@@ -32,11 +45,12 @@ class Bridge(QObject):
|
|||||||
|
|
||||||
call_javascript = Signal(str)
|
call_javascript = Signal(str)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TreeItem:
|
class TreeItem:
|
||||||
name: str
|
name: str = "Untitled"
|
||||||
parent: "TreeItem"
|
parent: "TreeItem" = None
|
||||||
children: list["TreeItem"]
|
children: list["TreeItem"] = []
|
||||||
|
|
||||||
def row(self):
|
def row(self):
|
||||||
return self.parent.children.index(self) if self.parent else 0
|
return self.parent.children.index(self) if self.parent else 0
|
||||||
@@ -50,14 +64,54 @@ class TreeItem:
|
|||||||
item.children.append(TreeItem.load(child, item))
|
item.children.append(TreeItem.load(child, item))
|
||||||
return item
|
return item
|
||||||
|
|
||||||
|
|
||||||
class LibraryModel(QAbstractItemModel):
|
class LibraryModel(QAbstractItemModel):
|
||||||
|
UNTITLED_NODE_NAME = "Untitled"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.root = TreeItem.load(
|
self.root = TreeItem.load(["root", [["first", [["second", [["third", []]]]]]]])
|
||||||
['root', [
|
|
||||||
['first', [
|
def index(self, row: int, column: int, parent=QModelIndex()) -> QModelIndex:
|
||||||
['second', [
|
if not self.hasIndex(row, column, parent):
|
||||||
['third', []]]]]]]])
|
return QModelIndex()
|
||||||
|
|
||||||
|
parent_item = parent.internalPointer() if parent.isValid() else self.root
|
||||||
|
|
||||||
|
if len(parent_item.children) > row:
|
||||||
|
return self.createIndex(row, column, parent_item.children[row])
|
||||||
|
|
||||||
|
return QModelIndex()
|
||||||
|
|
||||||
|
def parent(self, index: QModelIndex) -> QModelIndex:
|
||||||
|
if not index.isValid():
|
||||||
|
return QModelIndex()
|
||||||
|
|
||||||
|
child_item = index.internalPointer()
|
||||||
|
parent_item = child_item.parent
|
||||||
|
|
||||||
|
if parent_item == self.root:
|
||||||
|
return QModelIndex()
|
||||||
|
|
||||||
|
return self.createIndex(parent_item.row(), 0, parent_item)
|
||||||
|
|
||||||
|
def flags(self, index: QModelIndex) -> Qt.ItemFlags:
|
||||||
|
if index.isValid():
|
||||||
|
return (
|
||||||
|
Qt.ItemIsEnabled
|
||||||
|
| Qt.ItemIsSelectable
|
||||||
|
| Qt.ItemIsDragEnabled
|
||||||
|
| Qt.ItemIsDropEnabled
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return Qt.ItemIsEnabled
|
||||||
|
|
||||||
|
def rowCount(self, parent=QModelIndex()):
|
||||||
|
parent_item = parent.internalPointer() if parent.isValid() else self.root
|
||||||
|
return len(parent_item.children)
|
||||||
|
|
||||||
|
def columnCount(self, parent=QModelIndex()):
|
||||||
|
return 1
|
||||||
|
|
||||||
def data(self, index: QModelIndex, role: Qt.ItemDataRole) -> Any:
|
def data(self, index: QModelIndex, role: Qt.ItemDataRole) -> Any:
|
||||||
if not index.isValid():
|
if not index.isValid():
|
||||||
@@ -68,20 +122,9 @@ class LibraryModel(QAbstractItemModel):
|
|||||||
return item.name
|
return item.name
|
||||||
elif role == Qt.DecorationRole:
|
elif role == Qt.DecorationRole:
|
||||||
img = QImage()
|
img = QImage()
|
||||||
img.load('test.png')
|
img.load("test.png")
|
||||||
return QIcon(QPixmap.fromImage(img))
|
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):
|
def setData(self, index: QModelIndex, value: str, role: Qt.ItemDataRole):
|
||||||
if not index.isValid():
|
if not index.isValid():
|
||||||
return False
|
return False
|
||||||
@@ -93,47 +136,26 @@ class LibraryModel(QAbstractItemModel):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def index(self, row: int, column: int, parent=QModelIndex()) -> QModelIndex:
|
def insertRows(self, row: int, count: int, parent: QModelIndex) -> bool:
|
||||||
if not self.hasIndex(row, column, parent):
|
item: TreeItem = parent.internalPointer() or self.root
|
||||||
return QModelIndex()
|
self.beginInsertRows(parent, row, row + count)
|
||||||
|
item.children.insert(row, TreeItem(LibraryModel.UNTITLED_NODE_NAME, item, []))
|
||||||
|
self.endInsertRows()
|
||||||
|
return True
|
||||||
|
|
||||||
if not parent.isValid():
|
def removeRows(self, row: int, count: int, parent: QModelIndex) -> bool:
|
||||||
parentItem = self.root
|
item: TreeItem = parent.internalPointer() or self.root
|
||||||
else:
|
self.beginRemoveRows(parent, row, row + count)
|
||||||
parentItem = parent.internalPointer()
|
del item.children[row]
|
||||||
|
self.endRemoveRows()
|
||||||
|
return True
|
||||||
|
|
||||||
if len(parentItem.children) > row:
|
def supportedDragActions(self) -> Qt.DropActions:
|
||||||
return self.createIndex(row, column, parentItem.children[row])
|
return Qt.CopyAction | Qt.MoveAction
|
||||||
else:
|
|
||||||
return QModelIndex()
|
|
||||||
|
|
||||||
def parent(self, index: QModelIndex) -> QModelIndex:
|
def supportedDropActions(self) -> Qt.DropActions:
|
||||||
if not index.isValid():
|
return Qt.CopyAction | Qt.MoveAction
|
||||||
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):
|
class MainWindow(QMainWindow):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -148,18 +170,7 @@ class MainWindow(QMainWindow):
|
|||||||
self.web_view = QWebEngineView()
|
self.web_view = QWebEngineView()
|
||||||
self._layout.addWidget(self.web_view)
|
self._layout.addWidget(self.web_view)
|
||||||
|
|
||||||
self.library_model = LibraryModel()
|
self.dock_widget = QDockWidget("Library")
|
||||||
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_qwidget = QWidget()
|
||||||
self.dock_widget.setWidget(self.dock_qwidget)
|
self.dock_widget.setWidget(self.dock_qwidget)
|
||||||
@@ -167,28 +178,54 @@ class MainWindow(QMainWindow):
|
|||||||
self.dock_layout = QVBoxLayout()
|
self.dock_layout = QVBoxLayout()
|
||||||
self.dock_qwidget.setLayout(self.dock_layout)
|
self.dock_qwidget.setLayout(self.dock_layout)
|
||||||
|
|
||||||
self.new_node = QPushButton("New node")
|
self.library_model = LibraryModel()
|
||||||
|
|
||||||
|
self.library_view = QTreeView()
|
||||||
self.dock_layout.addWidget(self.library_view)
|
self.dock_layout.addWidget(self.library_view)
|
||||||
|
|
||||||
|
self.library_view.setModel(self.library_model)
|
||||||
|
|
||||||
|
self.library_view.setDragEnabled(True)
|
||||||
|
self.library_view.setAcceptDrops(True)
|
||||||
|
self.library_view.setDefaultDropAction(Qt.MoveAction)
|
||||||
|
|
||||||
|
self.library_view.setHeaderHidden(True)
|
||||||
|
|
||||||
|
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.new_node = QPushButton("New node")
|
||||||
self.dock_layout.addWidget(self.new_node)
|
self.dock_layout.addWidget(self.new_node)
|
||||||
self.new_node.clicked.connect(lambda:
|
self.new_node.clicked.connect(
|
||||||
self.library_model.insertRow(0, self.library_view.currentIndex())
|
lambda: self.library_model.insertRow(0, self.library_view.currentIndex())
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.delete_node = QPushButton("Delete node")
|
||||||
|
self.dock_layout.addWidget(self.delete_node)
|
||||||
|
self.delete_node.clicked.connect(
|
||||||
|
lambda: self.library_model.removeRow(
|
||||||
|
self.library_view.currentIndex().row(),
|
||||||
|
self.library_view.currentIndex().parent(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
self.addDockWidget(Qt.LeftDockWidgetArea, self.dock_widget)
|
self.addDockWidget(Qt.LeftDockWidgetArea, self.dock_widget)
|
||||||
|
|
||||||
self.web_view.load(qurl_from_local('pdf.js/build/generic/web/viewer.html'))
|
self.web_view.load(qurl_from_local("pdf.js/build/generic/web/viewer.html"))
|
||||||
|
|
||||||
def on_load():
|
def on_load():
|
||||||
def load_javascript_file(filepath):
|
def load_javascript_file(filepath):
|
||||||
self.web_view.page().runJavaScript(
|
self.web_view.page().runJavaScript(
|
||||||
'var s = document.createElement("script");'
|
'var s = document.createElement("script");'
|
||||||
f's.src="{filepath}";'
|
f's.src="{filepath}";'
|
||||||
'document.body.appendChild(s);'
|
"document.body.appendChild(s);"
|
||||||
)
|
)
|
||||||
|
|
||||||
load_javascript_file('qrc:///qtwebchannel/qwebchannel.js')
|
load_javascript_file("qrc:///qtwebchannel/qwebchannel.js")
|
||||||
load_javascript_file(file_url_from_local('controller.js'))
|
load_javascript_file(file_url_from_local("controller.js"))
|
||||||
|
|
||||||
self.web_view.loadFinished.connect(on_load)
|
self.web_view.loadFinished.connect(on_load)
|
||||||
|
|
||||||
@@ -196,22 +233,29 @@ class MainWindow(QMainWindow):
|
|||||||
self.web_view.page().setWebChannel(self.channel)
|
self.web_view.page().setWebChannel(self.channel)
|
||||||
|
|
||||||
self.bridge = Bridge()
|
self.bridge = Bridge()
|
||||||
self.channel.registerObject('bridge', self.bridge)
|
self.channel.registerObject("bridge", self.bridge)
|
||||||
|
|
||||||
def on_command(command):
|
def on_command(command):
|
||||||
if command['type'] == 'ready':
|
if command["type"] == "ready":
|
||||||
print('webchannel ready')
|
print("webchannel ready")
|
||||||
|
|
||||||
self.bridge.handler = on_command
|
self.bridge.handler = on_command
|
||||||
|
|
||||||
def call_javascript(command):
|
def call_javascript(command):
|
||||||
self.bridge.call_javascript.emit(json.dumps(command))
|
self.bridge.call_javascript.emit(json.dumps(command))
|
||||||
|
|
||||||
self.button = QPushButton('Load PDF')
|
self.button = QPushButton("Load PDF")
|
||||||
self._layout.addWidget(self.button)
|
self._layout.addWidget(self.button)
|
||||||
|
|
||||||
self.button.clicked.connect(lambda:
|
self.button.clicked.connect(
|
||||||
call_javascript({'type': 'load_pdf', 'url': file_url_from_local(QFileDialog.getOpenFileName()[0])}))
|
lambda: call_javascript(
|
||||||
|
{
|
||||||
|
"type": "load_pdf",
|
||||||
|
"url": file_url_from_local(QFileDialog.getOpenFileName()[0]),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
app = QApplication()
|
app = QApplication()
|
||||||
@@ -225,5 +269,6 @@ def main():
|
|||||||
|
|
||||||
sys.exit(app.exec_())
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
Reference in New Issue
Block a user