How to fix the python multiprocessing matplotlib savefig() issue? - matplotlib

I want to speed up matplotlib.savefig() for many figures by multiprocessing module, and trying to benchmark the performance between parallel and sequence.
Below is the codes:
# -*- coding: utf-8 -*-
"""
Compare the time of matplotlib savefig() in parallel and sequence
"""
import numpy as np
import matplotlib.pyplot as plt
import multiprocessing
import time
def gen_fig_list(n):
''' generate a list to contain n demo scatter figure object '''
plt.ioff()
fig_list = []
for i in range(n):
plt.figure();
dt = np.random.randn(5, 4);
fig = plt.scatter(dt[:,0], dt[:,1], s=abs(dt[:,2]*1000), c=abs(dt[:,3]*100)).get_figure()
fig.FM_figname = "img"+str(i)
fig_list.append(fig)
plt.ion()
return fig_list
def savefig_worker(fig, img_type, folder):
file_name = folder+"\\"+fig.FM_figname+"."+img_type
fig.savefig(file_name, format=img_type, dpi=fig.dpi)
return file_name
def parallel_savefig(fig_list, folder):
proclist = []
for fig in fig_list:
print fig.FM_figname,
p = multiprocessing.Process(target=savefig_worker, args=(fig, 'png', folder)) # cause error
proclist.append(p)
p.start()
for i in proclist:
i.join()
if __name__ == '__main__':
folder_1, folder_2 = 'Z:\\A1', 'Z:\\A2'
fig_list = gen_fig_list(10)
t1 = time.time()
parallel_savefig(fig_list,folder_1)
t2 = time.time()
print '\nMulprocessing time : %0.3f'%((t2-t1))
t3 = time.time()
for fig in fig_list:
savefig_worker(fig, 'png', folder_2)
t4 = time.time()
print 'Non_Mulprocessing time: %0.3f'%((t4-t3))
And I meet problem "This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information." error caused by p = multiprocessing.Process(target=savefig_worker, args=(fig, 'png', folder)) .
Why ? And how to solve it ?
(Windows XP + Python: 2.6.1 + Numpy: 1.6.2 + Matplotlib: 1.2.0)
EDIT: (add error msg on python 2.7.3)
When run on IDLE of python 2.7.3, it gives below error msg:
>>>
img0
Traceback (most recent call last):
File "C:\Documents and Settings\Administrator\desktop\mulsavefig_pilot.py", line 61, in <module>
proc.start()
File "d:\Python27\lib\multiprocessing\process.py", line 130, in start
File "d:\Python27\lib\pickle.py", line 286, in save
f(self, obj) # Call unbound method with explicit self
File "d:\Python27\lib\pickle.py", line 748, in save_global
(obj, module, name))
PicklingError: Can't pickle <function notify_axes_change at 0x029F5030>: it's not found as matplotlib.backends.backend_qt4.notify_axes_change
EDIT: (My solution demo)
inspired by Matplotlib: simultaneous plotting in multiple threads
# -*- coding: utf-8 -*-
"""
Compare the time of matplotlib savefig() in parallel and sequence
"""
import numpy as np
import matplotlib.pyplot as plt
import multiprocessing
import time
def gen_data(fig_qty, bubble_qty):
''' generate data for fig drawing '''
dt = np.random.randn(fig_qty, bubble_qty, 4)
return dt
def parallel_savefig(draw_data, folder):
''' prepare data and pass to worker '''
pool = multiprocessing.Pool()
fig_qty = len(draw_data)
fig_para = zip(range(fig_qty), draw_data, [folder]*fig_qty)
pool.map(fig_draw_save_worker, fig_para)
return None
def fig_draw_save_worker(args):
seq, dt, folder = args
plt.figure()
fig = plt.scatter(dt[:,0], dt[:,1], s=abs(dt[:,2]*1000), c=abs(dt[:,3]*100), alpha=0.7).get_figure()
plt.title('Plot of a scatter of %i' % seq)
fig.savefig(folder+"\\"+'fig_%02i.png' % seq)
plt.close()
return None
if __name__ == '__main__':
folder_1, folder_2 = 'A1', 'A2'
fig_qty, bubble_qty = 500, 100
draw_data = gen_data(fig_qty, bubble_qty)
print 'Mulprocessing ... ',
t1 = time.time()
parallel_savefig(draw_data, folder_1)
t2 = time.time()
print 'Time : %0.3f'%((t2-t1))
print 'Non_Mulprocessing .. ',
t3 = time.time()
for para in zip(range(fig_qty), draw_data, [folder_2]*fig_qty):
fig_draw_save_worker(para)
t4 = time.time()
print 'Time : %0.3f'%((t4-t3))
print 'Speed Up: %0.1fx'%(((t4-t3)/(t2-t1)))

You can try to move all of the matplotlib code(including the import) to a function.
Make sure you don't have a import matplotlib or import matplotlib.pyplot as plt at the top of your code.
create a function that does all the matplotlib including the import.
Example:
import numpy as np
from multiprocessing import pool
def graphing_function(graph_data):
import matplotlib.pyplot as plt
plt.figure()
plt.hist(graph_data.data)
plt.savefig(graph_data.filename)
plt.close()
return
pool = Pool(4)
pool.map(graphing_function, data_list)

It is not really a bug, per-say, more of a limitation.
The explanation is in the last line of your error mesage:
PicklingError: Can't pickle <function notify_axes_change at 0x029F5030>: it's not found as matplotlib.backends.backend_qt4.notify_axes_change
It is telling you that elements of the figure objects can not be pickled, which is how MultiProcess passes data between the processes. The objects are pickled in the main processes, shipped as pickles, and then re-constructed on the other side. Even if you fixed this exact issue (maybe by using a different backend, or stripping off the offending function (which might break things in other ways)) I am pretty sure there are core parts of Figure, Axes, or Canvas objects that can not be pickled.
As #bigbug point to, an example of how to get around this limitation, Matplotlib: simultaneous plotting in multiple threads. The basic idea is that you push your entire plotting routine off to the sub-process so you only push numpy arrays an maybe some configuration information across the process boundry.

Related

WebGL disabled when embedding Plotly in PyQt

I am trying to embed a plotly code in a PyQt GUI. My plotly code needs to receive a huge amount of data, which was not a problem at all when it was running alone. But when I am trying to put it in a PyQt code, the performance is not good and I receive a warning about WebGL. Apparently WebGL is not activated. I tried to correct it by adding this line :
self.browser.page().settings().setAttribute(QtWebEngineWidgets.QWebEngineSettings.WebGLEnabled, True)
but it didn't change anything.
Here is my code:
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
import numpy as np
import pandas as pd
import plotly.express as px
class Widget(QtWidgets.QWidget):
def __init__(self, df, parent=None):
super().__init__(parent)
self.browser = QtWebEngineWidgets.QWebEngineView(self)
# WebGL enabled: this line appears to be useless
self.browser.page().settings().setAttribute(QtWebEngineWidgets.QWebEngineSettings.WebGLEnabled, True)
vlayout = QtWidgets.QVBoxLayout(self)
vlayout.addWidget(self.browser)
fig = px.scatter(df, x='x', y='y', color='lambda', log_x=True, log_y=True, opacity=0.10)
self.browser.setHtml(fig.to_html(include_plotlyjs='cdn'))
self.resize(1000,800)
if __name__ == "__main__":
N = 20000
col0 = 400 + 400 * np.random.random(N)
col1 = np.linspace(1,N,N) * np.random.normal(1,0.5,N)
col2 = np.linspace(1,N,N) * np.random.normal(1,5,N)
result = np.array([col0, col1, col2]).T
df = pd.DataFrame(result, columns=['lambda', 'x', 'y'])
app = QtWidgets.QApplication([])
widget = Widget(df)
widget.show()
app.exec()
Here is what I am obtaining if I am putting a value too big for N:
[1784:7704:1202/161033.541:ERROR:gles2_cmd_decoder.cc(10941)] [.WebGL-000001AB7779E4D0]PERFORMANCE WARNING: Attribute 0 is disabled. This has significant performance penalty
js: [.WebGL-000001AB7779E4D0]PERFORMANCE WARNING: Attribute 0 is disabled. This has significant performance penalty
I have to say that even if I have this warning, the code is not crashing... but is taking seriously too long when N is huge.

Why does adding a subplot to a matplotlib figure cause numpy to crash?

I made a dialog window in QtDesigner that contains a widget that contains a layout named plot_layout .
When running this code, Numpy executes with no issue: Please note that self.ax1 = fig1.add_subplot(111) is commented out.
from PyQt5 import QtWidgets, uic
import os
import sys
import numpy as np
from pathlib import Path
from os.path import join
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
dir = Path(os.getcwd())
parent_dir = os.path.normpath(dir.parent)
sys.path.append(parent_dir)
class Test(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Test, self).__init__(parent)
uic.loadUi(join(parent_dir, r'sub_windows\test.ui'), self)
fig1 = Figure()
# self.ax1 = fig1.add_subplot(111)
canvas1 = FigureCanvas(fig1)
self.plot_layout.addWidget(canvas1)
toolbar = NavigationToolbar(canvas1, self.widget, coordinates=True)
self.plot_layout.addWidget(toolbar)
self.pushButton.clicked.connect(self.action)
def action(self):
c = np.polyfit(np.arange(1600), np.arange(1600) + 1, deg=1)
print(c)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
win = Test()
win.show()
sys.exit(app.exec_())
Now, after uncommenting self.ax1 = fig1.add_subplot(111), this error occurs:
** On entry to DLASCLS parameter number 4 had an illegal value
** On entry to DLASCLS parameter number 4 had an illegal value
** On entry to DLASCLS parameter number 4 had an illegal value
** On entry to DLASCLS parameter number 4 had an illegal value
** On entry to DLASCLS parameter number 5 had an illegal value
** On entry to DLASCLS parameter number 4 had an illegal value
Traceback (most recent call last):
File "test.py", line 31, in action
c = np.polyfit(np.arange(1600), np.arange(1600) + 1, deg=1)
File "<__array_function__ internals>", line 5, in polyfit
File "C:\Python38\lib\site-packages\numpy\lib\polynomial.py", line 629, in polyfit
c, resids, rank, s = lstsq(lhs, rhs, rcond)
File "<__array_function__ internals>", line 5, in lstsq
File "C:\Python38\lib\site-packages\numpy\linalg\linalg.py", line 2306, in lstsq
x, resids, rank, s = gufunc(a, b, rcond, signature=signature, extobj=extobj)
File "C:\Python38\lib\site-packages\numpy\linalg\linalg.py", line 100, in _raise_linalgerror_lstsq
raise LinAlgError("SVD did not converge in Linear Least Squares")
numpy.linalg.LinAlgError: SVD did not converge in Linear Least Squares
I've tried this same exact code on two other computers running the same version of python, and it ran without issue. Any suggestions on how to diagnose this on my current system? I'm running Python 3.7.8 64-bit, Windows 10 Pro 64-bit, all packages are the latest versions, Intel Core i5-8400. Note: I've also tried reinstalling python and even installing python 3.8, but all had the same result.
I believe this is a bug in numpy. Your problem could be the np.polyfit that appears incompatible with certain matplotlib charts. Even replacing it with np.polynomial.polynomial.polyfit will not help.
In my case, I solved this problem by replacing this function with my own least-squares linear regression fit:
def linreg(x, y):
meanx = np.mean(x)
meany = np.mean(y)
sumxx2 = np.sum((x - meanx) ** 2)
sumxy = np.sum(((x - meanx) * (y - meany)))
m = sumxy / sumxx2
c = meany - m * meanx
return np.array([c, m]) # though, you might want to return as np.array([m, c]) as np.polyfit would
Also see: Python: creating plot causes future numpy.linalg.ein to diverge

How to implement QThread correctly with matplotlib and pyplot

I understand that there have been one or two other questions posted that are related but not exactly what I need. I'm building this gui that activates a module by clicking a button. This python module that gets activated by pushing the button generates heatmaps from multiple pandas dataframes and saves those images, which in turn is then saved into an xlsx using pandas ExcelWriter.
I've tried to implement QThread, as other stackoverflow examples tried to explain similar problems but I continue getting this error: "It is not safe to use pixmaps outside the GUI thread". I understand that technically I'm not creating the heatmap inside the MAIN gui thread but I thought with QThread that I am still inside "a" gui thread. These dataframes that the heatmaps are based off of can be of a large size at times and I am somewhat grasping the concept of sending a signal to the main gui thread when a heatmap is to be created and have the heatmap function inside the main gui class...but I fear that will be troublesome later in passing so much data around..this is more like pipelining than threading. I just want this working thread to create these images and save them and then take those saved files and save them into an xlsx without interrupting the main gui..
(NOTE: This is a simplified version, in the real program there will be several of these threads created almost simultaneously and inside each thread several heatmaps will be created)
---main.py---
import sys
from MAIN_GUI import *
from PyQt4 import QtGui, QtCore
from excel_dummy import *
if __name__=="__main__":
app = QtGui.QApplication(sys.argv)
class MAIN_GUI(QtGui.QMainWindow):
def __init__(self):
super(MAIN_GUI, self).__init__()
self.uiM = Ui_MainWindow()
self.uiM.setupUi(self)
self.connect(self.uiM.updateALL_Button,QtCore.SIGNAL('clicked()'),self.newThread)
def newThread(self):
Excelify = excelify()
Excelify.start()
self.connect(Excelify,QtCore.SIGNAL('donethread(QString)'),(self.done))
def done(self):
print('done')
main_gui = MAIN_GUI()
main_gui.show()
main_gui.raise_()
sys.exit(app.exec_())
---excel_dummy.py---
import os, pandas as pd
from pandas import ExcelWriter
import numpy as np
import seaborn.matrix as sm
from PyQt4 import QtCore
from PyQt4.QtCore import QThread
from matplotlib.backends.backend_agg import FigureCanvas
from matplotlib.figure import Figure
import time
class excelify(QThread):
def __init__(self):
QThread.__init__(self)
def run(self):
path = 'home/desktop/produced_files'
with ExcelWriter(path + '/final.xlsx', engine='xlsxwriter') as writer:
workbook = writer.book
worksheet = workbook.add_worksheet()
heatit = self.heatmap()
worksheet.insert_image('C3',path + '/' + 'heat.jpg')
worksheet.write(2, 2, 'just write something')
writer.save()
print('file size: %s "%s"' % (os.stat(path).st_size, path))
time.slee(0.3)
self.emit(QtCore.SIGNAL('donethread(QString)'),'')
def heatmap(self):
df = pd.DataFrame(np.array([[1,22222,33333],[2,44444,55555],[3,44444,22222],[4,55555,33333]]),columns=['hour','in','out'])
dfu = pd.DataFrame(df.groupby([df.in,df.hour]).size())
dfu.reset_index(inplace=True)
dfu.rename(columns={'0':'Count'})
dfu.columns=['in','hour','Count']
dfu_2 = dfu.copy()
mask=0
fig = Figure()
ax = fig.add_subplot(1,1,1)
canvas = FigureCanvas(fig)
df_heatmap = dfu_2.pivot('in','hour','Count').fillna(0)
sm.heatmap(df_heatmap,ax=ax,square=True,annot=False,mask=mask)
fig.savefig(path + '/' + heat.jpg')
---MAIN_GUI.py---
from PyQt4 import QtCore,QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.unicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName(_fromUtf8("MainWindow"))
MainWindow.resize(320,201)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
self.updateALL_Button = QtGui.QPushButton(self.centralwidget)
self.updateALL_Button.setGeometry(QtCore.QRect(40,110,161,27))
self.updateALL_Button.setFocusPolicy(QtCore.Qt.NoFocus)
self.updateALL_Button.setObjectName(_fromUtf8("Options_updateALL_Button"))
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtGui.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 320, 24))
self.menubar.setObjectName(_fromUtf8("menubar"))
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtGui.QStatusBar(MainWindow)
self.statusbar.setObjectName(_fromUtf8("statusbar"))
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self,MainWindow):
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))
self.updateALL_Button.setText(_translate("MainWindow", "updateALL", None))
Even though you are explicitely using the Agg backend to generate your figure, it looks like Seaborn is still using the default backend on your system, which is most likely Qt4Agg, an interactive backend. We want Seaborn to use a non-interactive backend instead to avoid any error (see matplotlib documentation for more details about backends). To do so, tell Matplotlib in your imports to use the Agg backend and import Seaborn after Matplotlib.
You will also need to save your figure as a png, since jpg is not supported by the Agg backend. Unless you have some specific reasons for using jpg, png is usually a better format for graphs.
Finally, you could use a memory buffer instead of saving your images to a temporary file before saving them in an Excel Workbook. I haven't tested it, but it will probably be faster if you are working with large files.
Below is a MWE I've written which includes the aformentioned points and which does not give any error on my system in Python3.4:
import pandas as pd
import time
from pandas import ExcelWriter
import numpy as np
from PyQt4 import QtCore, QtGui
import matplotlib as mpl
mpl.use('Agg')
from matplotlib.backends.backend_agg import FigureCanvas
import seaborn.matrix as sm
try: # Python 2 (not tested)
from cStringIO import StringIO as BytesIO
except ImportError: # Python 3
from io import BytesIO
class MAIN_GUI(QtGui.QWidget):
def __init__(self):
super(MAIN_GUI, self).__init__()
self.worker = Excelify()
btn = QtGui.QPushButton('Run')
disp = QtGui.QLabel()
self.setLayout(QtGui.QGridLayout())
self.layout().addWidget(btn, 0, 0)
self.layout().addWidget(disp, 2, 0)
self.layout().setRowStretch(1, 100)
btn.clicked.connect(self.worker.start)
self.worker.figSaved.connect(disp.setText)
class Excelify(QtCore.QThread):
figSaved = QtCore.pyqtSignal(str)
def run(self):
self.figSaved.emit('Saving figure to Workbook.')
t1 = time.clock()
image_data = self.heatmap()
with ExcelWriter('final.xlsx', engine='xlsxwriter') as writer:
wb = writer.book
ws = wb.add_worksheet()
ws.insert_image('C3', 'heat.png', {'image_data': image_data})
writer.save()
t2 = time.clock()
self.figSaved.emit('Done in %f sec.' % (t2-t1))
def heatmap(self):
df = pd.DataFrame(np.array([[1, 22222, 33333], [2, 44444, 55555],
[3, 44444, 22222], [4, 55555, 33333]]),
columns=['hour', 'in', 'out'])
dfu = pd.DataFrame(df.groupby([df.out, df.hour]).size())
dfu.reset_index(inplace=True)
dfu.rename(columns={'0': 'Count'})
dfu.columns = ['in', 'hour', 'Count']
fig = mpl.figure.Figure()
fig.set_canvas(FigureCanvas(fig))
ax = fig.add_subplot(111)
df_heatmap = dfu.pivot('in', 'hour', 'Count').fillna(0)
sm.heatmap(df_heatmap, ax=ax, square=True, annot=False, mask=0)
buf= BytesIO()
fig.savefig(buf, format='png')
return(buf)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = MAIN_GUI()
w.show()
w.setFixedSize(200, 100)
sys.exit(app.exec_())

Dynamically updating plot with watchdog

Whenever a new txt file is added to a directory, I would like to plot and show the data from the file. If another file appears, I want the plot to update and show the new data. Creating a plot outside the main caused thread errors, so I made a (not very good) fix using global variables.
The problem is that a white figure appears and the plots do not show. After stopping the program, the white figure disappears and the plot appears. The correct image is saved to file, but I would like the image to be shown in real time. If I comment out the plt.show(), no plots appear.
I tried the "Dynamically updating plot in matplotlib" answer (Dynamically updating plot in matplotlib) but found that because it never called show(), no window appeared. If I tried calling show(), it blocked updates.
Inserting plt.pause() did not work (Real time matplotlib plot is not working while still in a loop)
The example code did not work (How to update a plot in matplotlib?)
Here is my code:
import time
from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler
import matplotlib.pyplot as plt
import numpy as np
import config
class MyHandler(PatternMatchingEventHandler):
patterns=["*.txt", "*.TXT"]
def on_created(self, event):
self.process(event)
def process(self, event):
filename = event.src_path
if '_AIt' in filename:
config.isnew=True
config.fname=filename
if __name__ == '__main__':
observer = Observer()
observer.schedule(MyHandler(), path='.', recursive=True)
observer.start()
dat=[0,1]
fig = plt.figure()
ax = fig.add_subplot(111)
Ln, = ax.plot(dat)
ax.set_xlim([0,10])
ax.set_ylim([-1,1])
plt.ion()
plt.show()
try:
while True:
time.sleep(1)
if config.isnew:
config.isnew=False
dataarray = np.array(np.transpose(np.loadtxt(config.fname)))
dat = dataarray[15] #AI0
Ln.set_ydata(dat)
Ln.set_xdata(range(len(dat)))
plt.savefig(config.fname[:-4] + '.png', bbox_inches='tight')
plt.draw()
except KeyboardInterrupt:
observer.stop()
observer.join()
config.py (creates default values of the configuration setting)
isnew=False
fname=""
I am confused because the following example code works well (from pylab.ion() in python 2, matplotlib 1.1.1 and updating of the plot while the program runs)
import pylab
import time
import matplotlib.pyplot as plt
import numpy as np
dat=[0,1]
fig = plt.figure()
ax = fig.add_subplot(111)
Ln, = ax.plot(dat)
ax.set_xlim([0,20])
ax.set_ylim([0,40])
plt.ion()
plt.show()
for i in range (18):
dat=np.array(range(20))+i
Ln.set_ydata(dat)
Ln.set_xdata(range(len(dat)))
plt.pause(1)
print 'done with loop'
The following should do what you want:
import time
from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler
import matplotlib.pyplot as plt
plt.ion() # enter interactive mode
ax = fig.add_subplot(111)
Ln, = ax.plot(dat)
ax.set_xlim([0,10])
ax.set_ylim([-1,1])
plt.draw() # non-blocking drawing
plt.pause(.001) # This line is essential, without it the plot won't be shown
try:
while True:
time.sleep(1)
if config.isnew:
...
plt.draw()
plt.pause(.001)
except KeyboardInterrupt:
observer.stop()
observer.join()
The essential thin is to call plt.pause after the plt.draw call, else it won't be drawn, as the other python code block matplotlib. The value .001 is simply a try. I don't really know how it works under the hood, but this seems to work.

matplotlib in gtk window with i18n (gettext) support

I am trying to show a matplotlib plot with axes labeled using gettext's _("label") construct. Trying to create a minimal example, I came up with the following python code. It runs fine through the NULLTranslations() like this:
python mpl_i18n_test.py
But when I switch to japanese, I get nothing but small squares in the plot -- though on the command-line, the translations look fine:
LANG=ja_JP.utf8 python mpl_i18n_test.py
Here is the file mpl_i18n_test.py
Note that this requires the mona-sazanami font installed, and the various python modules: pygtk, numpy, matplotlib, gettext and polib
So my question: Is there some trick to getting matplotlib play nicely with gettext? Am I missing something obvious here? Thank you.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import gtk
import numpy as np
import matplotlib as mpl
from matplotlib.figure import Figure
from matplotlib.backends.backend_gtkagg import \
FigureCanvasGTKAgg as FigureCanvas
from matplotlib.backends.backend_gtkagg import \
NavigationToolbar2GTKAgg as NavigationToolbar
import locale
import gettext
import polib
mpl.rcParams['font.family'] = 'mona-sazanami'
def append(po, msg):
occurances = []
for i,l in enumerate(open(__file__,'r')):
if "_('"+msg[0]+"')" in l:
occurances += [(__file__,str(i+1))]
entry = polib.POEntry(msgid=msg[0],
msgstr=msg[1],
occurrences=occurances)
print msg
print occurances
po.append(entry)
def generate_ja_mo_file():
po = polib.POFile()
msgs = [
(u'hello', u'こんにちは'),
(u'good-bye', u'さようなら'),
]
for msg in msgs:
append(po, msg)
po.save('mpl_i18n_test.po')
po.save_as_mofile('mpl_i18n_test.mo')
return 'mpl_i18n_test.mo'
def initialize():
'''prepare i18n/l10n'''
locale.setlocale(locale.LC_ALL, '')
loc,enc = locale.getlocale()
lang,country = loc.split('_')
l = lang.lower()
if l == 'ja':
filename = generate_ja_mo_file()
trans = gettext.GNUTranslations(open(filename, 'rb'))
else:
trans = gettext.NullTranslations()
trans.install()
if __name__ == '__main__':
initialize() # provides _() method for translations
win = gtk.Window(gtk.WINDOW_TOPLEVEL)
win.connect("destroy", lambda x: gtk.main_quit())
win.connect("delete_event", lambda x,y: False)
win.set_default_size(400,300)
win.set_title("Test of unicode in plot")
fig = Figure()
fig.subplots_adjust(bottom=.14)
ax = fig.add_subplot(1,1,1)
xx = np.linspace(0,10,100)
yy = xx*xx + np.random.normal(0,1,100)
ax.plot(xx,yy)
print 'hello --> ', _('hello')
print 'good-bye --> ', _('good-bye')
ax.set_title(u'こんにちは')
ax.set_xlabel(_('hello'))
ax.set_ylabel(_('good-bye'))
can = FigureCanvas(fig)
tbar = NavigationToolbar(can,None)
vbox = gtk.VBox()
vbox.pack_start(can, True, True, 0)
vbox.pack_start(tbar, False, False, 0)
win.add(vbox)
win.show_all()
gtk.main()
A solution I found was to merely specify unicode when the translation is "installed." It was a one-line change:
trans.install(unicode=True)
I will add that this is only needed in python 2.7, but not needed in python 3. Looks like python 2.6 and earlier still have issues with this