我正在 QPdfView 中显示 pdf 文档,并且需要在几页上的某些区域上绘制覆盖层。
我已经设法绘制了覆盖层,但我需要一些帮助来调整滚动时其位置的调整方式。
代码如下:
from PySide6.QtCore import QRectF
from PySide6.QtGui import QPainter, QColor
from PySide6.QtPdfWidgets import QPdfView
from PySide6.QtCore import Qt
from PySide6.QtPdf import QPdfDocument
class CustomPdfView(QPdfView):
def __init__(self, parent=None):
super().__init__(parent)
# Placeholder areas
self.highlights: dict[int, list[QRectF]] = {
i : [QRectF(150.0, 150.0, 300.0, 300.0)] for i in range(200)
}
def paintEvent(self, event):
super().paintEvent(event)
if self.document().status() == QPdfDocument.Status.Ready:
current_page_number = self.pageNavigator().currentPage()
if current_page_number in self.highlights.keys():
painter = QPainter(self.viewport())
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
painter.setBrush(QColor(255, 255, 0, 100))
painter.setPen(Qt.PenStyle.NoPen)
total_pages = self.document().pageCount()
current_page_size = self.document().pagePointSize(current_page_number)
viewport_size = self.viewport().size()
scroll_bar = self.verticalScrollBar()
max_scroll = scroll_bar.maximum()
scroll_per_page = max_scroll / total_pages
scroll_calculated = scroll_per_page * current_page_number
for rect in self.highlights[current_page_number]:
h_diff = scroll_bar.value() - scroll_calculated
scaled_rect = QRectF(
(rect.x() / current_page_size.width()) * viewport_size.width(),
rect.y() - h_diff,
(rect.width() / current_page_size.width()) * viewport_size.width(),
rect.height()
)
painter.drawRect(scaled_rect)
painter.end()
在最好的情况下,我想知道当前页面在视口中呈现了多少像素,因此 h_diff 变为 page_height - 可见像素
您的方法存在多个问题,但以下是最重要的问题:
currentPage
导航器的一部分,但在MultiPage
模式下可以同时看到多个页面,“当前”只是与视图上半部分的一条假想线相交的页面(当前,是视口高度的 0.4 处的一条 2 像素的线);FitToWidth
/FitInView
模式的可能缩放;QtPdf 模块相对较新,目前仍基本实现,因此遗憾的是它缺少许多有用的方面(例如了解每个页面的可视边界矩形),这些方面可能需要进一步实现,因此这些方面必须手动处理。
幸运的是,我们可以查看官方来源,我们可以看到它内部根据当前设置创建一个私有的“文档布局”,考虑到视口大小、实际比例、边距和页面间距。它本质上是一个基本映射,每个页面都有一个特定的 QRect 和基于视觉位置的比例。
通过访问该布局,我们可以根据每页的实际坐标正确映射每个“亮点”。
首先,我们需要创建一个计算所有文档布局的函数,并确保只要以下任何方面发生变化,它就会始终更新:
setDocumentMargins()
);SinglePage
模式;resizeEvent()
);布局使用形式为 的映射
{page: (pageRect, scale)}
,并通过循环遍历所有可见页面来计算:对于每个页面,它获取原始大小,缩放它(考虑缩放模式、屏幕 DPI 和可能的自定义缩放系数,或者可能考虑水平边距),然后使用0, 0
具有新大小的默认 QRect 创建上述字典映射的值,并根据新大小和原始大小之间的比率进行缩放。在此过程中,还会考虑最小宽度,同时考虑边距和实际页面宽度。此后,再次循环页面以更新每个 QRect 的水平(基于视口大小和最小宽度居中)和垂直(基于每个页面高度和页面间距)位置。
然后我们显然需要在 中绘制“亮点”
paintEvent()
,它会循环遍历所有布局的页面,检查页面当前是否在视口中,如果它包含亮点,则根据该页面的比例映射亮点矩形:x 和 y 被平移到页面原点,然后,与大小一起乘以页面比例。由于显而易见的原因,我仅尝试根据实际缩放来解决逻辑空间问题(例如:“基于区域”的突出显示):如果您想显示仅考虑其位置缩放而不考虑其大小的“注释”,则需要进一步实施。
以下代码是一个综合示例,它使用
updateDocumentLayout
基于上面链接的源的函数(calculateDocumentLayout()
):最后说明
如上所述,QtPdf 模块处于相对早期的阶段(即使基本类至少可以追溯到 2018 年)并且肯定不是 Qt 开发的优先事项:我几个月前提交了一些相关报告,即使它们可能被认为非常相关,但尚未得到解决。
这意味着上述代码所做的任何事情都是基于可能(并且希望)在未来的 Qt 版本中发生变化的实现,从而可能会破坏结果。