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

UnicodeEncodeError when trying to save a figure #4275

Closed
Nodd opened this issue Mar 25, 2015 · 8 comments · Fixed by #4278
Closed

UnicodeEncodeError when trying to save a figure #4275

Nodd opened this issue Mar 25, 2015 · 8 comments · Fixed by #4278
Assignees
Labels
Milestone

Comments

@Nodd
Copy link
Contributor

Nodd commented Mar 25, 2015

Run the following code under python2, then try to save the figure but clicking on the floppy button :

import matplotlib.pyplot as plt
fig = plt.figure()
fig.canvas.set_window_title(u"unïcode")
plt.show()

The window for choosing a filename doesn't open and this traceback is printed:

Traceback (most recent call last):
  File "/usr/lib/python2.7/site-packages/matplotlib/backends/backend_qt5.py", line 707, in save_figure
    start = os.path.join(startpath, self.canvas.get_default_filename())
  File "/usr/lib/python2.7/site-packages/matplotlib/backend_bases.py", line 2254, in get_default_filename
    default_filename = self.get_window_title() or 'image'
  File "/usr/lib/python2.7/site-packages/matplotlib/backend_bases.py", line 2239, in get_window_title
    return self.manager.get_window_title()
  File "/usr/lib/python2.7/site-packages/matplotlib/backends/backend_qt5.py", line 560, in get_window_title
    return str(self.window.windowTitle())
UnicodeEncodeError: 'ascii' codec can't encode character u'\xef' in position 2: ordinal not in range(128)

It works fine on python3.

@mdboom
Copy link
Member

mdboom commented Mar 25, 2015

I don't know why that str is there -- it appears to go way back to at least the Qt3 days. Removing it seems to work fine for Qt5, Qt4 and PySide.

@mfitzp: You appear to be the last major contributor to the Qt stuff. Do you see any downside to just removing the str() in get_window_title and thereby allowing a unicode string to pass through unaltered down to the Qt level?

@mdboom mdboom added the GUI: Qt label Mar 25, 2015
@mdboom mdboom added this to the next point release milestone Mar 25, 2015
@mdboom mdboom self-assigned this Mar 25, 2015
@mfitzp
Copy link
Member

mfitzp commented Mar 25, 2015

This exists for compatibility with "version 1" of the PyQt(4) API. In this version the APIs return QVariant types rather than native Python strings and require explicit conversion. The code here is just performing that by default to avoid requiring multiple versions of code for the two APIs.

There is some more information on the Riverbank docs for PyQt4. It notes that v2 of the API is default for Python 3 (I also think it is the default for matplotlib generally in PyQt4). In PyQt5 this distinction doesn't exist.

I guess there are a few options:

  • Remove the str() wrapper and return a bare QVariant type on v1 of the API. I'm not totally clued up on QVariant behaviour, e.g. whether it will work with os.path.join. @mdboom Can you check with your PyQt4 install by setting QT_API_PYQT = 'PyQt4' and seeing if the save figure path still works?
  • Check for QVariant instance and convert only if it is
  • Replace the string str() wrapper with a Python 2 & 3 compatible alternative. Just noticed that matplotlib depends on six which may have a helper for this.

What do you think?

@mfitzp
Copy link
Member

mfitzp commented Mar 25, 2015

The following replacement could be made on a subclassed Qt4 version of FigureManagerQT and then the str() can be removed completely from the Qt5 code (this follows the planned design for using the newer API as the base, and patching to keep old APIs working):

def get_window_title(self):
    s = self.window.windowTitle()

    # For PyQt4 v1 API check if we get a QVariant
    if isinstance(s, QtCore.QVariant):
        return s.toPyObject()

    return s

Let me know if this looks OK and I'll put together a PR.

@mdboom
Copy link
Member

mdboom commented Mar 25, 2015

Thanks. The fact that this is also used for the save figure path was forgotten, so wasn't included in my testing where I said "everything worked". In that event, actually none of the alternatives work (Qt4 old/new, PySide, Qt5) work on Python 2.7. So it looks like what you propose above work work either.

Just to confirm, when you say set QT_API_PYQT = 'PyQt4', you mean as an environment variable? If that's not right, then I'm probably not testing the v1 API correctly.

This works for me:

def get_window_title(self):
    return six.text_type(self.window.windowTitle())

Having issues getting a Python 3 / Qt environment set up, so I haven't tested there.

@mfitzp
Copy link
Member

mfitzp commented Mar 25, 2015

The above code would be used in addition to removing the str() on the PyQt5 version that is causing the problem. But now I'm looking into it it's possible to also set a v1-like API on PyQt5. I think the six approach is probably the way to go. Does that work for you with PyQt4 and the v1 API?

You're right that setting QT_API_PYQT = 'PyQt4' as an environment variable should set you on the Qt4 v1 API. You can check this by using:

import sip
sip.getapi('QString') 

It should return 1.

@mdboom
Copy link
Member

mdboom commented Mar 25, 2015

Yes -- using six.text_type works for all Python 2.7 Qt libraries and APIs (PyQt4 API v1/v2, PyQt5, PySide).

@mfitzp
Copy link
Member

mfitzp commented Mar 25, 2015

I've opened a PR with the suggested fix. Testing here it works fine, but also unable to test with Python 3 yet, will look into it.

When applying the fix I noticed there were a few other incidences of str() in the file where its possible that unicode could end up. I've replaced all of them with the six wrapper for consistency.

@Nodd
Copy link
Contributor Author

Nodd commented Mar 25, 2015

Thank you for your reactivity everyone, it is much appreciated !

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

Successfully merging a pull request may close this issue.

3 participants