- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我有一些用 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->PyQt5 文档中找到它(都是关于 API 更改的)。
如何使 QGraphicsItem
的线宽在设备坐标中为 1,同时仍保持场景坐标中的正确位置?更一般地说,如何缩放通用 QGraphicsItem,使其不会根据场景大小而膨胀或变胖?
代码如下。 SimpleDialog
是我用于各种选择器实用程序的主要基类,它包括 MouseView
和 ImageScene
,它们自动构建垂直翻转和背景图像。我在这里使用的两个实用程序是 BoxSelector
和 PointLayout
。
# 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/
我在 Qt 中遇到了帖子标题中描述的错误。 我有一个类调用“ball”,它继承类调用“tableItem”,后者继承了 QGraphicsItem。我正在尝试使用原型(prototype)设计模式,因
我想让 QGraphicsScene 中的一个 QGraphicsItem 在另一个移动时移动(或改变大小)。但是,尝试从 QGraphicsScene 对象访问任一 QGraphicsItem 会导
这个问题是关于:Forcing QGraphicsItem To Stay Put 我想在场景中四处移动时在固定位置放置一个QGraphicsItem。 建议的解决方案是覆盖子类 QGraphicsV
我想我的问题与此类似post但在 C++ 中和在 QGraphicsItem 中。 我想将对象的可移动区域固定在另一个 QGraphicsItem 中。如果我试图将它移到外面,我希望我的对象留在里面。
总结 我在SUSE 64位下使用qt 4.8.7 我有 2 个具有不同刷新率的 QGraphicsItem。但是当我对其中一个调用“update()”时,对它们两个都调用了“paint()”。所以两个
我从 OOAD 类(class)中了解到 dynamic_cast 是一个糟糕的设计但我不知道在 Qt 中没有 dynamic_cast 我怎么能做我想做的事因为我不能只在 QGraphicsItem
我有一个 QGraphicsItem 的子类,我想通过“Control+LMB click”将它的实例添加到场景中。问题是项目被添加到坐标比它们应该大两倍的位置。同时使用 scene.addEllip
我有一个自定义 QGraphicsItem 实现。我需要能够限制可以移动项目的位置 - 即将其限制在某个区域。当我检查 Qt 文档时,这是它的建议: QVariant Component::itemC
我被困在如何解决这个问题上。我在一个场景中有一个 QGraphicsItem,我正在将一个悬停事件从场景传递给这个 child 。当移动事件发生时(我只是使用带有鼠标跟踪的 mouseMoveEven
我应该怎么做才能删除 QGraphicsItem? 从我使用的场景中删除项目 QGraphicsScene::removeItem(QGraphicsItem * item); 来自此方法的文档:
我正在尝试围绕鼠标光标缩放对象。我很容易获得鼠标位置,并且可以使用 item->setScale(n) 毫无问题地缩放对象。但是,我不确定如何实际合并翻译以解释任意点。 有没有办法设置刻度中心?如果没
是否可以为QGraphicsItem设置动画,使其沿着QGraphicsScene/QGraphicsView内的某个路径移动? 类似于 this demo ,其中白点沿着齿轮移动 - 一条闭合曲线。
我想在按下鼠标按钮时检测鼠标光标何时移入QGraphicsItem,即在鼠标进入项目之前按下按钮。我的第一个想法是使用 hoverEnterEvent,但按下鼠标左键时似乎没有触发。我的另一个想法是使
我目前正在创建一个使用 QGraphicsView 的应用程序,并允许用户移动 QGraphicsItems,以便他们可以创建类似图表的结构。 我需要这些项目在单击时改变颜色,但在释放鼠标按钮时变回原
我正在尝试使用rubberBand来选择一些与某些预定的X和Y坐标关联的QGraphicItems。用户可以使用鼠标滚轮进行缩放,并使用鼠标中心按钮进行平移。用户可以单击鼠标左键并绘制一个选择窗口,但
我想在两个或多个 QGraphicsItem 之间实现像素完美碰撞检测器。 QGraphicsItem 类提供了一个使用 QPainterPath 对象的碰撞检测器,所以现在我想将图像从文件加载到 Q
我有很多 qgraphicsitem,它们是 map 上的航路点。我想将它们一起移动。因此,我使用了一个 for 循环来调用它们的 setPos() 函数。但是当项目数量变大(超过 100)时。 Ac
我怎样才能像这张图片一样绘制矩形和椭圆形。在这段代码中,它创建了带有单线边框的矩形和椭圆形。但是我需要像这张给定的图片那样更改边框样式。 Widget::Widget(QWidget *parent)
我正在使用一个 QGraphincsView,它包含几个继承自 QGraphicsItem 的元素。一切正常,我可以根据需要选择它们。当我按住 Ctrl 键时,我可以选择其中的几个。 现在我想实现一个
我有 QGraphicsScene,上面显示了一些自定义 QGraphicsItems。这些项目在 MeasurePoint 类中描述,该类继承自 QGraphicsItem。它们也存储在 QList
我是一名优秀的程序员,十分优秀!