QPrintPreviewWidget 在显示水平滚动条时冻结应用程序

问题描述 投票:0回答:1

我在使用 QPrintPreviewWidget(PySide2 5.15.2.1,Windows 11)时遇到错误。从我发这篇文章的那一刻起,我在网上没有找到任何相关内容。

当我强制水平滚动条出现在继承的滚动区域内(通过调整窗口大小)时,应用程序变得无响应,立即冻结,直到您幸运地调整小部件的大小并强制滚动条消失。

这是强制触发错误的示例:

from PySide2.QtWidgets import QApplication, QWidget, QLabel, QHBoxLayout
from PySide2.QtGui import QTextDocument
from PySide2.QtPrintSupport import QPrinter, QPrintPreviewWidget

app = QApplication()

doc = QTextDocument()
doc.setPlainText('\n' * 200)

printer = QPrinter()
view = QPrintPreviewWidget(printer)
view.paintRequested.connect(doc.print_)

window = QWidget()
layout = QHBoxLayout()
layout.addWidget(view)
layout.addWidget(QLabel('Just to trigger the horizontal scrollbar'))
window.setLayout(layout)
window.show()

app.exec_()

我必须创建一个超过一页的文档,才能强制显示垂直滚动条。当水平调整大小时,在某些时候水平滚动条将变得可见并滞后于应用程序。

到目前为止,我发现“修复”该错误的唯一方法是使用 QPrintPreviewWidget.setMinimumWidth 设置 QPrintPreviewWidget 的最小宽度。这可以防止小部件达到较小的宽度,从而避免(但不能解决)问题。

有谁知道解决这个问题的方法吗?

python pyqt5 pyside2 qscrollarea qprinter
1个回答
0
投票

QPrintPreviewWidget 实现了一个内部 QGraphicsView,这意味着这个问题与

fitInView()
警告密切相关:

但请注意,如果新转换切换滚动条的自动状态,从 resizeEvent() 内部调用 fitInView() 可能会导致不需要的调整大小递归。

问题是由于

fitInView()
可能会导致出现 one 滚动条,从而减少视口的可用区域,可能会强制执行另一个调整大小事件,从而导致另一个
fitInView()
调用。
然后,该调用将减小可见场景的矩形比例,以便可能不再需要第一个滚动条,从而导致另一个
fitInView()
调用;因此,我们得到了递归。

长话短说,这个问题没有真正的解决办法。

我们可以直接控制的 QGraphicsViews 可以解决类似的问题(例如,通过延迟

fitInView()
调用并使用智能、准确的递归检查),但这对于像本例中那样由 Qt 内部创建的对象来说是不可行的。

唯一可行且始终安全的解决方案是使滚动条始终可见;它可能有点难看,但它有效:

    graphicsView = view.findChild(QGraphicsView)
    graphicsView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)

如果您不喜欢看到不起作用的滚动条,那么唯一的选择是创建一个“虚拟”滚动条,该滚动条最终仅在必要时才“可见”,同时仍占据其所需的空间。这意味着您将始终在视图底部看到一个空白。 这是一个可能的实现:

class PrintPreviewFix(QPrintPreviewWidget): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) view = self.findChild(QGraphicsView) view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.realScrollBar = view.horizontalScrollBar() self.virtualScrollBar = QScrollBar(Qt.Horizontal, self) pol = self.virtualScrollBar.sizePolicy() pol.setRetainSizeWhenHidden(True) self.virtualScrollBar.setSizePolicy(pol) layout = self.layout() layout.setSpacing(0) layout.addWidget(self.virtualScrollBar) self.realScrollBar.rangeChanged.connect(self.updateScrollBar) self.realScrollBar.valueChanged.connect(self.updateScrollBar) self.virtualScrollBar.valueChanged.connect(self.realScrollBar.setValue) def updateScrollBar(self): minimum = self.realScrollBar.minimum() maximum = self.realScrollBar.maximum() with QSignalBlocker(self.virtualScrollBar): self.virtualScrollBar.setRange(minimum, maximum) self.virtualScrollBar.setValue(self.realScrollBar.value()) self.virtualScrollBar.setVisible(minimum != maximum) def resizeEvent(self, event): super().resizeEvent(event) self.updateScrollBar()

请注意,理论上也可以通过使用现有的滚动条并根据滚动条范围(最小值和最大值是否相同)在其上切换 
setUpdatesEnabled()

来实现,但这种方法最终可能会导致缓冲导致的一些图形伪影。

    

© www.soinside.com 2019 - 2024. All rights reserved.