src/eric7/PdfViewer/PdfToCWidget.py

branch
pdf_viewer
changeset 9702
7c973954919d
child 9704
6e1650b9b3b5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/PdfViewer/PdfToCWidget.py	Mon Jan 16 11:56:23 2023 +0100
@@ -0,0 +1,150 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a Table of Contents viewer widget.
+"""
+
+from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QModelIndex, QSortFilterProxyModel
+from PyQt6.QtPdf import QPdfBookmarkModel, QPdfDocument
+from PyQt6.QtWidgets import QWidget, QVBoxLayout, QLabel, QTreeView, QLineEdit
+
+
+class PdfToCModel(QPdfBookmarkModel):
+    """
+    Class implementing a TOC model with page numbers.
+    """
+
+    def __init__(self, parent):
+        """
+        Constructor
+
+        @param parent DESCRIPTION
+        @type TYPE
+        """
+        super().__init__(parent)
+
+    def columnCount(self, index):
+        """
+        Public method to define the number of columns to be shown.
+
+        @param index index of the element
+        @type QModelIndex
+        @return column count (always 2)
+        @rtype int
+        """
+        return 2
+
+    def data(self, index, role):
+        """
+        Public method to return the requested data.
+
+        @param index index of the element
+        @type QModelIndex
+        @param role data role
+        @type Qt.ItemDataRole
+        @return requested data
+        @rtype Any
+        """
+        if not index.isValid():
+            return
+
+        if index.column() == 1:
+            if role == Qt.ItemDataRole.DisplayRole:
+                page = index.data(QPdfBookmarkModel.Role.Page.value)
+                return self.document().pageLabel(page)
+            elif role == Qt.ItemDataRole.TextAlignmentRole:
+                return Qt.AlignmentFlag.AlignRight
+
+        return super().data(index, role)
+
+
+class PdfToCWidget(QWidget):
+    """
+    Class implementing a Table of Contents viewer widget.
+
+    @signal topicActivated(page, zoomFactor) emitted to navigate to the selected topic
+    """
+
+    topicActivated = pyqtSignal(int, float)
+
+    def __init__(self, document, parent=None):
+        """
+        Constructor
+
+        @param document reference to the PDF document object
+        @type QPdfDocument
+        @param parent reference to the parent widget (defaults to None)
+        @type QWidget (optional)
+        """
+        super().__init__(parent)
+
+        self.__layout = QVBoxLayout(self)
+
+        self.__header = QLabel("<h2>{0}</h2>".format(self.tr("Contents")))
+        self.__header.setAlignment(Qt.AlignmentFlag.AlignCenter)
+        self.__layout.addWidget(self.__header)
+
+        self.__searchEdit = QLineEdit(self)
+        self.__searchEdit.setPlaceholderText(self.tr("Search ..."))
+        self.__searchEdit.setClearButtonEnabled(True)
+        self.__layout.addWidget(self.__searchEdit)
+
+        self.__tocWidget = QTreeView(self)
+        self.__tocWidget.setHeaderHidden(True)
+        self.__tocWidget.setExpandsOnDoubleClick(False)
+        self.__tocModel = PdfToCModel(self)
+        self.__tocModel.setDocument(document)
+        self.__tocFilterModel = QSortFilterProxyModel(self)
+        self.__tocFilterModel.setRecursiveFilteringEnabled(True)
+        self.__tocFilterModel.setSourceModel(self.__tocModel)
+        self.__tocWidget.setModel(self.__tocFilterModel)
+        self.__layout.addWidget(self.__tocWidget)
+
+        self.setLayout(self.__layout)
+
+        self.__tocWidget.activated.connect(self.__topicSelected)
+        document.statusChanged.connect(self.__handleDocumentStatus)
+        self.__searchEdit.textEdited.connect(self.__searchTextChanged)
+
+    @pyqtSlot(QModelIndex)
+    def __topicSelected(self, index):
+        """
+        Private slot to handle the selection of a ToC entry.
+
+        @param index index of the activated entry
+        @type QModelIndex
+        """
+        if not index.isValid():
+            return
+
+        page = index.data(QPdfBookmarkModel.Role.Page.value)
+        zoomFactor = index.data(QPdfBookmarkModel.Role.Zoom.value)
+
+        self.topicActivated.emit(page, zoomFactor)
+
+    @pyqtSlot(QPdfDocument.Status)
+    def __handleDocumentStatus(self, status):
+        """
+        Private slot to handle a change of the document status.
+
+        @param status document status
+        @type QPdfDocument.Status
+        """
+        if status == QPdfDocument.Status.Ready:
+            self.__tocWidget.expandAll()
+            for column in range(self.__tocModel.columnCount(QModelIndex())):
+                self.__tocWidget.resizeColumnToContents(column)
+
+    @pyqtSlot(str)
+    def __searchTextChanged(self, text):
+        """
+        Private slot to handle a change of the search text.
+
+        @param text search text
+        @type str
+        """
+        self.__tocFilterModel.setFilterWildcard("*{0}*".format(text))
+        self.__tocWidget.expandAll()

eric ide

mercurial