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

FigureCanvasQT backend_qt5agg bug (backend_qt4agg) works #3656

Closed
atsju opened this issue Oct 17, 2014 · 15 comments · Fixed by #4312
Closed

FigureCanvasQT backend_qt5agg bug (backend_qt4agg) works #3656

atsju opened this issue Oct 17, 2014 · 15 comments · Fixed by #4312
Assignees
Milestone

Comments

@atsju
Copy link

atsju commented Oct 17, 2014

In this example,
http://matplotlib.org/examples/user_interfaces/embedding_in_qt4.html (embedding_in_qt4.py)
When replacing
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg
through
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg

I got this error
File "C:\Python34\lib\site-packages\matplotlib\backends\backend_qt5.py", line 238, in init
super(FigureCanvasQT, self).init(figure=figure)

When looking at class FigureCanvasQT in backend_qt5.py and backend_qt4.py it seems that super is calling init on first inherited class (QtWidgets.QWidget) instead of second one (FigureCanvasBase).

Let me know if I was not clear enough and if it is realy a bug or a missusage.

@atsju atsju changed the title backend_qt5agg bug (backend_qt4agg) works FigureCanvasQT backend_qt5agg bug (backend_qt4agg) works Oct 17, 2014
@jenshnielsen
Copy link
Member

This sounds similar to #3623. It turned out that this was because I had the env variable QT_API set to pyqt which resulted in pyqt4 being imported into the qt5 backend. This is fixed in 1.4.1rc1

That being said the line still looks wrong to me. You are right that super will first call init on the first inherited class and if not found move on according to the method resolution order: http://stackoverflow.com/questions/1848474/method-resolution-order-mro-in-new-style-python-classes
This should not work since init of QtWidgets.QWidget doesn't take a figure keyword argument.

But any combination of explicitly calling init on the two parent classes that I have tried fails.

@atsju
Copy link
Author

atsju commented Oct 17, 2014

Could you be more specific ? What should I do
-Use matplotlib 1.4.1rc1 (download from where and how to install SIMPLY under windows ?)
OR
-Set QT_API to someting other (how and to what ?)

@jenshnielsen
Copy link
Member

The easies is to check if the environmental variable is set.
You should be able to check the variable from python with something like:

import os
print os.environ.get('QT_API')

If this prints either pyqt or pyside it is likely this issue that you are seeing. Otherwise something else is wrong.

Without knowing how you have installed python and matplotlib it is hard to tell you how to install matplotlib but usually nothing is easy or simple on windows... If you have installed all the packages from scratch you might be able to use the one from here http://www.lfd.uci.edu/~gohlke/pythonlibs/#matplotlib
build by Christoph Gohlke

@atsju
Copy link
Author

atsju commented Oct 17, 2014

QT_API isn't set.
I'm using QT5.3.2 python 3.4.1 PyQT4
Used mathplotlib1.4.0
Uninstalled 1.4.0 and installed 1.4.1.rc1
Still the same error.

When I import FigureCanvasQTAgg from backend_qt4agg it is working well even if I have only QT5 installed and not QT4.

@tacaswell tacaswell modified the milestone: v1.4.x Oct 17, 2014
@jenshnielsen
Copy link
Member

Thanks for testing this confirms that there is some deeper problem somewere. I will try to have a closer look of what is happening but it might take some time

@atsju
Copy link
Author

atsju commented Oct 17, 2014

I workaround using qt4agg. Hope I was helpfull.
Thank you for your ultraquick responses

@jenshnielsen
Copy link
Member

Finally got a chance to look at this. I believe that this issue is due to a mixture of qt4 and qt5 being imported by different imports. The example added in #4312 shows how to embed in qt5. @atsju if you are still interested it would be great if you can test that example.

@atsju
Copy link
Author

atsju commented Apr 5, 2015

Hello,
Thank you for keeping me updated.
I'm really sorry but I haven't access to the computer I used anymore (I haven't Qt and matplotlib installed). I not even remember why I used PyQT4 and not PyQT5 at the time of this bug. I would not do better than the tests you already did for the fix.
Thank you very much I realy appreciate your work. Sorry I can't do more.

@jenshnielsen
Copy link
Member

No problem, Just wanted to keep you up to date.

@jrversteegh
Copy link
Contributor

As far as I can tell, this issue still exists:

MPLBACKEND=Qt5Agg python3 -c "import matplotlib.pyplot as plt; fig=plt.figure()"

@tacaswell
Copy link
Member

@jrversteegh Do you have pyqt5 installed?

@jrversteegh
Copy link
Contributor

Only just after I ran this test ;), so automatic fallback to pyqt4 is the issue here. I guess it shouldn't do that when I explicitly specify the Qt5Agg backend. It should just complain that PyQt5 or PySide2 isn't installed.
PEP 20: in the face of ambiguity, refuse the temptation to guess :)

@jrversteegh
Copy link
Contributor

The issue that brought me here remains: an error in the same place with PySide2.

/matplotlib/backends/backend_qt5.py", line 240, in __init__
    super(FigureCanvasQT, self).__init__(figure=figure)
AttributeError: 'figure()' is not a Qt property or a signal

but I guess that's a PyQt5 <-> PySide2 compatibility issue.

@tacaswell
Copy link
Member

Does pyside2 not do cooperative inheritance? If so, it just needs to keep going through the qt4 shims.

@jrversteegh That should get it's own issue.

@adam-grant-hendry
Copy link

adam-grant-hendry commented Jun 27, 2022

@tacaswell PySide2 and PySide6 both do not yet properly support cooperative multiple inheritance:

There are many different bugs stemming from this documented in the Qt for Python Development Notes 2021

Secondly, PyQt and PySide only accept keyword arguments for optional arguments (i.e. those that are specified as having default values in the docs). See the PyQt4 docs and PYSIDE-1964.

Therefore, figure is getting passed to the QWidget as a keyword argument, which creates the error that gets output by pyside.cpp::fillQtProperties.

To work around this, matplotlib currently does the following:

# To work around lack of cooperative inheritance in PySide2 and
# PySide6, when calling FigureCanvasQT.__init__, we temporarily patch
# QWidget.__init__ by a cooperative version, that first calls
# QWidget.__init__ with no additional arguments, and then finds the
# next class in the MRO with an __init__ that does support cooperative
# inheritance (i.e., not defined by the PyQt4 or sip, or PySide{,2,6}
# or Shiboken packages), and manually call its `__init__`, once again
# passing the additional arguments.

This method was recommended by Raymond Hettinger in his PyCon 2015 talk Super considered super! and his corresponding blog post

Update

Note that this is a PySide problem, but in PyQt only this was resolved in v5.15.4 (see Support for Cooperative Multi-inheritance).

It is important to recognize that the tool PyQt uses to generate python bindings from C++ Qt source code is called sip. The tool PySide uses is called shiboken ('Side' is Finnish for binding, and its progenitor Nokia is a Finnish company). Both behave differently as they were developed independently.

ASIDE: I have no articles or quotes to back this up, but I imagine "sip" is a cute (haha, get it, "cute"; Qt is pronounced "cute") play on words with Python's pip, or perhaps SWIG for C/C++.

However, for shiboken, contributor Marcelo Lira blogged:

Before going on with this, allow me to explain that Shiboken means absolutely nothing. Not buddhist void, I just mean that the word Shiboken has no meaning attached to it. Except, of course, “generator of CPython based binding code for C/C++ libraries”.



Disclaimer: I don’t know a thing about Japanese language and the above kanjis are just something that I found at wikitionary to match the sounds of Shiboken. Forgive me, Lauro. 🙂

Shiboken (pronounced Shi bō ken, 死某剣), if translated from the kanjis, means "death certain sword" or "death sword". I can't help but think the name was a "poke" (get it, poke) at "sip", though it is claimed to mean nothing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants