我在使用 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 的最小宽度。这可以防止小部件达到较小的宽度,从而避免(但不能解决)问题。
有谁知道解决这个问题的方法吗?
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()
来实现,但这种方法最终可能会导致缓冲导致的一些图形伪影。