Qt5中show()后QGraphicsView/QGraphicsScene初始定位的基本问题
Posted
技术标签:
【中文标题】Qt5中show()后QGraphicsView/QGraphicsScene初始定位的基本问题【英文标题】:Basic problem with initial positioning of QGraphicsView/QGraphicsScene after show() in Qt5 【发布时间】:2019-11-02 12:00:36 【问题描述】:我为板子的可视化编写了一些代码,但我在初始化时遇到了问题。我想从头到尾生成充满木板的视图(是否显示在第三张图片上)。我尝试使用许多 Qt5 方法但没有结果(我是 Qt5 的初学者)。第一次调整大小后视图看起来很完美。
我不知道我在初始化时做错了什么。
就在 .show() 之后:
失焦后(我转写这个问题):
调整大小后,它应该看起来像/我想要实现的目标:
如何修复此代码以使其在初始化时工作 - 我是 Qt5 的第 1 级(初学者)和编程的第 7 级。也许它需要非常简单的改变。
这是 Python 3.8/Qt5 的工作代码:
import logging
import sys
import typing
from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import QSize, QPoint, Qt, QRect, QMargins
from PyQt5.QtGui import QFont, QPaintEvent, QPainter, QBrush, QColor, QPen
from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout, QSizePolicy, QVBoxLayout, QHBoxLayout, QGraphicsWidget, \
QGraphicsScene, QGraphicsView, QGraphicsGridLayout, QStyleOptionGraphicsItem, QGraphicsSceneMouseEvent
class Application(QApplication):
pass
class SquareWidget(QGraphicsWidget):
def __init__(self, color):
super().__init__()
if color:
self.color = QtCore.Qt.white
else:
self.color = QtCore.Qt.black
def paint(self, painter: QtGui.QPainter, option: QStyleOptionGraphicsItem, widget: typing.Optional[QWidget] = ...) -> None:
painter.fillRect(option.rect, self.color)
class BoardContainer(QGraphicsWidget):
def __init__(self):
super().__init__()
logging.debug('size is %s for %s.', self.size(), self.__class__.__name__)
grid = QGraphicsGridLayout()
grid.setSpacing(0)
grid.setContentsMargins(0, 0, 0, 0)
self.setLayout(grid)
for row in range(8):
for column in range(8):
square_widget = SquareWidget((row + column) % 2)
grid.addItem(square_widget, row, column)
class BoardScene(QGraphicsScene):
def __init__(self):
super().__init__()
self.board_container = board_container = BoardContainer()
self.addItem(board_container)
class BoardView(QGraphicsView):
def __init__(self):
super().__init__()
logging.debug('size is %s for %s.', self.size(), self.__class__.__name__)
scene = BoardScene()
self.setScene(scene)
# no frame
self.setFrameShape(0)
# transparent background
# self.setStyleSheet('QGraphicsView background: transparent;')
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
def resizeEvent(self, event: QtGui.QResizeEvent) -> None:
super().resizeEvent(event)
self.fitInView(self.scene().board_container, Qt.KeepAspectRatio)
class BoardWidget(QWidget):
def __init__(self):
super().__init__()
logging.debug('size is %s for %s.', self.size(), self.__class__.__name__)
grid = QGridLayout()
board_view = BoardView()
grid.addWidget(board_view, 0, 0)
self.setLayout(grid)
def main():
# show exceptions
def excepthook(cls, exception, traceback):
sys.__excepthook__(cls, exception, traceback)
sys.excepthook = excepthook
logging.basicConfig(level=logging.DEBUG)
app = Application(sys.argv)
app.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True)
default_font = QFont()
default_font.setPointSize(12)
app.setFont(default_font)
board_widget = BoardWidget()
board_widget.setMinimumSize(640, 640)
board_widget.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()
【问题讨论】:
【参考方案1】:试试看:
import logging
import sys
import typing
from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import QSize, QPoint, Qt, QRect, QMargins
from PyQt5.QtGui import QFont, QPaintEvent, QPainter, QBrush, QColor, QPen
from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout, QSizePolicy, QVBoxLayout, QHBoxLayout, QGraphicsWidget, \
QGraphicsScene, QGraphicsView, QGraphicsGridLayout, QStyleOptionGraphicsItem, QGraphicsSceneMouseEvent
class Application(QApplication):
pass
class SquareWidget(QGraphicsWidget):
def __init__(self, color):
super().__init__()
self.resize(640, 640) # +++
if color:
self.color = QtCore.Qt.white
else:
self.color = QtCore.Qt.black
def paint(self, painter: QtGui.QPainter, option: QStyleOptionGraphicsItem, widget: typing.Optional[QWidget] = ...) -> None:
painter.fillRect(option.rect, self.color)
class BoardContainer(QGraphicsWidget):
def __init__(self):
super().__init__()
logging.debug('size is %s for %s.', self.size(), self.__class__.__name__)
self.resize(640, 640) # +++
self.setMinimumSize(80, 80) # +++
grid = QGraphicsGridLayout()
grid.setSpacing(0)
grid.setContentsMargins(0, 0, 0, 0)
self.setLayout(grid)
for row in range(8):
for column in range(8):
square_widget = SquareWidget((row + column) % 2)
grid.addItem(square_widget, row, column)
class BoardScene(QGraphicsScene):
def __init__(self):
super().__init__()
self.board_container = board_container = BoardContainer()
self.addItem(board_container)
class BoardView(QGraphicsView):
def __init__(self):
super().__init__()
logging.debug('size is %s for %s.', self.size(), self.__class__.__name__)
scene = BoardScene()
self.setScene(scene)
# no frame
self.setFrameShape(0)
# transparent background
# self.setStyleSheet('QGraphicsView background: transparent;')
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
def resizeEvent(self, event: QtGui.QResizeEvent) -> None:
super().resizeEvent(event)
self.fitInView(self.scene().board_container, Qt.KeepAspectRatio)
class BoardWidget(QWidget):
def __init__(self):
super().__init__()
logging.debug('size is %s for %s.', self.size(), self.__class__.__name__)
grid = QGridLayout()
board_view = BoardView()
grid.addWidget(board_view, 0, 0)
self.setLayout(grid)
def main():
# show exceptions
def excepthook(cls, exception, traceback):
sys.__excepthook__(cls, exception, traceback)
sys.excepthook = excepthook
logging.basicConfig(level=logging.DEBUG)
app = Application(sys.argv)
app.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True)
default_font = QFont()
default_font.setPointSize(12)
app.setFont(default_font)
board_widget = BoardWidget()
board_widget.setMinimumSize(640, 640)
board_widget.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()
【讨论】:
答案也很好。 fitInView 只有一个问题会导致与第二个答案相同的边距。【参考方案2】:由于网格没有关于 BoardContainer 大小的线索,它无法在 0 大小上正确布局项目。 您可以将 BoardContainer 的大小显式设置为您想要的任何大小。 这是代码的固定部分:
class BoardContainer(QGraphicsWidget):
def __init__(self):
super().__init__()
self.resize(100,100)
logging.debug('size is %s for %s.', self.size(), self.__class__.__name__)
grid = QGraphicsGridLayout()
grid.setSpacing(0)
grid.setContentsMargins(0, 0, 0, 0)
self.setLayout(grid)
for row in range(8):
for column in range(8):
square_widget = SquareWidget((row + column) % 2)
grid.addItem(square_widget, row, column)
grid.activate()
毕竟,调用 grid.activate() 会强制根据大小进行布局。
为了鼓励您使用 QML,这里有一个带有精美动画的同一应用程序的最小 QML 示例,以演示如何在 QML 上轻松使用动画。 如果你从这个实现中删除添加的动画,让它变得像你的代码的 c++ 版本,那么它只有 30 行代码,恕我直言。
import QtQml 2.12
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
ApplicationWindow
id:mywin
visible: true
width: 640
height: 640
minimumWidth: 640
minimumHeight: 480
title: qsTr("Fancy Board")
Item
anchors.centerIn: parent
width: Math.min(parent.width,parent.height);
height: width
GridLayout
id : grid
anchors.fill: parent
rows: 8
columns: 8
rowSpacing: 0
columnSpacing: 0
Repeater
model: 64
Rectangle
Layout.fillWidth: true
Layout.fillHeight: true
color: ((index%8) - (index/8 | 0)) %2 === 0 ? 'black' : 'white'
Rectangle
anchors.fill: parent
opacity: mouseArea.containsMouse ? 0.5 : 0
scale: mouseArea.containsMouse? 1 : 0
color: 'red'
Behavior on opacity
NumberAnimation duration: 300
Behavior on scale
NumberAnimation duration: 300; easing.type: Easing.OutCubic
MouseArea
id: mouseArea
anchors.fill: parent
hoverEnabled: true
您也可以将此代码复制并粘贴到 main.qml 文件中,将其压缩,然后将其上传到https://qt-webassembly.io/designviewer/ 以在您的浏览器上运行它,看看它也可以在您的浏览器下运行。
【讨论】:
我尝试将 setSceneRect 添加到场景或视图的代码中,设置后没有任何变化 - 你确定它会起作用吗?可以加这行代码测试一下吗? @Chameleon,我只是想测试一下。我正在安装 Python 3.8 以保持与您的环境相同。 @Chameleon,我认为调用 fitInView 是不合适的。让我尝试另一种方法,我会更新你。 我在 Python 3.7 上对其进行了测试(文本拼写错误,但我认为 3.8 无关紧要)。 fitInView 可能错误但有效(底部 1 像素大小行中有一些伪影)。 您需要从 x=0 和 y=0 使用 setSceneRect,然后您的小部件将不再居中。以上是关于Qt5中show()后QGraphicsView/QGraphicsScene初始定位的基本问题的主要内容,如果未能解决你的问题,请参考以下文章
QGraphicsView Scale & QGraphicsRectItem 绘画未能调用