gpt4 book ai didi

python - QGraphicsItem 线宽/转换从 PyQt4 更改为 PyQt5

转载 作者:太空宇宙 更新时间:2023-11-03 14:25:31 26 4
gpt4 key购买 nike

我有一些用 PyQt 制作的小型实用程序 GUI,它们使用包含一些项目的 QGraphicsScene,以及响应用户点击的 View (用于制作框、选择点等)。

在我使用的(离线)机器上,软件刚刚从Anaconda 2.5升级到Anaconda 4.3,包括从PyQt4到PyQt5的切换。一切仍然有效,除了如果场景矩形是在像素坐标以外的任何内容中定义的,那么我的各种 QGraphicsItem 对象的转换就会变得困惑。

预先问题:从 PyQt4 到 PyQt5 的项目转换发生了什么变化?

下面是我正在讨论的示例:顶行是一个框选择器,其中包含场景中的虚拟灰度,其边界矩形为 (0, 0, 2pi, 4pi) 。绿色框是用户绘制的 QGraphicsRectItem,单击“完成”后我可以从中获取 LL 和 UR 点(在场景坐标中)。底行是一个点布局,带有用户单击的椭圆,位于已放大 20 倍的小虚拟图像上。

左侧和右侧是用相同的代码制成的。左边的版本是在 Anaconda 2.5 Python 3.5 下使用 PyQt4 的结果,右边的版本是在 Anaconda 4.3 Python 3.6 下使用 PyQt5 的结果。

PyQt4 version of box PyQt5 version of box

PyQt4 version of ellipse PyQt5 version of ellipse

显然,存在某种以不同方式处理的项目转换,但我无法在任何 PyQt4->PyQt5 文档中找到它(都是关于 API 更改的)。

如何使 QGraphicsItem 的线宽在设备坐标中为 1,同时仍保持场景坐标中的正确位置?更一般地说,如何缩放通用 QGraphicsItem,使其不会根据场景大小而膨胀或变胖?

代码如下。 SimpleDialog 是我用于各种选择器实用程序的主要基类,它包括 MouseViewImageScene,它们自动构建垂直翻转和背景图像。我在这里使用的两个实用程序是 BoxSelectorPointLayout

# Imports
import numpy as np
try:
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot, QPointF
from PyQt5.QtGui import QImage, QPixmap, QFont, QBrush, QPen, QTransform
from PyQt5.QtWidgets import (QGraphicsView, QGraphicsScene, QDialog, QSizePolicy,
QVBoxLayout, QPushButton, QMainWindow, QApplication)
except ImportError:
from PyQt4.QtCore import Qt, pyqtSignal, pyqtSlot, QPointF
from PyQt4.QtGui import (QImage, QPixmap, QFont, QBrush, QPen, QTransform,
QGraphicsView, QGraphicsScene, QDialog, QSizePolicy,
QVBoxLayout, QPushButton, QMainWindow, QApplication)

class MouseView(QGraphicsView):
"""A subclass of QGraphicsView that returns mouse click events."""

mousedown = pyqtSignal(QPointF)
mouseup = pyqtSignal(QPointF)
mousemove = pyqtSignal(QPointF)

def __init__(self, scene, parent=None):
super(MouseView, self).__init__(scene, parent=parent)

self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setSizePolicy(QSizePolicy.Fixed,
QSizePolicy.Fixed)
self.scale(1, -1)

self.moving = False

def mousePressEvent(self, event):
"""Emit a mouse click signal."""
self.mousedown.emit(self.mapToScene(event.pos()))

def mouseReleaseEvent(self, event):
"""Emit a mouse release signal."""
self.mouseup.emit(self.mapToScene(event.pos()))

def mouseMoveEvent(self, event):
"""Emit a mouse move signal."""
if self.moving:
self.mousemove.emit(self.mapToScene(event.pos()))


class ImageScene(QGraphicsScene):
"""A subclass of QGraphicsScene that includes a background pixmap."""

def __init__(self, data, scene_rect, parent=None):
super(ImageScene, self).__init__(parent=parent)

bdata = ((data - np.min(data)) / (np.max(data) - np.min(data)) * 255).astype(np.uint8)
wid, hgt = data.shape

img = QImage(bdata.T.copy(), wid, hgt, wid, QImage.Format_Indexed8)

self.setSceneRect(*scene_rect)

px = QPixmap.fromImage(img)

self.px = self.addPixmap(px)

px_trans = QTransform.fromTranslate(scene_rect[0], scene_rect[1])
px_trans = px_trans.scale(scene_rect[2]/wid, scene_rect[3]/hgt)
self.px.setTransform(px_trans)

class SimpleDialog(QDialog):
"""A base class for utility dialogs using a background image in scene."""

def __init__(self, data, bounds=None, grow=[1.0, 1.0], wsize=None, parent=None):
super(SimpleDialog, self).__init__(parent=parent)

self.grow = grow

wid, hgt = data.shape
if bounds is None:
bounds = [0, 0, wid, hgt]

if wsize is None:
wsize = [wid, hgt]

vscale = [grow[0]*wsize[0]/bounds[2], grow[1]*wsize[1]/bounds[3]]

self.scene = ImageScene(data, bounds, parent=self)

self.view = MouseView(self.scene, parent=self)
self.view.scale(vscale[0], vscale[1])

quitb = QPushButton("Done")
quitb.clicked.connect(self.close)

lay = QVBoxLayout()
lay.addWidget(self.view)
lay.addWidget(quitb)

self.setLayout(lay)

def close(self):
self.accept()

class BoxSelector(SimpleDialog):
"""Simple box selector."""

def __init__(self, *args, **kwargs):
super(BoxSelector, self).__init__(*args, **kwargs)

self.rpen = QPen(Qt.green)
self.rect = self.scene.addRect(0, 0, 0, 0, pen=self.rpen)

self.view.mousedown.connect(self.start_box)
self.view.mouseup.connect(self.end_box)
self.view.mousemove.connect(self.draw_box)

self.start_point = []
self.points = []

self.setWindowTitle('Box Selector')

@pyqtSlot(QPointF)
def start_box(self, xy):
self.start_point = [xy.x(), xy.y()]
self.view.moving = True

@pyqtSlot(QPointF)
def end_box(self, xy):
lx = np.minimum(xy.x(), self.start_point[0])
ly = np.minimum(xy.y(), self.start_point[1])
rx = np.maximum(xy.x(), self.start_point[0])
ry = np.maximum(xy.y(), self.start_point[1])
self.points = [[lx, ly], [rx, ry]]
self.view.moving = False

@pyqtSlot(QPointF)
def draw_box(self, xy):
newpoint = [xy.x(), xy.y()]
minx = np.minimum(self.start_point[0], newpoint[0])
miny = np.minimum(self.start_point[1], newpoint[1])
size = [np.abs(i - j) for i, j in zip(self.start_point, newpoint)]
self.rect.setRect(minx, miny, size[0], size[1])

class PointLayout(SimpleDialog):
"""Simple point display."""

def __init__(self, *args, **kwargs):
super(PointLayout, self).__init__(*args, **kwargs)

self.pen = QPen(Qt.green)

self.view.mousedown.connect(self.mouse_click)

self.circles = []
self.points = []

self.setWindowTitle('Point Layout')

@pyqtSlot(QPointF)
def mouse_click(self, xy):
self.points.append((xy.x(), xy.y()))
pt = self.scene.addEllipse(xy.x()-0.5, xy.y()-0.5, 1, 1, pen=self.pen)
self.circles.append(pt)

这是我用来进行测试的代码:

def test_box():

x, y = np.mgrid[0:175, 0:100]
img = x * y

app = QApplication.instance()
if app is None:
app = QApplication(['python'])

picker = BoxSelector(img, bounds=[0, 0, 2*np.pi, 4*np.pi])
picker.show()
app.exec_()

return picker

def test_point():

np.random.seed(159753)
img = np.random.randn(10, 5)

app = QApplication.instance()
if app is None:
app = QApplication(['python'])

pointer = PointLayout(img, bounds=[0, 0, 10, 5], grow=[20, 20])
pointer.show()

app.exec_()

return pointer

if __name__ == "__main__":
pick = test_box()
point = test_point()

最佳答案

我发现将画笔宽度明确设置为零可以恢复之前的行为:

class BoxSelector(SimpleDialog):
def __init__(self, *args, **kwargs):
...
self.rpen = QPen(Qt.green)
self.rpen.setWidth(0)
...

class PointLayout(SimpleDialog):
def __init__(self, *args, **kwargs):
...
self.pen = QPen(Qt.green)
self.pen.setWidth(0)
...

在Qt4中似乎默认是0,但在Qt5中默认是1

来自 QPen.setWidth 的 Qt 文档:

A line width of zero indicates a cosmetic pen. This means that the pen width is always drawn one pixel wide, independent of the transformation set on the painter.

关于python - QGraphicsItem 线宽/转换从 PyQt4 更改为 PyQt5,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47661681/

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