在 QGraphicsScene 中连续移动 QGraphicsItem 并检查 Collision
move QGraphicsItem continuously in QGraphicsScene and check Collision
大约 3 周前,我要求更改 GraphicsScene 中的 GraphicsItem。这是一条线,解决方案是:self.setLine(..)。现在我试着理解,不断地移动它。所以我有一个 graphicsview gvCanvas 并在一个场景中初始化矩形,线条......它有效
为了移动一个矩形,我有以下代码用于 Class myRect 和一个 __init__
和一个 movemyrect
class myRect(QtWidgets.QGraphicsRectItem):
def __init__(self, QRectF): # *args):
QtWidgets.QGraphicsRectItem.__init__(self)
self.setRect(QRectF)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
self.setPen(QtGui.QPen(QtCore.Qt.white, 3))
def movemyRect(self, x, y, angl):
myyrs = QtWidgets.QGraphicsRectItem.rect(self) #(self.myyr) #line(items[0])
ptrr = myyrs.bottomLeft()
xpos = ptrr.x() + x #+x
ypos = ptrr.y() - y
ptnew=QtCore.QPoint(xpos,ypos)
myr = QtCore.QRectF(ptnew, QtCore.QSizeF(15,15))
self.setRotation(angl)
self.setRect(myr)
self.update()
然后我试着在一个矩形周围移动
xm =20
ym = 20
for i in range(1,5):
time.sleep(0.8)
print("nachsleep",i)
self.rect2.movemyRect(xm, ym, 10)
myyrs = QtWidgets.QGraphicsRectItem.rect(self.rect2)
self.rect2.setRect(myyrs)
self.scene.update()
listtreffer = self.scene.collidingItems(self.lnk, mode=Qt.IntersectsItemShape)
for treffer in listtreffer:
print("treffer", treffer)
循环有效,但移动和旋转的矩形仅在我场景的循环结束时出现变化,而不是在每一步之后。我认为,使用语句 "setRect..." 它应该在每次循环时都有效。 scene.update() 也没有帮助。
如果我不用循环,它也可以。
怎么了,移动的矩形没有出现在循环的每一步上?
检查碰撞的语句在每一步都正确。
这是一个额外的问题:如果我只想检查这个矩形的碰撞,在 class 定义中而不是在这个循环中定义一种碰撞检查会更好吗?
(我也尝试对动画和 QPropertyAnimantion 做同样的事情。在那里我无法触发或获取预先声明 运行。检查碰撞,即使在移动时也是如此。但我认为,为此我应该提出一个新问题)
在 GUI 中,你永远不应该使用 time.sleep,因为它会阻塞事件循环,因此 window 会冻结,从而阻止 GUI 被重绘。在 Qt 中,您必须使用事件来执行操作,例如在每次触发计时器时在以下代码中更改位置。要使移动平滑,请使用 QVariantAnimation,不能使用 QPropertyAnimation,因为 QGraphicsItem 不是 QObjects 并且位置不是 qproperty。
import random
from functools import partial
from PyQt5 import QtCore, QtGui, QtWidgets
class RectItem(QtWidgets.QGraphicsRectItem):
def __init__(self, rect=QtCore.QRectF()):
super(RectItem, self).__init__(rect)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True)
self.setPen(QtGui.QPen(QtCore.Qt.red, 3))
self._pos_animation = QtCore.QVariantAnimation()
self._pos_animation.valueChanged.connect(self.setPos)
def move_smooth(self, end, duration=1000):
if self._pos_animation.state() == QtCore.QAbstractAnimation.Running:
self._pos_animation.stop()
self._pos_animation.setDuration(duration)
self._pos_animation.setStartValue(self.pos())
self._pos_animation.setEndValue(end)
self._pos_animation.start()
def itemChange(self, change, value):
if change ==QtWidgets.QGraphicsItem.ItemPositionChange:
color = QtGui.QColor(QtCore.Qt.red)
if self.scene().collidingItems(self):
color = QtGui.QColor(QtCore.Qt.green)
self.setPen(QtGui.QPen(color, 3))
return super(RectItem, self).itemChange(change, value)
def move_pos(scene):
for it in scene.items():
pos = QtCore.QPointF(*random.sample(range(-100, 200), 2))
if hasattr(it, 'move_smooth'):
it.move_smooth(pos, 1000)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
app.setStyle('fusion')
scene = QtWidgets.QGraphicsScene(-100, -100, 200, 200)
scene.setBackgroundBrush(QtCore.Qt.gray)
view = QtWidgets.QGraphicsView(scene)
view.resize(640, 480)
view.show()
l = []
for _ in range(4):
pos = QtCore.QPointF(*random.sample(range(-100, 200), 2))
it = RectItem(QtCore.QRectF(-20, -20, 40, 40))
scene.addItem(it)
it.setPos(pos)
l.append(it)
wrapper = partial(move_pos, scene)
timer = QtCore.QTimer(interval=3000, timeout=wrapper)
timer.start()
wrapper()
sys.exit(app.exec_())
大约 3 周前,我要求更改 GraphicsScene 中的 GraphicsItem。这是一条线,解决方案是:self.setLine(..)。现在我试着理解,不断地移动它。所以我有一个 graphicsview gvCanvas 并在一个场景中初始化矩形,线条......它有效
为了移动一个矩形,我有以下代码用于 Class myRect 和一个 __init__
和一个 movemyrect
class myRect(QtWidgets.QGraphicsRectItem):
def __init__(self, QRectF): # *args):
QtWidgets.QGraphicsRectItem.__init__(self)
self.setRect(QRectF)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
self.setPen(QtGui.QPen(QtCore.Qt.white, 3))
def movemyRect(self, x, y, angl):
myyrs = QtWidgets.QGraphicsRectItem.rect(self) #(self.myyr) #line(items[0])
ptrr = myyrs.bottomLeft()
xpos = ptrr.x() + x #+x
ypos = ptrr.y() - y
ptnew=QtCore.QPoint(xpos,ypos)
myr = QtCore.QRectF(ptnew, QtCore.QSizeF(15,15))
self.setRotation(angl)
self.setRect(myr)
self.update()
然后我试着在一个矩形周围移动
xm =20
ym = 20
for i in range(1,5):
time.sleep(0.8)
print("nachsleep",i)
self.rect2.movemyRect(xm, ym, 10)
myyrs = QtWidgets.QGraphicsRectItem.rect(self.rect2)
self.rect2.setRect(myyrs)
self.scene.update()
listtreffer = self.scene.collidingItems(self.lnk, mode=Qt.IntersectsItemShape)
for treffer in listtreffer:
print("treffer", treffer)
循环有效,但移动和旋转的矩形仅在我场景的循环结束时出现变化,而不是在每一步之后。我认为,使用语句 "setRect..." 它应该在每次循环时都有效。 scene.update() 也没有帮助。
如果我不用循环,它也可以。
怎么了,移动的矩形没有出现在循环的每一步上?
检查碰撞的语句在每一步都正确。
这是一个额外的问题:如果我只想检查这个矩形的碰撞,在 class 定义中而不是在这个循环中定义一种碰撞检查会更好吗?
(我也尝试对动画和 QPropertyAnimantion 做同样的事情。在那里我无法触发或获取预先声明 运行。检查碰撞,即使在移动时也是如此。但我认为,为此我应该提出一个新问题)
在 GUI 中,你永远不应该使用 time.sleep,因为它会阻塞事件循环,因此 window 会冻结,从而阻止 GUI 被重绘。在 Qt 中,您必须使用事件来执行操作,例如在每次触发计时器时在以下代码中更改位置。要使移动平滑,请使用 QVariantAnimation,不能使用 QPropertyAnimation,因为 QGraphicsItem 不是 QObjects 并且位置不是 qproperty。
import random
from functools import partial
from PyQt5 import QtCore, QtGui, QtWidgets
class RectItem(QtWidgets.QGraphicsRectItem):
def __init__(self, rect=QtCore.QRectF()):
super(RectItem, self).__init__(rect)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True)
self.setPen(QtGui.QPen(QtCore.Qt.red, 3))
self._pos_animation = QtCore.QVariantAnimation()
self._pos_animation.valueChanged.connect(self.setPos)
def move_smooth(self, end, duration=1000):
if self._pos_animation.state() == QtCore.QAbstractAnimation.Running:
self._pos_animation.stop()
self._pos_animation.setDuration(duration)
self._pos_animation.setStartValue(self.pos())
self._pos_animation.setEndValue(end)
self._pos_animation.start()
def itemChange(self, change, value):
if change ==QtWidgets.QGraphicsItem.ItemPositionChange:
color = QtGui.QColor(QtCore.Qt.red)
if self.scene().collidingItems(self):
color = QtGui.QColor(QtCore.Qt.green)
self.setPen(QtGui.QPen(color, 3))
return super(RectItem, self).itemChange(change, value)
def move_pos(scene):
for it in scene.items():
pos = QtCore.QPointF(*random.sample(range(-100, 200), 2))
if hasattr(it, 'move_smooth'):
it.move_smooth(pos, 1000)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
app.setStyle('fusion')
scene = QtWidgets.QGraphicsScene(-100, -100, 200, 200)
scene.setBackgroundBrush(QtCore.Qt.gray)
view = QtWidgets.QGraphicsView(scene)
view.resize(640, 480)
view.show()
l = []
for _ in range(4):
pos = QtCore.QPointF(*random.sample(range(-100, 200), 2))
it = RectItem(QtCore.QRectF(-20, -20, 40, 40))
scene.addItem(it)
it.setPos(pos)
l.append(it)
wrapper = partial(move_pos, scene)
timer = QtCore.QTimer(interval=3000, timeout=wrapper)
timer.start()
wrapper()
sys.exit(app.exec_())