I'm currently in the process of porting a large application from Py2/PySide 1.2.4 to Py3/PySide2 5.13.0 and I'm discovering a DeprecationWarning related to the use of QPixmapCache.find(key, pixmap).
c:\path\to\module.py:220: DeprecationWarning: QPixmapCache.find(const QString & key, QPixmap & pixmap) is deprecated
if (QPixmapCache.find("image_key", pixmap) is False):
I would like to fix this deprecation warning, but the documentation isn't very helpful as it:
Actually at one place explicitly recommends the use of the deprecated function. (PySide2.QtGui.QPixmapCache.find(key))
Has two entries for static PySide2.QtGui.QPixmapCache.find(key, pixmap)
One is listed as deprecated.
The other is not.
Seems to have no advice on what the modern usage would be. (Or I didn't find it).
So, what is the recommended fix for the deprecated usage of PySide2.QtGui.QPixmapCache.find(key, pixmap)?
As #ekhumoro points out it looks like a bug, but the following method currently works using the QPixmapCache::Key:
from PySide2 import QtGui
if __name__ == '__main__':
import sys
app = QtGui.QGuiApplication(sys.argv)
filename = "test.png"
key = QtGui.QPixmapCache.Key()
pm = QtGui.QPixmap()
for i in range(100):
pix = QtGui.QPixmapCache.find(key)
if pix is None:
pm.load(filename)
key = QtGui.QPixmapCache.insert(pm)
print("load from filename")
else:
pm = pix
Output:
load from filename
The recommended fix is to avoid passing a pixmap as the second argument to find, since it is not really needed (see below). So your code should simply change to:
pixmap = QPixmapCache.find("image_key")
if pixmap is None:
...
The inclusion of the methods which take a second argument seems to be a bug (or misfeature) in PySide2. It probably should have only ever implemented these two overloads:
find(str) -> QPixmap
find(QPixmapCache.Key) -> QPixmap
The other methods are more specific to C++, where they are currently defined like this:
find(const QString &key, QPixmap *pixmap) -> bool
find(const QPixmapCache::Key &key, QPixmap *pixmap) -> bool
The second argument here is a pointer which Qt will set to the found pixmap. It has to be done this way in C++, because there is no way to return a tuple of (bool, QPixmap), as might have been done in Python. And by the same token, it makes little sense to implement such methods in PySide, because there are no pointers in Python. (I would guess that the deprecated method uses something like QPixmap.swap on the passed in argument to get similar behaviour).
The confusion in the current API/documentation should be reported on the PySide bug tracker. As a point of reference, PyQt5 only implements the first two methods shown above, which seems the most pythonic way of doing things. It's hard to see a good reason why any other overloads should be included (other than for backwards compatibility).
So actually the one-argument version PySide2.QtGui.QPixmapCache.find(key) also raises a DeprecationWarning. In the end it had to be fixed with in the way #eyllanesc proposed, which was slightly inconvenient in my case, as I generated the keys beforehand from hashed data. Upvoted both answers and accepted the one from #eyllanesc. Thanks!
Related
I don't do a lot of work in GUI's, but I've decided to move from occasionally using PyQt4 to PyQt5. My IDE is giving me warnings about some of the __init__ functions, particularly QWidget and QMainWindow.
If you look at the IntelliSense'd parameters, you'll see that the parentparameter has a default and the flags does not. The IDE tells me that flags is unfilled, but when I don't provide it, nothing happens. Why is this happening?
I'm using Python 3.5.
The correct signature is this:
QMainWindow(parent: QWidget = None,
flags: Union[Qt.WindowFlags, Qt.WindowType] = Qt.WindowFlags())
So it looks like the IntelliSense in your IDE either does not know how to parse type-hints properly, or the PyQt stub files need to be installed. There are only two arguments: parent and flags, both of which have defaults.
(NB: you should never use self.__class__ with super as it can lead to an infinite recursion under certain circumstances. Always pass in the subclass as the first argument - unless you're using Python 3, in which case you can omit all the arguments).
Installing pyQt5-stubs will fix this error.
I'm writing a Python (using 3.8.2 version) app for my project. In the app I use PySide2 to create QTableWidget object called items_tableWidget. Here's the fragment of my code, which creates QTableWidgetItem objects, fills them with my data, makes them not editable by user and puts them into the table:
cell = QtWidgets.QTableWidgetItem()
cell.setData(Qt.DisplayRole, data)
cell.setFlags(cell.flags() & ~Qt.ItemIsEditable)
ui.items_tableWidget.setItem(row_number, column_number, cell)
I get this warning when running the code:
C:\Users\Deronek\Documents\Qt\SkyblockBazaar\main.py:346: DeprecationWarning: an integer is required (got type PySide2.QtCore.Qt.ItemFlags). Implicit conversion to integers using __int__ is deprecated, and may be removed in a future version of Python.
cell.setFlags(cell.flags() & ~Qt.ItemIsEditable)
I am a newbie to Python, but I think this warning doesn't make sense, because setFlags argument, as well as flags() return value and ItemIsEditable enum value all have defined types and operators in Qt namespace, so I am not performing an implict conversion. Am I right or I am missing something?
Thank you for your answers.
In Python 3.8 there has been the following change:
Constructors of int, float and complex will now use the __index__()
special method, if available and the corresponding method __int__(),
__float__() or __complex__() is not available. (Contributed by Serhiy Storchaka in bpo-20092.)
And PySide2 uses the ints to map the enumerations and it has already been reported PYSIDE-1226. And as they point out in the comments in the next release, that warning will no longer be released.
In general you shouldn't have a problem and just get that annoying warning.
I'm trying to use the typing module to document my Python package, and I have a number of situations where several different types are allowable for a function parameter. For instance, you can either pass a number, an Envelope object (one of the classes in my package), or a list of numbers from which an Envelope is constructed, or a list of lists of numbers from which an envelope is constructed. So I make an alias type as follows:
NumberOrEnvelope = Union[Sequence[Real], Sequence[Sequence[Real]], Real, Envelope]
Then I write the function:
def example_function(parameter: NumberOrEnvelope):
...
And that looks great to me. However, when I create the documentation using Sphinx, I end up with this horrifically unreadable function signature:
example_function(parameter: Union[Sequence[numbers.Real], Sequence[Sequence[numbers.Real]], numbers.Real, expenvelope.envelope.Envelope])
Same thing also with the hints that pop up when I start to try to use the function in PyCharm.
Is there some way I can have it just leave it as "NumberOrEnvelope". Ideally that would also link in the documentation to a clarification of what "NumberOrEnvelope" is, though even if it didn't it would be way better than what's appearing now.
I had the same issue and used https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autodoc_type_aliases, introduced in version 3.3.
In your sphinx conf.py, insert this section. It does not seem to make much sense at the first sight, but does the trick:
autodoc_type_aliases = dict(NumberOrEnvelope='NumberOrEnvelope')
Warning: It only works in modules that start with from __future__ import annotation
Note: If there is a target in the documentation, type references even have a hyperlink to the definition. I have classes, documented elsewhere with autoclass, which are used as types of function parameters, and the docs show the nice names of the types with links.
Support for this appears to be in the works.
See Issue #6518.
That issue can be closed by the recent updates to Pull Request #8007 (under review).
If you want the fix ASAP, you can perhaps try using that build.
EDIT: This doesn't quite work, sadly.
Turns out after a little more searching, I found what I was looking for. Instead of:
NumberOrEnvelope = Union[Sequence[Real], Sequence[Sequence[Real]], Real, Envelope]
I found that you can create your own compound type that does the same thing:
NumberOrEnvelope = TypeVar("NumberOrEnvelope", Sequence[Real], Sequence[Sequence[Real]], Real, Envelope)
This displays in documentation as "NumberOrEnvelope", just as I wanted.
I'm writing an MPRIS player, which communicates with clients over
dbus. I need to emit a signal when my playback state changes. However,
the signal requires a format of (sa{sv}as), and my code is producing
(sa{sv}av). Here's the important part:
self.signal = QDBusMessage.createSignal(
"/org/mpris/MediaPlayer2",
"org.freedesktop.DBus.Properties",
"PropertiesChanged"
)
self.signal.setArguments(
[interface, {property: values}, ['']]
)
The problem is the third item in the list given to setArguments. It is
an empty string in a list because I need to produce a type of 'array
of string' (as) but pyqt5 translates that into 'array of variant' (av).
I never need to put any actual data in that list, I just need the type
signature to be correct.
Is there any way to do this in PyQt5? Perhaps using QDBusArgument?
I... I got it working. Wow. That was a campaign.
I don't know what is going wrong exactly, I wasn't able to dig out of the PyQt5 source where exactly the conversion happens. However, I did look into QDbusArgument(). There is no documentation for this in python, and the C++ docs are worthless due to major differences, so I took to the source code. in sip/QtDbus/qdbusargument.sip, I discovered a completely undocumented new method called qdbusargument_add. This maps to QDbusArgument().add() in python. It is used to add arguments with an explicit type id to a QDbusArgument. And it has a special case for QStringList!
From then I just bruteforced every possibility I could think of with arguments to QDbusArgument(), and finally got the following:
def PropertiesChanged(self, interface, property, values):
"""Sends PropertiesChanged signal through sessionBus.
Args:
interface: interface name
property: property name
values: current property value(s)
"""
emptyStringListArg = QDBusArgument()
emptyStringListArg.add([""], QMetaType.QStringList)
self.signal.setArguments([interface, {property: values}, emptyStringListArg])
QDBusConnection.sessionBus().send(self.signal)
It'd be great if the add() function could be documented, but I can't seem to send messages to the PyQt5 mailing list. Do I have to register first?
I want to select 5 images with Python so that I can use these imges in my python program. I tried to do this with QFileDialog() (PyQt5) but I only succeed to single select a file.
And how to select a folder is also not really comprehensive.
I just want to select 5 images and select a folder so that I can save files in that folder. But it seems to be not so easy to do that.
I really like Python because its so easy but PyQt5 makes me everytime I use it just aggressive, all other libraries are just nice and easy to understand.
Maybe there is a good alternative to pyqt? tkinter maybe?
thanks.
In order to select a folder you can use this code:
widget = QWidget()
dialog = QFileDialog(
widget, "Select Directory of the Desired Files", os.path.curdir
)
dialog.setFileMode(QFileDialog.DirectoryOnly)
Qt supplies a bunch of static methods to get standardized file dialogs, two of them already satisfy your needs: getOpenFileNames() (stress on the final "s") and getExistingDirectory().
The first will return a list of absolute paths of selected file[s], the last will return the selected directory.
I know that reading the official documentation might be a bit overwhelming if you don't know anything about C++ (they are explained in detail, though), but they're not as hard as one could think.
Every function is listed in a very simple way:
returned_type : function_name(arguments) [const -> you can usually ignore this]
The returned_type is the type of the value the function is expected to return. In "c++ slang", void is the same as return (or return None or no return at all, as Python implicitly returns None if no other value/object is returned at the end of a function), if the type is a QString it's automatically converted to a Python str, while qreal is the same as Python's floats,. This is very important for "private" functions (methods), which are internally used by Qt: if you are subclassing and want to override a private method of a Qt class, you have to return the type Qt expects. You could theoretically ignore the returned_type for public functions if you know what you're doing, but it's usually better to stick with the original type.
There are some small "exceptions" that require some consideration. In some cases Qt expects some argument that will be modified within the function and would normally return whether the function was successful or not, while in Python it might return the reference to the argument (sorry, I can't remember them right now). Some other functions return a tuple instead of a single value, and that's the case of some static QFileDialog functions such as getOpenFileName[s] which return both the selected file[s] and the selected filter.