最初在 QGraphics 中移动滚动条
Posted
技术标签:
【中文标题】最初在 QGraphics 中移动滚动条【英文标题】:Initially moved scroll bar inside QGraphics 【发布时间】:2019-09-08 22:25:32 【问题描述】:我正在尝试设置最初在 QGraphicsScene 小部件内移动的垂直和水平滚动条。以下代码应该移动条形并将它们设置在中间,但它们不会移动:
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
class Diedrico(QtWidgets.QWidget):
def paintEvent(self, event):
qp = QtGui.QPainter(self)
pen = QtGui.QPen(QtGui.QColor(QtCore.Qt.black), 5)
qp.setPen(pen)
qp.drawRect(500, 500, 1000, 1000)
class UiVentana(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(UiVentana, self).__init__(parent)
self.resize(1000, 1000)
self.setFixedSize(1000, 1000)
self.scene = QtWidgets.QGraphicsScene(self)
self.view = QtWidgets.QGraphicsView(self.scene)
# This two lines should move the scroll bar
self.view.verticalScrollBar().setValue(500)
self.view.horizontalScrollBar().setValue(500)
self.diedrico = Diedrico()
self.diedrico.setFixedSize(2000, 2000)
self.scene.addWidget(self.diedrico)
self.setCentralWidget(self.view)
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_R:
self.view.setTransform(QtGui.QTransform())
elif event.key() == QtCore.Qt.Key_Plus:
scale_tr = QtGui.QTransform()
scale_tr.scale(1.5, 1.5)
tr = self.view.transform() * scale_tr
self.view.setTransform(tr)
elif event.key() == QtCore.Qt.Key_Minus:
scale_tr = QtGui.QTransform()
scale_tr.scale(1.5, 1.5)
scale_inverted, invertible = scale_tr.inverted()
if invertible:
tr = self.view.transform() * scale_inverted
self.view.setTransform(tr)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ui = UiVentana()
ui.show()
sys.exit(app.exec_())
当我使用滚动区域时,我可以移动条形图,例如 question
【问题讨论】:
【参考方案1】:@S.Nick 给出的答案很好,但我想补充一些关于您为什么会面临这个问题以及“幕后”发生的事情的见解。
首先,在您的代码中,您尝试在将任何对象添加到场景之前设置滚动条的值。
那时,您刚刚创建了视图和场景。视图小部件尚未显示(因此它还不“知道”它的实际大小),并且场景是空的,这意味着 sceneRect
为 null,如 0 宽度和 0高度:在这种情况下,滚动条的最大值为 0,设置任何值都不会产生任何结果。
注意:有一个非常需要牢记的重要方面:除非 明确声明或请求,
sceneRect
QGraphicsScene 总是 null 直到视图显示它。并且通过 “请求”我的意思是即使只是打电话给scene.sceneRect()
足以确保场景实际上并最终“知道”它 范围。
尝试设置滚动条后(没有结果),您将小部件添加到场景中。问题是视图(它是 QAbstractScrollArea 的后代)只有在实际映射到屏幕上时才会更新其滚动条。
这是一个复杂的“path”,它从显示主父 window(如果有)开始,它根据其内容调整自身大小,并再次调整其内容的大小,如果他们需要它,最终基于他们的 [嵌套小部件] 大小策略。只有然后,视图“决定”是否需要滚动条,并最终设置它们的最大值。而且,只有然后您才能真正为这些滚动条设置一个值,这是因为只有视图“询问”场景关于它的sceneRect
。
这也(部分地)解释了为什么视图的行为方式与标准滚动区域不同:小部件有一个 sizeHint
被包含在滚动区域内的 QWidget 使用,并且理论上,它们的大小在它们被创建。 但是。这取决于它们的大小提示和策略,因此在最终映射/显示之前,您无法保证实际的滚动区域内容大小;长话短说:它“有效”,但并不完美 - 至少在所有内容最终展示之前不会。
一个测试例子
根据您的需要和实施,有不同的方法可以解决您的问题。
独立设置 sceneRect,甚至在向场景添加任何对象之前(但如果这些对象边界超出场景,您将面临一些不一致) 如上所述调用scene.sceneRect()
,在添加所有对象后
仅在视图显示并调整大小后设置 scoll 条
我准备了一个示例来显示上述三种情况。它将创建一个新视图并根据复选框在不同点更新其滚动条,以显示它们的行为方式有何不同。请注意,在设置 sceneRect 时,我使用了一个小于小部件大小的矩形来更好地显示其行为:您可以看到“设置场景矩形”和“检查场景矩形”的视觉结果相似,但滚动条位置不同.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Diedrico(QtWidgets.QWidget):
def paintEvent(self, event):
qp = QtGui.QPainter(self)
pen = QtGui.QPen(QtGui.QColor(QtCore.Qt.black), 5)
qp.setPen(pen)
qp.drawRect(500, 500, 1000, 1000)
class TestView(QtWidgets.QGraphicsView):
def __init__(self, setRect=False, checkScene=False, showEventCheck=False):
super(TestView, self).__init__()
self.setFixedSize(800, 800)
scene = QtWidgets.QGraphicsScene()
self.setScene(scene)
self.diedrico = Diedrico()
self.diedrico.setFixedSize(2000, 2000)
scene.addWidget(self.diedrico)
if setRect:
scene.setSceneRect(0, 0, 1500, 1500)
elif checkScene:
scene.sceneRect()
self.showEventCheck = showEventCheck
if not showEventCheck:
self.scroll()
def scroll(self):
self.verticalScrollBar().setValue(500)
self.horizontalScrollBar().setValue(500)
def showEvent(self, event):
super(TestView, self).showEvent(event)
if not event.spontaneous() and self.showEventCheck:
self.scroll()
class ViewTester(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
layout = QtWidgets.QVBoxLayout()
self.setLayout(layout)
self.setRectCheck = QtWidgets.QCheckBox('Set scene rect')
layout.addWidget(self.setRectCheck)
self.checkSceneCheck = QtWidgets.QCheckBox('Check scene rect')
layout.addWidget(self.checkSceneCheck)
self.showEventCheck = QtWidgets.QCheckBox('Scroll when shown')
layout.addWidget(self.showEventCheck)
showViewButton = QtWidgets.QPushButton('Show view')
layout.addWidget(showViewButton)
showViewButton.clicked.connect(self.showView)
self.view = None
def showView(self):
if self.view:
self.view.close()
self.view.deleteLater()
self.view = TestView(
setRect = self.setRectCheck.isChecked(),
checkScene = self.checkSceneCheck.isChecked(),
showEventCheck = self.showEventCheck.isChecked()
)
self.view.show()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
viewTester = ViewTester()
viewTester.show()
sys.exit(app.exec_())
最后,请记住,对滚动条使用绝对值并不是一个好主意。如果您想将视图“居中”,请考虑使用centerOn
(及其item based overload),或根据scrollBar.maximum()/2
设置值。
【讨论】:
你的回答比较准确和完整,也谢谢你最后的部分,很有用【参考方案2】:您想在小部件尚未形成时设置值,请稍等。
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Diedrico(QtWidgets.QWidget):
def paintEvent(self, event):
qp = QtGui.QPainter(self)
pen = QtGui.QPen(QtGui.QColor(QtCore.Qt.black), 5)
qp.setPen(pen)
qp.drawRect(500, 500, 1000, 1000)
class UiVentana(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(UiVentana, self).__init__(parent)
self.resize(1000, 1000)
self.setFixedSize(1000, 1000)
self.scene = QtWidgets.QGraphicsScene(self)
self.view = QtWidgets.QGraphicsView(self.scene)
# This two lines should move the scroll bar
QtCore.QTimer.singleShot(0, self.set_Value) # +++
self.diedrico = Diedrico()
self.diedrico.setFixedSize(2000, 2000)
self.scene.addWidget(self.diedrico)
self.setCentralWidget(self.view)
def set_Value(self): # +++
self.view.verticalScrollBar().setValue(500)
self.view.horizontalScrollBar().setValue(500)
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_R:
self.view.setTransform(QtGui.QTransform())
elif event.key() == QtCore.Qt.Key_Plus:
scale_tr = QtGui.QTransform()
scale_tr.scale(1.5, 1.5)
tr = self.view.transform() * scale_tr
self.view.setTransform(tr)
elif event.key() == QtCore.Qt.Key_Minus:
scale_tr = QtGui.QTransform()
scale_tr.scale(1.5, 1.5)
scale_inverted, invertible = scale_tr.inverted()
if invertible:
tr = self.view.transform() * scale_inverted
self.view.setTransform(tr)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ui = UiVentana()
ui.show()
sys.exit(app.exec_())
【讨论】:
为什么当滚动条位于图形场景而不是滚动区域内时,必须以不同的方式完成?以上是关于最初在 QGraphics 中移动滚动条的主要内容,如果未能解决你的问题,请参考以下文章
如何从 JetBrains IDEA 的滚动条中删除大部分亮点?