python - 在 QLabel 内旋转 QPixmap 会导致像素图沿 x 轴移动,而不是停留在 QLabel 内
问题描述
我试图让一个球(QLabel 内的 QPixmap)在从屏幕边缘反弹的同时旋转。但是球,即使它确实旋转,似乎也沿着 QLabel 内的轴移动,所以在计时器的几次移动之后,它移动到 QLabel 的边界之外,因此不再出现。
请在下面查看我的代码。
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import random
x = 0
y = 00
velX = 2
velY = 1
randX = random.choice([1, 2, 3])
randY = random.choice([1, 2, 3])
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setWindowTitle('ball move')
self.setMinimumWidth(800)
self.setMaximumWidth(800)
self.setMinimumHeight(500)
self.setMaximumHeight(500)
self.setStyleSheet('background-color: black;border:none;')
self.ballLabel = QLabel(self)
self.ballPixmap = QPixmap('ball.png')
self.resizedBallPixmap = self.ballPixmap.scaled(50, 50, Qt.KeepAspectRatio, Qt.FastTransformation)
self.ballLabel.setFixedSize(50, 50)
self.ballRotation = 10
self.ballLabel.setPixmap(self.resizedBallPixmap)
self.ballLabel.setStyleSheet('border:1px solid red;')
self.ballLabel.show()
self.ballLabel.move(0, 0)
def rotateBall(self):
self.resizedBallPixmap = self.resizedBallPixmap.transformed(
QTransform().rotate(self.ballRotation), Qt.SmoothTransformation)
# self.resizedBallPixmap = self.resizedBallPixmap.transformed(QTransform().translate(self.resizedBallPixmap.size().width()/2, self.resizedBallPixmap.size().height()/2))
self.ballLabel.setPixmap(self.resizedBallPixmap)
def ballMove():
global x, y, velX, velY, randX, randY
if (main_window.ballLabel.pos().x() + 50) > 800:
velX = -1
randX = random.choice([1, 2, 3])
randY = random.choice([1, 2, 3])
elif main_window.ballLabel.pos().x() < 0:
velX = 1
randX = random.choice([1, 2, 3])
randY = random.choice([1, 2, 3])
elif (main_window.ballLabel.pos().y() + 50) > 500:
velY = -1
randX = random.choice([1, 2, 3])
randY = random.choice([1, 2, 3])
elif main_window.ballLabel.pos().y() < 0:
velY = 1
randX = random.choice([1, 2, 3])
randY = random.choice([1, 2, 3])
x += velX*randX
y += velY*randY
main_window.rotateBall()
main_window.ballLabel.move(x, y)
if __name__ == "__main__":
app = QApplication([])
main_window = MainWindow()
main_window.show()
timer = QTimer()
timer.timeout.connect(ballMove)
timer.start(1000)
app.exec_()
解决方案
解释:
要理解问题,self.resizedBallPixmap
必须使用以下代码分析大小:
def rotateBall(self):
print(self.resizedBallPixmap.size())
# ...
输出:
PyQt5.QtCore.QSize(50, 50)
PyQt5.QtCore.QSize(59, 58)
PyQt5.QtCore.QSize(70, 68)
PyQt5.QtCore.QSize(81, 80)
PyQt5.QtCore.QSize(94, 93)
PyQt5.QtCore.QSize(110, 108)
PyQt5.QtCore.QSize(128, 126)
PyQt5.QtCore.QSize(149, 147)
PyQt5.QtCore.QSize(173, 171)
PyQt5.QtCore.QSize(201, 199)
PyQt5.QtCore.QSize(233, 231)
PyQt5.QtCore.QSize(271, 268)
PyQt5.QtCore.QSize(314, 311)
...
如您所见,QPixmap 的大小在变化,为什么会变化?因为在旋转矩形时,前内接矩形必须更大,是什么导致矩形更大?好吧,QLabel 的大小不足以绘制 QPixmap,它只绘制左侧部分,让用户观察到球在前进。
解决方案:
解决方案是,当 QPixmap 旋转时,它会被剪切,只保留必要的部分。另外,每次旋转变换都会产生失真,所以不建议迭代同一个QPixmap,而是保持原来的QPixmap,增加旋转角度。
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
# ...
self.ballLabel.move(0, 0)
self.angle = 0
def rotateBall(self):
self.angle += self.ballRotation
pixmap = self.resizedBallPixmap.transformed(
QTransform().rotate(self.angle), Qt.FastTransformation
)
r = QtCore.QRect(self.resizedBallPixmap.rect())
r.moveCenter(pixmap.rect().center())
pixmap = pixmap.copy(r)
self.ballLabel.setPixmap(pixmap)
更好的解决方案是使用 Qt 图形框架的元素,例如实现旋转和平移的 QGraphicsItems。
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setWindowTitle("ball move")
self.setFixedSize(800, 500)
scene = QGraphicsScene()
scene.setSceneRect(QRectF(QPointF(), QSizeF(self.size())))
view = QGraphicsView(scene)
self.setCentralWidget(view)
self.setStyleSheet("background-color: black;border:none;")
pixmap = QPixmap("ball.png").scaled(
50, 50, Qt.KeepAspectRatio, Qt.FastTransformation
)
self.ballLabel = scene.addPixmap(pixmap)
self.ballLabel.setTransformOriginPoint(self.ballLabel.boundingRect().center())
class BallManager(QObject):
positionChanged = pyqtSignal(QPointF)
angleChanged = pyqtSignal(float)
def __init__(self, parent=None):
super(BallManager, self).__init__(parent)
self.pos = QPointF(0, 0)
self.angle = 0
self.vel = QPointF(2, 1)
self.rand = QPointF(*random.sample([1, 2, 3], 2))
self.step_angle = 10
self.timer = QTimer(interval=1000, timeout=self.ballMove)
self.timer.start()
def ballMove(self):
if (self.pos.x() + 50) > 800:
self.vel.setX(-1)
self.randX = QPointF(*random.sample([1, 2, 3], 2))
elif self.pos.x() < 0:
self.vel.setX(1)
self.rand = QPointF(*random.sample([1, 2, 3], 2))
elif (self.pos.y() + 50) > 500:
self.vel.setY(-1)
self.rand = QPointF(*random.sample([1, 2, 3], 2))
elif self.pos.y() < 0:
self.vel.setY(1)
self.rand = QPointF(*random.sample([1, 2, 3], 2))
self.pos += QPointF(self.vel.x() * self.rand.x(), self.vel.y() * self.rand.y())
self.angle += self.step_angle
self.positionChanged.emit(self.pos)
self.angleChanged.emit(self.angle)
if __name__ == "__main__":
app = QApplication([])
main_window = MainWindow()
main_window.show()
manager = BallManager()
manager.positionChanged.connect(main_window.ballLabel.setPos)
manager.angleChanged.connect(main_window.ballLabel.setRotation)
app.exec_()
推荐阅读
- python - 为什么每次我在 python 中运行代码时我的 PCA 都会发生变化?
- express - 我应该在生产中使用代理中间件吗?
- javascript - WordPress Ajax Uncaught ReferenceError: url is not defined
- azure-devops - 仅在 ARM 模板部署后才知道 ConnectionString 时如何生成 EF Core 迁移脚本?
- javascript - 重要的 npm install @babel/plugin-transform-react-jsx 不起作用
- ansible - 如何将我的文件的包含与ansible进行比较?
- react-native - React Native Calendars:如何使用 Redux 仅更改一项的背景颜色
- sql - 如何使用两列转置表格
- mongodb - 有没有办法在删除集合后释放被 mongodb 索引占用的 RAM?
- node.js - Visual Studio 2017 中未出现的建议