9702
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
1
|
# -*- coding: utf-8 -*- |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
2
|
|
11090
|
3
|
# Copyright (c) 2023 - 2025 Detlev Offenbach <detlev@die-offenbachs.de> |
9702
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
4
|
# |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
5
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
6
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
7
|
Module implementing a Table of Contents viewer widget. |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
8
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
9
|
|
9722
|
10
|
from PyQt6.QtCore import QModelIndex, QSortFilterProxyModel, Qt, pyqtSignal, pyqtSlot |
9702
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
11
|
from PyQt6.QtPdf import QPdfBookmarkModel, QPdfDocument |
9722
|
12
|
from PyQt6.QtWidgets import QLabel, QLineEdit, QTreeView, QVBoxLayout, QWidget |
9702
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
13
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
14
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
15
|
class PdfToCModel(QPdfBookmarkModel): |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
16
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
17
|
Class implementing a TOC model with page numbers. |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
18
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
19
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
20
|
def __init__(self, parent): |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
21
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
22
|
Constructor |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
23
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
24
|
@param parent DESCRIPTION |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
25
|
@type TYPE |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
26
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
27
|
super().__init__(parent) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
28
|
|
10683
779cda568acb
Changed the source code and the source code documentation to improve the indication of unused method/function arguments.
Detlev Offenbach <detlev@die-offenbachs.de>
diff
changeset
|
29
|
def columnCount(self, _index): |
9702
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
30
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
31
|
Public method to define the number of columns to be shown. |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
32
|
|
10683
779cda568acb
Changed the source code and the source code documentation to improve the indication of unused method/function arguments.
Detlev Offenbach <detlev@die-offenbachs.de>
diff
changeset
|
33
|
@param _index index of the element (unused) |
9702
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
34
|
@type QModelIndex |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
35
|
@return column count (always 2) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
36
|
@rtype int |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
37
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
38
|
return 2 |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
39
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
40
|
def data(self, index, role): |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
41
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
42
|
Public method to return the requested data. |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
43
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
44
|
@param index index of the element |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
45
|
@type QModelIndex |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
46
|
@param role data role |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
47
|
@type Qt.ItemDataRole |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
48
|
@return requested data |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
49
|
@rtype Any |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
50
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
51
|
if not index.isValid(): |
9722
|
52
|
return None |
9702
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
53
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
54
|
if index.column() == 1: |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
55
|
if role == Qt.ItemDataRole.DisplayRole: |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
56
|
page = index.data(QPdfBookmarkModel.Role.Page.value) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
57
|
return self.document().pageLabel(page) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
58
|
elif role == Qt.ItemDataRole.TextAlignmentRole: |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
59
|
return Qt.AlignmentFlag.AlignRight |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
60
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
61
|
return super().data(index, role) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
62
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
63
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
64
|
class PdfToCWidget(QWidget): |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
65
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
66
|
Class implementing a Table of Contents viewer widget. |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
67
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
68
|
@signal topicActivated(page, zoomFactor) emitted to navigate to the selected topic |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
69
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
70
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
71
|
topicActivated = pyqtSignal(int, float) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
72
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
73
|
def __init__(self, document, parent=None): |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
74
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
75
|
Constructor |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
76
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
77
|
@param document reference to the PDF document object |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
78
|
@type QPdfDocument |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
79
|
@param parent reference to the parent widget (defaults to None) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
80
|
@type QWidget (optional) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
81
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
82
|
super().__init__(parent) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
83
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
84
|
self.__layout = QVBoxLayout(self) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
85
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
86
|
self.__header = QLabel("<h2>{0}</h2>".format(self.tr("Contents"))) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
87
|
self.__header.setAlignment(Qt.AlignmentFlag.AlignCenter) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
88
|
self.__layout.addWidget(self.__header) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
89
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
90
|
self.__searchEdit = QLineEdit(self) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
91
|
self.__searchEdit.setPlaceholderText(self.tr("Search ...")) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
92
|
self.__searchEdit.setClearButtonEnabled(True) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
93
|
self.__layout.addWidget(self.__searchEdit) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
94
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
95
|
self.__tocWidget = QTreeView(self) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
96
|
self.__tocWidget.setHeaderHidden(True) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
97
|
self.__tocWidget.setExpandsOnDoubleClick(False) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
98
|
self.__tocModel = PdfToCModel(self) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
99
|
self.__tocModel.setDocument(document) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
100
|
self.__tocFilterModel = QSortFilterProxyModel(self) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
101
|
self.__tocFilterModel.setRecursiveFilteringEnabled(True) |
9704
|
102
|
self.__tocFilterModel.setFilterCaseSensitivity( |
|
103
|
Qt.CaseSensitivity.CaseInsensitive |
|
104
|
) |
9702
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
105
|
self.__tocFilterModel.setSourceModel(self.__tocModel) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
106
|
self.__tocWidget.setModel(self.__tocFilterModel) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
107
|
self.__layout.addWidget(self.__tocWidget) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
108
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
109
|
self.setLayout(self.__layout) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
110
|
|
9704
|
111
|
self.__searchEdit.setEnabled(False) |
|
112
|
self.__tocWidget.setEnabled(False) |
|
113
|
|
9702
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
114
|
self.__tocWidget.activated.connect(self.__topicSelected) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
115
|
document.statusChanged.connect(self.__handleDocumentStatus) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
116
|
self.__searchEdit.textEdited.connect(self.__searchTextChanged) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
117
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
118
|
@pyqtSlot(QModelIndex) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
119
|
def __topicSelected(self, index): |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
120
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
121
|
Private slot to handle the selection of a ToC entry. |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
122
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
123
|
@param index index of the activated entry |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
124
|
@type QModelIndex |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
125
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
126
|
if not index.isValid(): |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
127
|
return |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
128
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
129
|
page = index.data(QPdfBookmarkModel.Role.Page.value) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
130
|
zoomFactor = index.data(QPdfBookmarkModel.Role.Zoom.value) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
131
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
132
|
self.topicActivated.emit(page, zoomFactor) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
133
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
134
|
@pyqtSlot(QPdfDocument.Status) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
135
|
def __handleDocumentStatus(self, status): |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
136
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
137
|
Private slot to handle a change of the document status. |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
138
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
139
|
@param status document status |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
140
|
@type QPdfDocument.Status |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
141
|
""" |
9704
|
142
|
ready = status == QPdfDocument.Status.Ready |
|
143
|
if ready: |
9702
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
144
|
self.__tocWidget.expandAll() |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
145
|
for column in range(self.__tocModel.columnCount(QModelIndex())): |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
146
|
self.__tocWidget.resizeColumnToContents(column) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
147
|
|
9704
|
148
|
self.__searchEdit.setEnabled(ready) |
|
149
|
self.__tocWidget.setEnabled(ready) |
|
150
|
|
9702
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
151
|
@pyqtSlot(str) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
152
|
def __searchTextChanged(self, text): |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
153
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
154
|
Private slot to handle a change of the search text. |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
155
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
156
|
@param text search text |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
157
|
@type str |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
158
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
159
|
self.__tocFilterModel.setFilterWildcard("*{0}*".format(text)) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
160
|
self.__tocWidget.expandAll() |