0

When I run the program before cythonize, with pure .py files everything works as it should, the method connected to valueChanged is called when the animation value changes. But, after I did build cythonize, and the program runs from .pyd extensions the method connected to the valueChanged signal is never called.

Although I checked the same thing with the QVariantAnimation finished signal and it is called, everything works, it is with valueChanged that this is a problem.

Python version: 3.11 PyQt6 version: 6.4.0 Cython version: 3.0.0a11 OS: Windows 11

Here's an example of how it looks in my code:

from PyQt6.QtCore import QAbstractAnimation, QVariant, QVariantAnimation, pyqtSlot
from PyQt6.QtWidgets import QHBoxLayout, QPushButton, QWidget


class SampleWidget(QWidget):
    def __init__(self, parent: QWidget | None = None) -> None:
        super().__init__(parent)
        horizontal_layout = QHBoxLayout()
        horizontal_layout.setContentsMargins(0, 0, 0, 0)
        horizontal_layout.setSpacing(0)

        self.button = QPushButton(self)
        self.button.setText("Start animation")
        self.button.clicked.connect(lambda: self.start_animation(180))
        horizontal_layout.addWidget(self.button)

        self.setLayout(horizontal_layout)

    def start_animation(self, value: float) -> None:
        self._animation = QVariantAnimation(self)
        self._animation.setStartValue(0)
        self._animation.setEndValue(value)
        self._animation.setDuration(400)
        self._animation.valueChanged.connect(self._on_animation_value_changed)
        self._animation.finished.connect(self._on_animation_finished)
        self._animation.start(QAbstractAnimation.DeletionPolicy.DeleteWhenStopped)

    @pyqtSlot()
    def _on_animation_finished(self) -> None:
        # This executes as it should be after cythonize.
        print("Animation finished")

    @pyqtSlot(QVariant)
    def _on_animation_value_changed(self, value: float) -> None:
        # Here is problem. This is never executes after cythonize.
        print("Animation value changed to", value)

And cythonize function:

...

cythonize(
        module_list=extension_modules,
        # Don't build in source tree (this leaves behind .c files)
        build_dir=BUILD_DIR,
        # Don't generate an .html output file. This will contain source.
        annotate=False,
        # Tell Cython we're using Python 3
        compiler_directives={"language_level": "3", "always_allow_keywords": True},
        # (Optional) Always rebuild, even if files untouched
        force=True,
    )

...
monotype
  • 217
  • 2
  • 10

1 Answers1

0

I set the type annotation for value to float, although Qt sends int, because of this there was a conflict because Cython used my wrong annotation.

Many thanks to everyone for the answers and explanations, I am very grateful to you!

monotype
  • 217
  • 2
  • 10
  • Can you please explain what you mean? Right now what you've written is unclear. – musicamante Oct 29 '22 at 17:03
  • So far, what I have come to is when I set type hint "float" for **value** in "rotate_icon" and "_on_rotate_icon_value_changed" methods arguments it not works after cythonization but when I changed type hint for **value** to "int" everything started working. Idk why it is – monotype Oct 29 '22 at 17:24
  • And it turns out that the code that I originally provided in the question was working, but my old code where the type hint for **value** was "float" does not work – monotype Oct 29 '22 at 17:28
  • By the way, judging by my observations, this pyqtSlot decorator does not affect anything, so I left it as pyqtSlot(QVariant), or can it be removed overall? Why is it needed? – monotype Oct 29 '22 at 17:39
  • 1
    Setting annotation to `float` causes Cython to type the value as a __C__ `double`. Setting it to `int` types the value as a __Python__ `int` (because not all Python `int`s can be converted to a C `int`) (note this is depends a little on the Cython version). I suspect the value from Qt is passed as a `QVariant`. Cython probably doesn't know how to convert this to `double` and so fails. I'm not sure what PyQt does with Python exceptions. – DavidW Oct 29 '22 at 19:43
  • Your question is lacking an [mre] though, and apparently contradicts your answer, so if it's pretty hard to know what's going on. – DavidW Oct 29 '22 at 19:44
  • @DavidW I slightly changed the code exactly to the one that should have been originally in the question (in which this problem occurs, namely, it occurs when the type annotation is: float) and added a comment in the code. – monotype Oct 29 '22 at 20:02
  • @monotype for general usage, the pyqtSlot is not relevant: The only situations of its requirement are: 1. performance is important, since a decorated slot is slightly faster and requires a bit less memory, but this makes sense only when you have hundreds of objects with dozens of functions connected to signals, they are emitted very often and/or require smaller response time; 2. special cases of cross-thread communication; 3. where a Qt slot is expected (for instance, plugins for custom widgets in Designer). See [this related post](https://stackoverflow.com/q/40325953/2001654). – musicamante Oct 29 '22 at 21:21
  • @DavidW I updated the question again with a minimal reproducible example but only for widget, if you know Cython then please do cythonize yourself. – monotype Oct 29 '22 at 21:47
  • So far, as I understand it, this does not work because of the `float` annotation, if you change it to `int` then everything works as it should. Is this how it should be or is it some kind of bug? – monotype Oct 29 '22 at 21:50
  • 1
    @monotype If you use integer values for both start and end values, then you'll always get an animation using integer values, so `valueChanged` will always need the appropriate annotation (`int`) **if** you need annotations; if start and end values are floating, then you'll always get a `float` argument for `valueChanged`, and in that case the annotation would be that. For big value ranges (or short durations), you can normally keep integer values for animations, but if you have smaller ranges or long durations (or need more precision), always use floats. Start/end types *must* be the same. – musicamante Oct 29 '22 at 22:50
  • 1
    I wasn't quite able to figure out what's going on. But as a general guide: Cython uses annotations for _exact_ type checking while Python ignores them. This is not a bug. But it does mean you may get errors if the annotations don't match Qt (and remember that Qt is passing `QVariant` rather than `int`/`float`). If you want to disable this then turn off this use of annotations with Cython's `annotation_typing` compiler directive. – DavidW Oct 30 '22 at 08:30
  • @DavidW So I set the type annotation for `value` to `float`, although Qt sends `int`, because of this there was a conflict. I set annotation_typing=False to avoid such problems, but the whole problem was due to the fact that I did not specify the type annotation correctly. – monotype Oct 30 '22 at 10:31
  • That's definitely my understanding of the problem. – DavidW Oct 30 '22 at 10:48