From 904e3ca57ed7b59e122697c8593ac9487bb61129 Mon Sep 17 00:00:00 2001 From: olari Date: Sat, 12 Jun 2021 18:58:10 +0300 Subject: [PATCH] librarymodel progress --- main.py | 257 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 151 insertions(+), 106 deletions(-) diff --git a/main.py b/main.py index 87a03b0..a209e16 100644 --- a/main.py +++ b/main.py @@ -1,13 +1,19 @@ -import sys import json +import sys 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.QtCore import ( + QAbstractItemModel, + QModelIndex, + QObject, + QUrl, + Qt, + Signal, + Slot, +) + from PySide2.QtWidgets import ( QApplication, QDockWidget, @@ -16,15 +22,22 @@ from PySide2.QtWidgets import ( QPushButton, QTreeView, 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): return QUrl.fromLocalFile(str(Path(fpath).absolute())) + def file_url_from_local(fpath): - return f'file://{Path(fpath).absolute()}' - + return f"file://{Path(fpath).absolute()}" + + class Bridge(QObject): @Slot(str, result=str) def call_python(self, data): @@ -32,11 +45,12 @@ class Bridge(QObject): call_javascript = Signal(str) + @dataclass class TreeItem: - name: str - parent: "TreeItem" - children: list["TreeItem"] + name: str = "Untitled" + parent: "TreeItem" = None + children: list["TreeItem"] = [] def row(self): return self.parent.children.index(self) if self.parent else 0 @@ -50,14 +64,54 @@ class TreeItem: item.children.append(TreeItem.load(child, item)) return item + class LibraryModel(QAbstractItemModel): + UNTITLED_NODE_NAME = "Untitled" + def __init__(self): super().__init__() - self.root = TreeItem.load( - ['root', [ - ['first', [ - ['second', [ - ['third', []]]]]]]]) + self.root = TreeItem.load(["root", [["first", [["second", [["third", []]]]]]]]) + + def index(self, row: int, column: int, parent=QModelIndex()) -> QModelIndex: + if not self.hasIndex(row, column, parent): + 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: if not index.isValid(): @@ -68,20 +122,9 @@ class LibraryModel(QAbstractItemModel): return item.name elif role == Qt.DecorationRole: img = QImage() - img.load('test.png') + 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 @@ -93,73 +136,41 @@ class LibraryModel(QAbstractItemModel): return True - def index(self, row: int, column: int, parent=QModelIndex()) -> QModelIndex: - if not self.hasIndex(row, column, parent): - return QModelIndex() + def insertRows(self, row: int, count: int, parent: QModelIndex) -> bool: + item: TreeItem = parent.internalPointer() or self.root + self.beginInsertRows(parent, row, row + count) + item.children.insert(row, TreeItem(LibraryModel.UNTITLED_NODE_NAME, item, [])) + self.endInsertRows() + return True - if not parent.isValid(): - parentItem = self.root - else: - parentItem = parent.internalPointer() + def removeRows(self, row: int, count: int, parent: QModelIndex) -> bool: + item: TreeItem = parent.internalPointer() or self.root + self.beginRemoveRows(parent, row, row + count) + del item.children[row] + self.endRemoveRows() + return True - if len(parentItem.children) > row: - return self.createIndex(row, column, parentItem.children[row]) - else: - return QModelIndex() + def supportedDragActions(self) -> Qt.DropActions: + return Qt.CopyAction | Qt.MoveAction - def parent(self, index: QModelIndex) -> QModelIndex: - if not index.isValid(): - return QModelIndex() + def supportedDropActions(self) -> Qt.DropActions: + return Qt.CopyAction | Qt.MoveAction - 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_widget = QDockWidget("Library") self.dock_qwidget = QWidget() self.dock_widget.setWidget(self.dock_qwidget) @@ -167,55 +178,88 @@ class MainWindow(QMainWindow): self.dock_layout = QVBoxLayout() 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.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.new_node.clicked.connect(lambda: - self.library_model.insertRow(0, self.library_view.currentIndex()) + self.new_node.clicked.connect( + 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.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 load_javascript_file(filepath): self.web_view.page().runJavaScript( 'var s = document.createElement("script");' f's.src="{filepath}";' - 'document.body.appendChild(s);' + "document.body.appendChild(s);" ) - - load_javascript_file('qrc:///qtwebchannel/qwebchannel.js') - load_javascript_file(file_url_from_local('controller.js')) - + + 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) - + self.channel.registerObject("bridge", self.bridge) + def on_command(command): - if command['type'] == 'ready': - print('webchannel ready') - + 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.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])})) - + + 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) @@ -225,5 +269,6 @@ def main(): sys.exit(app.exec_()) -if __name__ == '__main__': - main() \ No newline at end of file + +if __name__ == "__main__": + main()