gpt4 book ai didi

python - 如何正确缩放 QGraphicsScene

转载 作者:行者123 更新时间:2023-12-01 00:33:11 30 4
gpt4 key购买 nike

我创建了一个小应用程序,我试图使其在调整主窗口大小(GraphicsView 和场景也是如此)时整个场景(像素图和矩形)垂直缩放以完全适合内部图形 View 。我不想要垂直滚动条,也不希望它水平缩放。

我不知道如何正确缩放场景。我使用 GraphicsScene 来包含一个图形和几个垂直矩形“标记”。当我可以通过重新绘制像素图来缩放图形以适应然后重新附加它时,z 顺序是错误的,并且矩形小部件不会随之缩放。

我需要跟踪矩形小部件,因此我不能只是不断删除并重新添加它们,因为每个小部件都包含元数据。

我知道 fitInView (来自这里: Issue with fitInView of QGraphicsView when ItemIgnoresTransformations is on )适用于包含的 GraphicsView,但我不明白为什么它需要一个参数。我只想让场景适合 GraphicsView(垂直而不是水平),那么为什么 GraphicsView 不缩放场景中的所有内容以适合其当前大小呢?参数应该是什么样子才能使场景垂直适合?

在 resizeEvent 中,我可以重新绘制像素图并重新添加,但由于 z 顺序困惑,它会覆盖矩形。另外,它不会在场景中垂直居中,我需要复制元数据。

import sys
import os
from PyQt5 import QtCore, QtGui, QtWidgets
import PyQt5 as qt
from PyQt5.QtGui import QColor
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout, QGroupBox, QDialog, QVBoxLayout
from PyQt5.QtWidgets import QVBoxLayout, QGridLayout, QStackedWidget, QTabWidget
import numpy as np

class GraphicsScene(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super(GraphicsScene, self).__init__(parent)

def minimumSizeHint(self):
return QtCore.QSize(300, 200)

def dragMoveEvent(self, event):
print("dragMoveEvent", event)

class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
#super(MainWindow).__init__()

layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)

max_x, max_y = 2400, 700
max_x_view = 1200
self.max_x = max_x
self.max_y = max_y
self.first = True
self.setGeometry(200, 200, max_x_view, self.max_y)

self.gv = QtWidgets.QGraphicsView(self)
self.gv.setGeometry(0, 0, max_x_view, self.max_y)
self.gv2 = QtWidgets.QGraphicsView(self)

layout.addWidget(self.gv)
layout.addWidget(self.gv2)

scene = GraphicsScene()
self.scene = scene

self.gv.setScene(scene)
tab_widget = QTabWidget()
tab_widget.setTabPosition(QTabWidget.West)
widget = QWidget()
widget.setLayout(layout)
tab_widget.addTab(widget, "main")

self.setCentralWidget(tab_widget)
np.random.seed(777)
self.x_time = np.linspace(0, 12.56, 3000)
rand_data = np.random.uniform(0.0, 1.0, 3000)
self.data = .45*(np.sin(2*self.x_time) + rand_data) - .25*(np.sin(3*self.x_time))
self.first = True

pixmap_height = max_y//2 - 2*22 # 22 to take care of scrollbar height
pixmap = self.draw_graph()

pen = QtGui.QPen()
pen.setWidth(2)
pen.setColor(QtGui.QColor("red"))
self.gv1_pixmap = scene.addPixmap(pixmap)
rect = scene.sceneRect()
print("scene rect = {}".format(rect))
scene.setSceneRect(rect)
side, offset = 50, 200

for i in range(2):
r = QtCore.QRectF(QtCore.QPointF((i + 1)*offset + i * 2 * side, 2), QtCore.QSizeF(side, pixmap_height - 4))
rect_ref = scene.addRect(r, pen, QColor(255, 0, 0, 127))
rect_ref.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable)

all_items = scene.items()
print(all_items)

def draw_graph(self):
print("draw_graph: main Window size {}:".format(self.size()))

pixmap_height = self.height()//2 - 2*22 # 22 to take care of scrollbar height
x_final = self.x_time[-1]
data = self.data / np.max(np.abs(self.data))
data = [abs(int(k * pixmap_height)) for k in self.data]
x_pos = [int(self.x_time[i] * self.max_x / x_final) for i in range(len(data))]

pixmap = QtGui.QPixmap(self.max_x, pixmap_height)
painter = QtGui.QPainter(pixmap)
pen = QtGui.QPen()
pen.setWidth(2)
rect = pixmap.rect()
pen.setColor(QtGui.QColor("red"))
painter.drawRect(rect)
print("pixmap rect = {}".format(rect))
painter.fillRect(rect, QtGui.QColor('lightblue'))
pen.setWidth(2)
pen.setColor(QtGui.QColor("green"))
painter.setPen(pen)
for x, y in zip(x_pos, data):
painter.drawLine(x, pixmap_height, x, pixmap_height - y)
painter.end()
return pixmap

def resizeEvent(self, a0: QtGui.QResizeEvent):
#print("main Window resizeEvent")
print("main Window size {}:".format(a0.size()))

redraw = False
if redraw:
pixmap = self.draw_graph()
self.scene.removeItem(self.gv1_pixmap)
self.gv1_pixmap = self.scene.addPixmap(pixmap)
self.gv1_pixmap.moveBy(0, 30)
else:
#rect = QtCore.QRect(self.gv.startPos, self.gv.endPos)
#sceneRect = self.gv.mapToScene(rect).boundingRect()
#print 'Selected area: viewport coordinate:', rect,', scene coordinate:', sceneRect
#self.gv.fitInView(sceneRect)
pass

app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()

最佳答案

我的解决方案将使封装所有项目(sceneRect)的最小矩形的高度适合QGraphicsView的视口(viewport)。因此,将项目的高度设置得不要太小,以免损失图像质量。我还使用 QTransforms 缩放了项目。另外,QGraphicsView坐标系被颠倒了,因为默认情况下垂直轴是上下的,我已经颠倒了它,以便绘画与数据更加一致。

我重构了 OP 代码以使其更具可扩展性,有一个 GraphItem 获取数据 (x, y) 和图像尺寸。

考虑到上述情况,解决方案是:

import numpy as np
from PyQt5 import QtCore, QtGui, QtWidgets


class GraphItem(QtWidgets.QGraphicsPixmapItem):
def __init__(self, xdata, ydata, width, height, parent=None):
super(GraphItem, self).__init__(parent)

self._xdata = xdata
self._ydata = ydata
self._size = QtCore.QSize(width, height)
self.redraw()

def redraw(self):
x_final = self._xdata[-1]
pixmap = QtGui.QPixmap(self._size)
pixmap_height = pixmap.height()
pixmap.fill(QtGui.QColor("lightblue"))
painter = QtGui.QPainter(pixmap)

pen = QtGui.QPen(QtGui.QColor("green"))
pen.setWidth(2)
painter.setPen(pen)
for i, (x, y) in enumerate(
zip(self._xdata, self._ydata / np.max(np.abs(self._ydata)))
):
x_pos = int(x * self._size.width() / x_final)
y_pos = abs(int(y * pixmap_height))
painter.drawLine(x_pos, 0, x_pos, y_pos)

painter.end()
self.setPixmap(pixmap)


class HorizontalRectItem(QtWidgets.QGraphicsRectItem):
def itemChange(self, change, value):
if change == QtWidgets.QGraphicsItem.ItemPositionChange and self.scene():
newPos = self.pos()
newPos.setX(value.x())
return newPos
return super(HorizontalRectItem, self).itemChange(change, value)


class GraphicsView(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super(GraphicsView, self).__init__(parent)
scene = QtWidgets.QGraphicsScene(self)
self.setScene(scene)
self.scale(1, -1)

def resizeEvent(self, event):

h = self.mapToScene(self.viewport().rect()).boundingRect().height()
r = self.sceneRect()
r.setHeight(h)
self.setSceneRect(r)

height = self.viewport().height()
for item in self.items():
item_height = item.boundingRect().height()
tr = QtGui.QTransform()
tr.scale(1, height / item_height)
item.setTransform(tr)

super(GraphicsView, self).resizeEvent(event)

class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)

tab_widget = QtWidgets.QTabWidget(tabPosition=QtWidgets.QTabWidget.West)
self.setCentralWidget(tab_widget)

self.graphics_view_top = GraphicsView()
self.graphics_view_bottom = QtWidgets.QGraphicsView()

container = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(container)
lay.addWidget(self.graphics_view_top)
lay.addWidget(self.graphics_view_bottom)

tab_widget.addTab(container, "main")

self.resize(640, 480)

side, offset, height = 50, 200, 400

np.random.seed(777)
x_time = np.linspace(0, 12.56, 3000)
rand_data = np.random.uniform(0.0, 1.0, 3000)
data = 0.45 * (np.sin(2 * x_time) + rand_data) - 0.25 * (np.sin(3 * x_time))

graph_item = GraphItem(x_time, data, 3000, height)
self.graphics_view_top.scene().addItem(graph_item)

for i in range(2):
r = QtCore.QRectF(
QtCore.QPointF((i + 1) * offset + i * 2 * side, 2),
QtCore.QSizeF(side, height),
)
it = HorizontalRectItem(r)
it.setPen(QtGui.QPen(QtGui.QColor("red"), 2))
it.setBrush(QtGui.QColor(255, 0, 0, 127))
self.graphics_view_top.scene().addItem(it)
it.setFlags(
it.flags()
| QtWidgets.QGraphicsItem.ItemIsMovable
| QtWidgets.QGraphicsItem.ItemSendsGeometryChanges
)


def main():
import sys

app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()

ret = app.exec_()

sys.exit(ret)


if __name__ == "__main__":
main()

关于python - 如何正确缩放 QGraphicsScene,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58019808/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com