Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detach PyQt6 QPixmap instance before returning #8509

Merged
merged 1 commit into from
Oct 29, 2024

Conversation

radarhere
Copy link
Member

@radarhere radarhere commented Oct 28, 2024

Alternative to #8507

The user in that PR reported seeing corrupt output from ImageQt.toqpixmap() on Windows.

Pillow/src/PIL/ImageQt.py

Lines 214 to 216 in 9a4b3e0

def toqpixmap(im: Image.Image | str | QByteArray) -> QPixmap:
qimage = toqimage(im)
return getattr(QPixmap, "fromImage")(qimage)

Testing, I found I was able to use GitHub Actions to reproduce the matter.

Testing further, I found that it was only happening when qimage was inside the function - if I unwrapped the function so that qimage wouldn't be garbage collected, the problem disappeared. I created a reproduction just using PyQt6 and asked about this at https://stackoverflow.com/questions/79133259/corrupted-display-from-qpixmap-fromimage. The response I received was

This is caused by Qt's implicit data sharing. The QImage will be garbage-collected when wrap returns, which may mean some of the shared data is no longer accesible. To avoid these problems, call pixmap.detach() to enforce a copy-on-write.

This fix allows us to test PyQt6 in GitHub Actions with CPython, which would otherwise fail.

@radarhere radarhere changed the title Detach QPixmap instance before returning Detach PyQt6 QPixmap instance before returning Oct 28, 2024
@codev8services
Copy link

This resolved the issue #8507. RGB and RGBA work correctly.

@hugovk hugovk added the Windows label Oct 28, 2024
@@ -213,4 +213,7 @@ def toqimage(im: Image.Image | str | QByteArray) -> ImageQt:

def toqpixmap(im: Image.Image | str | QByteArray) -> QPixmap:
qimage = toqimage(im)
return getattr(QPixmap, "fromImage")(qimage)
pixmap = getattr(QPixmap, "fromImage")(qimage)
if qt_version == "6":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This "6" means PyQt, and "side6" would mean PySide6.

Do we know if this also affects PySide6?

Copy link
Member Author

@radarhere radarhere Oct 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unable to replicate the problem on Windows on GitHub Actions with PySide6.

If I run the test suite with PySide6 without this fix, it passes.

Furthermore, detach() doesn't exist in PySide6 - https://doc.qt.io/qtforpython-6/PySide6/QtGui/QPixmap.html / https://github.com/python-pillow/Pillow/actions/runs/11554805812/job/32158808074#step:6:5698. It is possible they don't offer it precisely because it isn't needed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for testing, let's merge. We'll have to see what happens when/if PyQt7 comes out in the future.

@hugovk hugovk merged commit e214dbf into python-pillow:main Oct 29, 2024
46 of 47 checks passed
@radarhere radarhere deleted the toqpixmap branch October 29, 2024 11:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Qt Qt for Python, PyQt, PySide Windows
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants