Как анимировать сцену Rectangle?

Я пытаюсь переместить свой прямоугольник QGraphicsScene с помощью анимации, чтобы создать впечатление, что он движется плавно. Но я не знаю, как это работает. Может ли кто-нибудь мне помочь? Я хотел бы знать, возможно ли анимировать экземпляр Qtransform. если это так, как я могу это сделать?

Вопросы:

1 - Как анимировать функцию перевода, которая перемещает прямоугольник моей сцены. Я хочу анимировать его, потому что хочу, чтобы он выглядел гладким.

2 - Можно ли анимировать экземпляр Qtransform?

вот мой код:

from PyQt5.QtWidgets import QWidget, QApplication, QHBoxLayout
from PyQt5 import QtGui
from PyQt5 import QtWidgets
from PyQt5 import QtCore


class Example(QWidget):

    def __init__(self, parent=None):
        super(Example, self).__init__(parent) # this widget has no parent
        hbox = QHBoxLayout(self)
        self.setMinimumHeight(500)
        self.setMinimumWidth(500)
        self.button = QtWidgets.QPushButton("move scene", self)  # this button is responsible to emit a signal to animate the
        # scene translation
        self.button.clicked.connect(self.do)  # connect to the animation
        self.scene = QtWidgets.QGraphicsScene()  # instantiate the scene
        self.view = QtWidgets.QGraphicsView(self)  # instantiate the view
        self.view.setScene(self.scene)
        hbox.addWidget(self.view)  # insert into the layout
        hbox.addWidget(self.button)  # insert into the layout
        self.r = self.view.mapToScene(self.view.viewport().rect()).boundingRect()# take the viewport bounding rectangle
        self.view.setSceneRect(self.r)  # define the viewport bounding rectangle as the initial scene rectangle
        self.scene.addEllipse(self.r, QtGui.QPen(QtGui.QBrush(QtCore.Qt.darkRed), 10, join=QtCore.Qt.RoundJoin))
        # draw an ellipse in our scene
        self.scene.setBackgroundBrush(QtGui.QBrush(QtCore.Qt.CrossPattern))  # set a grid patter to turn easier to
        # see the displacements of objects

    def translateSceneSmooth(self, ds: tuple) -> None:
        """
        ds = (dx, dy)
        :param ds: is the differential in x and y.
        :return: None
        """
        self.view.setSceneRect(self.view.sceneRect().translated(*ds)) # I want  that the animation pass a interpolation
        # of a tuple here: starting at (0, 0) and ending at (100, 100)
        # I don't know if this approach is correct.
        # Because maybe it will not  move 100 px.if I have a list of numbers in the form passing through the function:
        # [0, 10, 20, 50, 70, 100] maybe it'll move 0+10+20+50+70+100 = 250 px

    def do(self, duration=100):
        """
        I want the  scene rectangle to move smoothly
        """
        print('Starting animation')
        self._animation = QtCore.QVariantAnimation()
        self._animation.setStartValue(0)
        self._animation.setEndValue(1000)
        self._animation.setDuration(duration)
        self._animation.start(QtCore.QAbstractAnimation.DeleteWhenStopped)
        self._animation.valueChanged.connect(self.translateSceneSmooth)
        print('Ending animation')


if __name__ == "__main__":
    app = QApplication([])
    ex = Example()
    ex.show()
    app.exec_()

person cap    schedule 21.12.2020    source источник
comment
Вы уверены, что хотите анимировать прямоугольник сцены? Если вы хотите переместить эллипс, вы должны переместить его, а не прямоугольник сцены.   -  person musicamante    schedule 21.12.2020
comment
Ваш вопрос сбивает с толку, вы хотите переместить сцену или вы хотите переместить предметы?   -  person eyllanesc    schedule 21.12.2020
comment
@eyllanesc Я хочу переместить прямоугольник сцены с помощью анимации. Чтобы переместить его без анимации, мне просто нужно «обновить» прямоугольник сцены, используя это: self.view.setSceneRect(self.view.sceneRect().translated(dx, dy)) но с анимацией я не знаю, как сделай это.   -  person cap    schedule 21.12.2020
comment
@cap еще раз, вы действительно уверены, что хотите переместить прямоугольник сцены? Из того, что мы видим, в этом нет необходимости, вместо этого следует перемещать элемент эллипса, можете ли вы пояснить, почему вам нужно перемещать сцену?   -  person musicamante    schedule 21.12.2020
comment
@musicamante да, да, сэр. Я уверен. Это просто пример, упрощение моей реальной программы. И в моей реальной программе я должен перевести сцену. И единственным решением для перевода прямоугольника сцены, которое я нашел, было использование метода: 'translate (dx, dy)'. И теперь я должен сделать этот перевод гладким, и я понятия не имею, как это сделать.   -  person cap    schedule 21.12.2020
comment
@cap хорошо, ответа от eyllanesc должно быть достаточно, но имейте в виду, что если вы постоянно пытаетесь использовать прямоугольник сцены current для перевода, вы не получите правильный результат, так как перевод приведет в сумме предыдущего перевода. Кроме того, если вы хотите иметь разные позиции/скорости, вы не можете просто использовать одну анимацию, и вместо этого вам следует рассмотреть возможность использования QParallelAnimation.   -  person musicamante    schedule 21.12.2020
comment
@musicamante, почему я не получу правильный результат?   -  person cap    schedule 21.12.2020
comment
@cap анимация имеет прогрессивные значения; скажем, вы начинаете с (0, 0) с шагом 10 пикселей: затем анимация отправляет (10, 10), поэтому у вас будет sceneRect в (0, 0), переведенный на (10, 10), перемещая его на ( 10, 10); следующий шаг - (20, 20), но поскольку вы используете прямоугольник сцены current для новой позиции, которая находится в (10, 10), этот шаг переместит его в (10, 10) + (20, 20), что дает (30, 30); следующий шаг анимации будет (30, 30), вы уже находитесь в (30, 30), а затем он переместится в (60, 60) и так далее.   -  person musicamante    schedule 21.12.2020
comment
@musicamante, но это то, что я хотел, большое спасибо за вашу заботу   -  person cap    schedule 22.12.2020


Ответы (1)


Если вы хотите переместить sceneRect, сгенерируйте анимацию прямоугольника, вычислив начальный и конечный прямоугольники. С другой стороны, по умолчанию нажатый сигнал передает логическое значение, которое переопределяет значение длительности по умолчанию. Возможным решением является использование декоратора pyqtSlot, чтобы сделать сигнатуру соединения явной:

from PyQt5 import QtCore, QtGui, QtWidgets


class Example(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Example, self).__init__(parent)

        self.setMinimumSize(500, 500)
        self.button = QtWidgets.QPushButton("move scene")
        # scene translation
        self.button.clicked.connect(self.do)
        self.scene = QtWidgets.QGraphicsScene()
        self.view = QtWidgets.QGraphicsView()
        self.view.setScene(self.scene)

        hbox = QtWidgets.QHBoxLayout(self)
        hbox.addWidget(self.view)
        hbox.addWidget(self.button)

        self.r = self.view.mapToScene(self.view.viewport().rect()).boundingRect()
        self.view.setSceneRect(self.r)
        self.scene.addEllipse(
            self.r,
            QtGui.QPen(QtGui.QBrush(QtCore.Qt.darkRed), 10, join=QtCore.Qt.RoundJoin),
        )
        self.scene.setBackgroundBrush(QtGui.QBrush(QtCore.Qt.CrossPattern))

    @QtCore.pyqtSlot()
    def do(self, duration=100):
        """
        I want the  scene rectangle to move smoothly
        """
        ds = QtCore.QPointF(100, 100)

        current_rect = self.view.sceneRect()
        next_rect = current_rect.translated(ds)
        self._animation = QtCore.QVariantAnimation()
        self._animation.setStartValue(current_rect)
        self._animation.setEndValue(next_rect)
        self._animation.setDuration(duration)
        self._animation.valueChanged.connect(self.view.setSceneRect)
        self._animation.finished.connect(lambda: print("Ending animation"))
        self._animation.start(QtCore.QAbstractAnimation.DeleteWhenStopped)
        print("Starting animation")


if __name__ == "__main__":
    app = QtWidgets.QApplication([])
    ex = Example()
    ex.show()
    app.exec_()
person eyllanesc    schedule 21.12.2020