Commit MetaInfo

Revisão335a66e944044144678806468c5457dfccdff255 (tree)
Hora2013-03-19 14:24:21
Autorbijoux
Commiterbijoux

Mensagem de Log

New matplotlib components

Mudança Sumário

Diff

diff -r 0108e683446f -r 335a66e94404 src/pylafiii/mplext.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pylafiii/mplext.py Tue Mar 19 14:24:21 2013 +0900
@@ -0,0 +1,163 @@
1+# coding: utf-8
2+
3+import Tkinter, gc
4+import matplotlib
5+from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
6+from matplotlib.figure import Figure
7+import ingredient as laf
8+from ingredient import event
9+from tkext import tkWidget
10+#matplotlib.use('Agg',warn=False)
11+#matplotlib.rcParams['figure.facecolor'] = 'white'
12+#matplotlib.rcParams['figure.edgecolor'] = 'white'
13+#matplotlib.rcParams['font.family'] = 'sans-serif'
14+#matplotlib.rcParams['font.size'] = 9.0
15+
16+def args(*args,**kw): return args,kw
17+
18+def parse_port_args(arg):
19+ if not isinstance(arg,tuple): return (arg,),{}
20+ if not len(arg) == 2: return arg,{}
21+ if not (type(arg[0]) == tuple and type(arg[1]) == dict): return arg,{}
22+ return args(*arg[0],**arg[1])
23+
24+class Frame(Tkinter.Frame):
25+ '''
26+ 軽量プロットウィジェット
27+ tkaggバックエンドでのメモリ解放に関する不具合を修正したプロッタ
28+ '''
29+ def __init__(self,master=None,figsize=(5,4),dpi=75,cnf={},**kw):
30+ Tkinter.Frame.__init__(self,master,cnf,**kw)
31+ self.figure = figure = Figure(figsize=figsize,dpi=dpi)
32+ # === make canvas object ===
33+ old = list(master.winfo_toplevel()._tclCommands) # tclコマンドを控える
34+ self.canvas = canvas = FigureCanvasTkAgg(figure,master=self) # ここでscroll_event_windowsとfilter_destroyがrootに追加される
35+ self._root_tclCommands = [] # destroy時に追加分だけ破棄するためのidを保持する
36+ for com in master.winfo_toplevel()._tclCommands:
37+ if old.count(com): continue
38+ self._root_tclCommands.append(com)
39+ # ===
40+ canvas.get_tk_widget().pack()
41+ def destroy_figure_canvas_axes(self):
42+ # rootにバインドされているイベントを削除する
43+ root = self.canvas._tkcanvas.winfo_toplevel()
44+ for name in self._root_tclCommands:
45+ root.deletecommand(name)
46+ self._root_tclCommands = []
47+ # figureオブジェクトを削除する
48+ self.figure.clear() # 生成されているaxesオブジェクトをクリアする
49+ self.canvas.figure = None # FigureCanvasTkAggからの参照を削除する
50+ self.figure.figurePatch = None # Rectangleオブジェクトを削除して逆参照をクリアする
51+ self.figure.patch = None # Rectangleオブジェクトを削除して逆参照をクリアする
52+ self.figure = None # MPLFrameからの参照をクリアする
53+ # canvasオブジェクトを削除する
54+ self.canvas._tkcanvas.destroy(); self.canvas._tkcanvas = None # canvasからtkcanvasを破棄する
55+ for cid in range(self.canvas.callbacks._cid): self.canvas.mpl_disconnect(cid+1) # コールバックを削除する
56+ matplotlib.backend_bases.LocationEvent.lastevent = None # <- これはまだ必要かどうかわからない。クリックしたら解放されなくなるバグの修正
57+ self.canvas = None # MPLFrameからの参照をクリアする
58+ gc.collect()
59+ def destroy(self):
60+ self.destroy_figure_canvas_axes()
61+ Tkinter.Frame.destroy(self)
62+
63+# class BasePlot(Frame):
64+# class Rectlinear(BasePlot): PROJECTION='rectlinear'
65+# class Polar(BasePlot): PROJECTION='polar'
66+class BasePlot(Frame):
67+ def __init__(self,master=None,figsize=(5,4),dpi=75,align=111,projection='rectilinear',cnf={},**kw):
68+ Frame.__init__(self,master,figsize,dpi,cnf,**kw)
69+ self.ax = self.figure.add_subplot(align,projection=projection) # 1つの矩形プロットを追加する
70+ def resize(self,figsize=(5,4),dpi=75,align=111,projection='rectilinear'):
71+ self.destroy_figure_canvas_axes()
72+ self.figure = figure = Figure(figsize=figsize,dpi=dpi)
73+ # === make canvas object ===
74+ master = self.master
75+ old = list(master.winfo_toplevel()._tclCommands) # tclコマンドを控える
76+ self.canvas = canvas = FigureCanvasTkAgg(figure,master=self) # ここでscroll_event_windowsとfilter_destroyがrootに追加される
77+ self._root_tclCommands = [] # destroy時に追加分だけ破棄するためのidを保持する
78+ for com in master.winfo_toplevel()._tclCommands:
79+ if old.count(com): continue
80+ self._root_tclCommands.append(com)
81+ # ===
82+ canvas.get_tk_widget().pack()
83+ self.ax = self.figure.add_subplot(align,projection=projection)
84+ def _clear_lines(self): # axes.clear
85+ while len(self.ax.lines): # 残っているすべてのラインについて
86+ line = self.ax.lines[-1] # 最終ラインへの参照
87+ self.ax.lines.remove(line) # ラインの削除
88+ line._lineFunc = None # Line2Dの循環参照を解放する
89+ #line = weakref.ref(line); print line # オブジェクトが削除されたか確認するデバッグコード
90+ # === 削除しないで! ===
91+ # ラインの解放にトラブルが発生したら以下のコードを試してみる
92+ # たとえば、スミスチャートの実装時などに問題がでるかも
93+ #for key,item in self.ax.xaxis.callbacks.callbacks['units'].items(): # コールバックを解除する
94+ # if item == line.recache:
95+ # del self.ax.xaxis.callbacks.callbacks['units'][key]
96+ #for key,item in self.ax.yaxis.callbacks.callbacks['units'].items(): # コールバックを解除する
97+ # if item == line.recache:
98+ # del self.ax.yaxis.callbacks.callbacks['units'][key]
99+ # ===================
100+ self.ax.ignore_existing_data_limits = True # dataLimをアップデートする
101+ self.ax._get_lines.set_color_cycle() # カラーサイクルを初期状態に戻す
102+ def _clear_patch(self):
103+ while len(self.ax.patches): # きちんとパッチが解放されることを確認した
104+ patch = self.ax.patches[-1]
105+ self.ax.patches.remove(patch)
106+ def clear(self):
107+ self._clear_lines()
108+ self._clear_patch()
109+# def savefig(self):
110+# fname = tkFileDialog.asksaveasfilename(title='Enter Filename')
111+# self.ax.figure.savefig(fname)
112+# def load(self):
113+# from numpy import genfromtxt
114+# fname = tkFileDialog.askopenfilename(title='Enter Filename')
115+# data = genfromtxt(fname,delimiter=',').transpose()
116+# self.sig_in.set(data)
117+ def destroy(self):
118+ self.ax = None
119+ Frame.destroy(self)
120+
121+# 3次元についてはVTKを利用するかも
122+class ThreeD(Frame):
123+ def __init__(self,master=None,figsize=(5,4),dpi=75,align=111,cnf={},**kw):
124+ Frame.__init__(self,master,figsize,dpi,cnf,**kw)
125+ self.ax = self.figure.add_subplot(align,projection='rectilinear') # 1つの矩形プロットを追加する
126+ def plot(self): pass
127+ def scatter(self): pass
128+ def wireframe(self): pass
129+ def surface(self): pass
130+ def destroy(self):
131+ self.ax = None
132+ Frame.destroy(self)
133+
134+class DiagramPlot(BasePlot,tkWidget):
135+ def __init__(self,master=None,figsize=(5,4),dpi=75,align=111,projection='rectilinear',cnf={},**kw):
136+ BasePlot.__init__(self,master,figsize,dpi,align,projection,cnf,**kw)
137+ self._buffer = None
138+ self._updated = False
139+ self._idle_loop()
140+ def _idle_loop(self):
141+ if self._updated:
142+ self.plot(self._buffer)
143+ self._updated = False
144+ self.after(50,self._idle_loop)
145+ @event
146+ def input(self):
147+ self._buffer = laf.port(self,'input').container.value
148+ self._updated = True
149+ def plot(self,input):
150+ args,kw = parse_port_args(input)
151+ self.clear()
152+ try:
153+ self.ax.plot(*args,**kw)
154+ except ValueError:
155+ return
156+ self.canvas.show()
157+ self.update_idletasks() # スレッド処理でButtonに対応するにはupdate_idletasksを使う。updateではButtonがロックすることがある。
158+ def winfo_reqheight(self):
159+ for w in self.children.itervalues(): pass
160+ return w.winfo_reqheight()
161+ def winfo_reqwidth(self):
162+ for w in self.children.itervalues(): pass
163+ return w.winfo_reqwidth()
Show on old repository browser