【问题标题】:Initially moved scroll bar inside QGraphics最初在 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

【问题讨论】:

    标签: python user-interface pyqt5


    【解决方案1】:

    @S.Nick 给出的答案很好,但我想补充一些关于您为什么会面临这个问题以及“幕后”发生的事情的见解。

    首先,在您的代码中,您尝试在将任何对象添加到场景之前设置滚动条的值。
    那时,您刚刚创建了视图和场景。视图小部件尚未显示(因此它还不“知道”它的实际大小),并且场景是空的,这意味着 sceneRectnull,如 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_())
    

    【讨论】:

    • 为什么当滚动条位于图形场景而不是滚动区域内时,必须以不同的方式完成?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多