Revisão | 8d3cc81108428e682db91036632d23ad516561a1 (tree) |
---|---|
Hora | 2010-04-22 14:52:38 |
Autor | Hidehisa SHIOMI <pylaf@user...> |
Commiter | Hidehisa SHIOMI |
sourceforge公開用に整理したコード
@@ -0,0 +1,26 @@ | ||
1 | +Copyright (c) 2010, Hidehisa Shiomi <pylaf@users.sourceforge.jp> | |
2 | +All rights reserved. | |
3 | + | |
4 | +Redistribution and use in source and binary forms, with or without | |
5 | +modification, are permitted provided that the following conditions are met: | |
6 | + | |
7 | + o Redistributions of source code must retain the above copyright notice, | |
8 | + this list of conditions and the following disclaimer. | |
9 | + o Redistributions in binary form must reproduce the above copyright | |
10 | + notice, this list of conditions and the following disclaimer in the | |
11 | + documentation and/or other materials provided with the distribution. | |
12 | + o Neither the name of the <ORGANIZATION> nor the names of its | |
13 | + contributors may be used to endorse or promote products derived from | |
14 | + this software without specific prior written permission. | |
15 | + | |
16 | +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
17 | +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
18 | +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
19 | +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | |
20 | +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
21 | +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
22 | +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
23 | +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
24 | +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
25 | +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
26 | +POSSIBILITY OF SUCH DAMAGE. | |
\ No newline at end of file |
@@ -0,0 +1,13 @@ | ||
1 | +COPYING | |
2 | +README | |
3 | +setup.py | |
4 | +examples/transmitter.py | |
5 | +src/PyLAF/__init__.py | |
6 | +src/PyLAF/components.py | |
7 | +src/PyLAF/framework.py | |
8 | +src/PyLAF/plotters.py | |
9 | +src/PyLAF/widgets.py | |
10 | +src/PyLAF/Wireless/Filters.py | |
11 | +src/PyLAF/Wireless/Transmitter.py | |
12 | +src/PyLAF/Wireless/Wireless.py | |
13 | +src/PyLAF/Wireless/__init__.py |
@@ -0,0 +1,2 @@ | ||
1 | +include COPYING | |
2 | +recursive-include examples *.py |
@@ -0,0 +1,7 @@ | ||
1 | +Install: | |
2 | + | |
3 | + $ tar xvfz PyLAF-0.1.tar.gz | |
4 | + $ cd PyLAF-0.1 | |
5 | + $ python setup.py install | |
6 | + | |
7 | +See http://pylaf.sourceforge.jp/ for detailed information. | |
\ No newline at end of file |
@@ -0,0 +1,11 @@ | ||
1 | +from distutils.core import setup | |
2 | +setup( | |
3 | + name='PyLAF', | |
4 | + version='0.1.0a1', | |
5 | + package_dir={'PyLAF' : 'src/PyLAF'}, | |
6 | + packages=['PyLAF'], | |
7 | + description='Python Laboratory Application Framework', | |
8 | + author='Hidehisa Shiomi', | |
9 | + author_email='pylaf@users.sourceforge.jp', | |
10 | + url='http://pylaf.sourceforge.jp', | |
11 | + ) | |
\ No newline at end of file |
@@ -0,0 +1,7 @@ | ||
1 | +"""\ | |
2 | +PyLAF --- A laboratory application framework for Python | |
3 | +""" | |
4 | +from framework import * | |
5 | +from widgets import * | |
6 | +from plotters import * | |
7 | +from easy import * |
@@ -0,0 +1,92 @@ | ||
1 | +# coding: utf-8 | |
2 | + | |
3 | +from numpy import array | |
4 | +from framework import Component, Port, Variable, Frame, iscontain | |
5 | +from framework import DoubleVar, IntVar, StringVar, BooleanVar | |
6 | +from widgets import GridPane, Menu | |
7 | +from inspect import getmembers, stack, getargvalues | |
8 | +import Tkinter | |
9 | + | |
10 | +class EasyComponent(Component): | |
11 | + PLOTTER = None | |
12 | + def __init__(self,master): | |
13 | + Component.__init__(self,master) | |
14 | + self.colcount = 0 | |
15 | + self._menu = None | |
16 | + self.sig_out = Port(Variable(array([]))) | |
17 | + def gui(self,master,pltcnf={},cnf={},**kw): | |
18 | + frm = Frame(master) | |
19 | + if not self.PLOTTER == None: | |
20 | + v = self.__class__.PLOTTER(frm,pltcnf); v.plot(); v.show(); v.pack() | |
21 | + setattr(frm,'plotter',v) | |
22 | + self.sig_out.insertlink(0,v.sig_in); | |
23 | + o = EasyGrid(frm,self); o.pack() | |
24 | + return frm | |
25 | + def menu(self,master,cnf={},**kw): | |
26 | + if self._menu == None: | |
27 | + return None | |
28 | + else: | |
29 | + menu = EasyMenu(master,list=self._menu,cnf=cnf,**kw) | |
30 | + return menu | |
31 | + | |
32 | +class EasyPort(Port): | |
33 | + def __init__(self,variable,label=None,var=None,key=None): | |
34 | + Port.__init__(self,variable); | |
35 | + if label == None: | |
36 | + self.label = '' | |
37 | + else: | |
38 | + self.label = label | |
39 | + if var == None: | |
40 | + if type(variable.get()) == float: | |
41 | + self.tkvar = DoubleVar(self) | |
42 | + elif type(variable.get()) == int: | |
43 | + self.tkvar = IntVar(self) | |
44 | + elif type(variable.get()) == bool: | |
45 | + self.tkvar = BooleanVar(self) | |
46 | + else: | |
47 | + self.tkvar = StringVar(self) | |
48 | + else: | |
49 | + self.tkvar = var(self) | |
50 | + if key == None: | |
51 | + caller = self._caller() | |
52 | + caller.colcount += 1 | |
53 | + self.sortkey = caller.colcount | |
54 | + else: | |
55 | + self.sortkey = key | |
56 | + def _caller(self): | |
57 | + ''' | |
58 | + 下記ブログに掲載されていたコードを使わせてもらった | |
59 | + http://d.hatena.ne.jp/Kazumi007/20090914/1252915940 | |
60 | + ''' | |
61 | + try: | |
62 | + framerecords = stack() | |
63 | + framerecord = framerecords[2] | |
64 | + frame = framerecord[0] | |
65 | + arginfo = getargvalues(frame) | |
66 | + return arginfo.locals['self'] if 'self' in arginfo.locals else None | |
67 | + finally: | |
68 | + del frame | |
69 | + return None | |
70 | + | |
71 | +class EasyGrid(GridPane): | |
72 | + def __init__(self,master,component,cnf={},**kw): | |
73 | + GridPane.__init__(self,master,cnf,**kw) | |
74 | + ms = getmembers(component,lambda x:iscontain(x,EasyPort)) | |
75 | + ms.sort(lambda x,y:cmp(x[1].sortkey,y[1].sortkey)) | |
76 | + for m,o in ms: | |
77 | + self.entry(o.label,o.tkvar) | |
78 | + # self.append(o.label,Entry(self,textvariable=o.tkvar)) | |
79 | + | |
80 | +class EasyMenu(Menu): | |
81 | + def __init__(self,master=None,list=None,cnf={},**kw): | |
82 | + Menu.__init__(self,master,cnf,**kw) | |
83 | + for m in list: self.add_children(self,m) | |
84 | + def add_children(self,parent,children): | |
85 | + if type(children[1]) == list: | |
86 | + cascade = Tkinter.Menu(parent) | |
87 | + parent.add_cascade(label=children[0],menu=cascade) | |
88 | + for m in children[1:]: self.add_children(cascade,m) | |
89 | + else: | |
90 | + label,func = children | |
91 | + parent.add_command(label=label,command=func) | |
92 | + |
@@ -0,0 +1,323 @@ | ||
1 | +# coding: utf-8 | |
2 | + | |
3 | +import Tkinter, weakref, inspect | |
4 | + | |
5 | +MASTER, SLAVE = range(2) | |
6 | + | |
7 | +def iscontain(obj,cls): | |
8 | + try: | |
9 | + bases = inspect.getmro(obj.__class__) # オブジェクトのクラスツリーをタプルで返す | |
10 | + except AttributeError: | |
11 | + return False | |
12 | + if bases.__contains__(cls): | |
13 | + return True | |
14 | + else: | |
15 | + return False | |
16 | + | |
17 | +def popcnf(key,cnf): | |
18 | + item = None | |
19 | + if cnf.has_key(key): | |
20 | + item = cnf[key] | |
21 | + del cnf[key] | |
22 | + return item | |
23 | + | |
24 | +class CallableList(list): | |
25 | + ''' | |
26 | + 関数を保持するリスト | |
27 | + ''' | |
28 | + def call(self): | |
29 | + for func in self: | |
30 | + func() | |
31 | +# FuncList = CallableList | |
32 | + | |
33 | +class SingletonList(list): | |
34 | + ''' | |
35 | + 同じアイテムをただひとつしか登録しないリスト | |
36 | + ''' | |
37 | + def append(self,obj): | |
38 | + if not self.__contains__(obj): | |
39 | + list.append(self, obj) | |
40 | + | |
41 | +class WeakRefList(list): | |
42 | + ''' | |
43 | + オブジェクトの弱参照を保持するリスト | |
44 | + ''' | |
45 | + def contains(self,obj): | |
46 | + return self.tolist().__contains__(obj) | |
47 | + def append(self,obj): | |
48 | + if not self.contains(obj): | |
49 | + list.append(self, weakref.ref(obj)) | |
50 | + def insert(self,index,obj): | |
51 | + if not self.contains(obj): | |
52 | + list.insert(self, index, weakref.ref(obj)) | |
53 | + def remove(self,obj): | |
54 | + if self.contains(obj): | |
55 | + for ref in self: | |
56 | + if obj is ref(): | |
57 | + list.remove(self, ref) | |
58 | + break | |
59 | + def tolist(self): | |
60 | + return [ref() for ref in self] | |
61 | + def cleanup(self): | |
62 | + ''' | |
63 | + dead参照のアイテムを削除する | |
64 | + ''' | |
65 | + items = [] | |
66 | + for ref in self: | |
67 | + if ref() == None: | |
68 | + items.append(ref) | |
69 | + for item in items: | |
70 | + list.remove(self, item) | |
71 | + | |
72 | +class CallableWeakRefList(WeakRefList): | |
73 | + def call(self): | |
74 | + for o in self: o()() | |
75 | + | |
76 | +class Listner: | |
77 | + def __init__(self,subject): | |
78 | + self.subject = None | |
79 | + self.register(subject) | |
80 | + def register(self,subject,index=None): | |
81 | + self.unregister() | |
82 | + subject.register(self,index) | |
83 | + self.subject = subject | |
84 | + def unregister(self): | |
85 | + if not self.subject == None: | |
86 | + self.subject.unregister(self) | |
87 | + self.subject = None | |
88 | + def update(self,event): pass | |
89 | + | |
90 | +class Subject: | |
91 | + def __init__(self): | |
92 | + self.listners = WeakRefList() | |
93 | + def register(self,listner,index=None): | |
94 | + if index == None: | |
95 | + self.listners.append(listner) | |
96 | + else: | |
97 | + self.listners.insert(index,listner) | |
98 | + def unregister(self,listner): | |
99 | + self.listners.cleanup() | |
100 | + self.listners.remove(listner) | |
101 | + def notify(self,*args): | |
102 | + self.listners.cleanup() | |
103 | + for listner in self.listners.tolist(): listner.update(*args) | |
104 | + | |
105 | +class Port(Listner): | |
106 | + def __init__(self,variable): | |
107 | + Listner.__init__(self,variable) | |
108 | + self._callbacks = CallableList() | |
109 | + def update(self,*args): | |
110 | + self._callbacks.call() | |
111 | + def bind(self,func): | |
112 | + self._callbacks.append(func) | |
113 | + return self | |
114 | + def link(self,*listners): | |
115 | + for listner in listners: | |
116 | + for obj in listner.subject.listners.tolist(): | |
117 | + obj.register(self.subject) | |
118 | + return self | |
119 | + def insertlink(self,index,listner): | |
120 | + for obj in listner.subject.listners.tolist(): | |
121 | + obj.register(self.subject,index) | |
122 | + return self | |
123 | + def set(self,value,mode=MASTER): | |
124 | + self.subject.set(value,mode) | |
125 | + def get(self): | |
126 | + return self.subject.get() | |
127 | + | |
128 | +class Variable(Subject): | |
129 | + ''' | |
130 | + 複数のPortクラスで同期する任意のリテラルを格納する実体クラス | |
131 | + ''' | |
132 | + def __init__(self,value,**kw): | |
133 | + Subject.__init__(self) | |
134 | + self.value = None | |
135 | + self.set(value,mode=SLAVE) | |
136 | + # ========================== | |
137 | + # for backward compatibility | |
138 | + if kw.has_key('value'): | |
139 | + self.set(kw['value'],mode=SLAVE) | |
140 | + del kw['value'] | |
141 | + # ========================== | |
142 | + def set(self,value,mode=MASTER): | |
143 | + ''' | |
144 | + もしvalueがcopyメソッドを持っていればvalue.copy()する | |
145 | + ''' | |
146 | + try: | |
147 | + self.value = value.copy() | |
148 | + except AttributeError: | |
149 | + self.value = value | |
150 | + if mode == MASTER: | |
151 | + self.notify() | |
152 | + else: | |
153 | + self.notify_tk_only() | |
154 | + def notify_tk_only(self,*args): | |
155 | + self.listners.cleanup() | |
156 | + for listner in self.listners.tolist(): | |
157 | + if iscontain(listner,VariableTk): listner.update(*args) | |
158 | + def get(self): | |
159 | + '''格納しているオブジェクトの参照を返す''' | |
160 | + return self.value | |
161 | + | |
162 | +class PortHolder: | |
163 | + ''' | |
164 | + Portインスタンスをメンバとして保持する場合に発生する循環参照を自動的に解消する機構 | |
165 | + Portインスタンスをメンバとして保持するクラスはPortHolderを継承してdestroyメソッドでパージしなければならない | |
166 | + ''' | |
167 | + def _inherit_ports(self,portholder,*ports,**kw): | |
168 | + ''' | |
169 | + _inherit_ports(obj) | |
170 | + _inherit_ports(obj,omit=[a,b,c]) | |
171 | + _inherit_ports(obj,a,b,c) | |
172 | + ''' | |
173 | + m = inspect.getmembers(portholder,lambda x:iscontain(x,Port)) | |
174 | + # | |
175 | + omit = [] | |
176 | + if kw.has_key('omit'): omit = kw['omit'] | |
177 | + # | |
178 | + # print map(lambda x:x[0],m) # リストの第1引数だけをリストアップする | |
179 | + ports = list(ports) | |
180 | + if not ports == []: | |
181 | + m = filter(lambda x:not ports.count(x[0]) == 0,m) | |
182 | + else: | |
183 | + m = filter(lambda x:omit.count(x[0]) == 0,m) # リストの第1引数がomitに含まれないものだけ選択する | |
184 | + for name,info in m: setattr(self, name, getattr(portholder, name)) | |
185 | + def destroy(self): | |
186 | + '''メンバからPortインスタンスへの参照を削除する''' | |
187 | + m = inspect.getmembers(self,lambda x:iscontain(x,Port)) | |
188 | + for name,info in m: setattr(self, name, None) | |
189 | + | |
190 | +class ComponentHolder: | |
191 | + ''' | |
192 | + Component,FrameComponentインスタンスをメンバとして保持する場合に発生する循環参照を自動的に解消する機構 | |
193 | + ''' | |
194 | + def _remove(self,m): | |
195 | + for name,info in m: | |
196 | + if not name == 'master': | |
197 | + setattr(self, name, None) | |
198 | + def destroy(self): | |
199 | + '''self.master以外のメンバからComponent,FrameCopmponentインスタンスへの参照を削除する''' | |
200 | + self._remove(inspect.getmembers(self,lambda x:iscontain(x,LeafTk))) | |
201 | + self._remove(inspect.getmembers(self,lambda x:iscontain(x,Tkinter.BaseWidget))) | |
202 | + | |
203 | +class LeafTk: | |
204 | + ''' | |
205 | + Tkinter.BaseWidgetのリーフになれるコンポーネント。Tkinter.pyを参考にした。 | |
206 | + ''' | |
207 | + def __init__(self,master): | |
208 | + self.master = master | |
209 | + self._name = repr(id(self)) | |
210 | + self._assign_to_master() | |
211 | + def _assign_to_master(self): | |
212 | + if self.master.children.has_key(self._name): | |
213 | + '''master.childrenにおけるidの唯一性を保証する''' | |
214 | + self.master.children[self._name].destroy() | |
215 | + self.master.children[self._name] = self | |
216 | + def _remove_from_master(self): | |
217 | + '''master.childrenから自身へのオブジェクト参照を廃棄する''' | |
218 | + if self.master.children.has_key(self._name): | |
219 | + del self.master.children[self._name] | |
220 | + def destroy(self): | |
221 | + self._remove_from_master() | |
222 | + | |
223 | +class Component(LeafTk,PortHolder,ComponentHolder): | |
224 | + def __init__(self,master): | |
225 | + LeafTk.__init__(self,master) | |
226 | + self.children = {} | |
227 | + def destroy(self): | |
228 | + for c in self.children.values(): c.destroy() # purge children | |
229 | + ComponentHolder.destroy(self) # purge component members | |
230 | + PortHolder.destroy(self) # purge port members | |
231 | + LeafTk.destroy(self) # purge itself | |
232 | + def gui(self,master,cnf={},**kw): | |
233 | + frm = Tkinter.Frame(master,cnf,**kw) | |
234 | + Tkinter.Label(frm,text=self.__class__.__name__).pack(side=Tkinter.TOP,expand=1,fill=Tkinter.X) | |
235 | + return frm | |
236 | + def menu(self,master,cnf={},**kw): | |
237 | + return None | |
238 | + | |
239 | +class Frame(Tkinter.Frame,PortHolder,ComponentHolder): | |
240 | + def destroy(self): | |
241 | + ComponentHolder.destroy(self) | |
242 | + PortHolder.destroy(self) | |
243 | + Tkinter.Frame.destroy(self) | |
244 | + | |
245 | +class LabelFrame(Tkinter.LabelFrame,PortHolder,ComponentHolder): | |
246 | + def destroy(self): | |
247 | + ComponentHolder.destroy(self) | |
248 | + PortHolder.destroy(self) | |
249 | + Tkinter.LabelFrame.destroy(self) | |
250 | + | |
251 | +class Equipment: | |
252 | + def __init__(self,comp,gui=True,guicnf={}): | |
253 | + if inspect.isclass(comp): | |
254 | + self.component = comp(self) | |
255 | + else: | |
256 | + self.component = comp | |
257 | + self._inherit_ports(self.component) | |
258 | + if gui: | |
259 | + self.gui = self.component.gui(self,**guicnf) | |
260 | + self.gui.pack() | |
261 | + def configure(self,gui=None): | |
262 | + if not gui == None: | |
263 | + try: | |
264 | + self.gui.destroy() | |
265 | + except AttributeError: pass | |
266 | + gui(self).pack() | |
267 | + | |
268 | +class App(Frame,Equipment): | |
269 | + def __init__(self,master,comp,gui=True,menu=True,guicnf={},cnf={},**kw): | |
270 | + Frame.__init__(self,master,cnf,**kw) | |
271 | + Equipment.__init__(self,comp,gui,guicnf) | |
272 | + if menu: | |
273 | + m = self.component.menu(self.master) | |
274 | + if not m == None: self.master.config(menu=m) | |
275 | + def configure(self,gui=None,menu=None,cnf={},**kw): | |
276 | + Frame.configure(self,cnf,**kw) | |
277 | + Equipment.configure(self,gui) | |
278 | + if not menu == None: self.master.config(menu=menu(self.master)) | |
279 | + | |
280 | +class Embed(LabelFrame,Equipment): | |
281 | + def __init__(self,master,comp,gui=True,menu=True,guicnf={},cnf={},**kw): | |
282 | + LabelFrame.__init__(self,master,cnf,**kw) | |
283 | + Equipment.__init__(self,comp,gui,guicnf) | |
284 | + if menu: self.component.menu(self,popup=True) | |
285 | + def configure(self,gui=None,menu=None,cnf={},**kw): | |
286 | + Frame.configure(self,cnf,**kw) | |
287 | + Equipment.configure(self,gui) | |
288 | + if not menu == None: self.master.config(menu=menu(self,popup=True)) | |
289 | + | |
290 | +class VariableTk(Listner): | |
291 | + ''' | |
292 | + Variableクラスを参照して同期するよう[Double|Int|String|Boolean]Varを拡張するMixin | |
293 | + ''' | |
294 | + def __init__(self,port,forward=lambda x:x,backward=lambda x:x): | |
295 | + Listner.__init__(self,port.subject) | |
296 | + self.forward = forward | |
297 | + self.backward = backward | |
298 | + def update(self,*args): | |
299 | + self.set(self.forward(self.subject.get())) | |
300 | + def fixed(self,*args): | |
301 | + self.subject.set(self.backward(self.get())) | |
302 | + def __eq__(self,other): | |
303 | + return id(self) == id(other) | |
304 | + | |
305 | +class DoubleVar(Tkinter.DoubleVar,VariableTk): | |
306 | + def __init__(self,port,master=None,value=None,name=None,forward=lambda x:x,backward=lambda x:x): | |
307 | + Tkinter.DoubleVar.__init__(self,master,forward(port.subject.get()),name) | |
308 | + VariableTk.__init__(self,port,forward,backward) | |
309 | + | |
310 | +class IntVar(Tkinter.IntVar,VariableTk): | |
311 | + def __init__(self,port,master=None,value=None,name=None,forward=lambda x:x,backward=lambda x:x): | |
312 | + Tkinter.IntVar.__init__(self,master,forward(port.subject.get()),name) | |
313 | + VariableTk.__init__(self,port,forward,backward) | |
314 | + | |
315 | +class StringVar(Tkinter.StringVar,VariableTk): | |
316 | + def __init__(self,port,master=None,value=None,name=None,forward=lambda x:x,backward=lambda x:x): | |
317 | + Tkinter.StringVar.__init__(self,master,forward(port.subject.get()),name) | |
318 | + VariableTk.__init__(self,port,forward,backward) | |
319 | + | |
320 | +class BooleanVar(Tkinter.BooleanVar,VariableTk): | |
321 | + def __init__(self,port,master=None,value=None,name=None,forward=lambda x:x,backward=lambda x:x): | |
322 | + Tkinter.BooleanVar.__init__(self,master,forward(port.subject.get()),name) | |
323 | + VariableTk.__init__(self,port,forward,backward) |
@@ -0,0 +1,271 @@ | ||
1 | +# coding: utf-8 | |
2 | + | |
3 | +import StringIO, Image, ImageTk, matplotlib, tkFileDialog | |
4 | +matplotlib.use('Agg') | |
5 | +from matplotlib import pyplot | |
6 | +from scipy import array, arange, real, imag, sin, pi, fft, log10, fftpack | |
7 | +from Tkinter import Label, BOTTOM, Toplevel, mainloop, _cnfmerge | |
8 | +from framework import PortHolder, Port, Variable, SLAVE | |
9 | +from framework import DoubleVar, IntVar, BooleanVar, StringVar | |
10 | +from widgets import popcnf, Menu, GridPane | |
11 | + | |
12 | +class SimplePlot(Label): | |
13 | + ''' | |
14 | + 軽量プロットウィジェット | |
15 | + CanvasTkAgg を使った埋め込みはでは使用後に解放されずメモリに残ってしまうので、 | |
16 | + 以下のような軽量プロットを作ってみた。 | |
17 | + matplotlib のバックエンドで画像を作成してPILで表示する。 | |
18 | + グラフの描画だけなら、むしろこちらの方が管理楽ちん。 | |
19 | + インタラクティブにしたい場合は、メインウィンドウと同じライフサイクルでひとつだけCanvasTkAggを使い | |
20 | + 共用するとよいだろう。 | |
21 | + ''' | |
22 | + def __init__(self,master=None,cnf={},**kw): | |
23 | + if kw: cnf = _cnfmerge((cnf,kw)) | |
24 | + figsize, dpi = popcnf('figsize',cnf), popcnf('dpi',cnf) | |
25 | + if figsize == None: figsize = (300,300) | |
26 | + if dpi == None: dpi = 50 | |
27 | + Label.__init__(self,master,cnf,**kw) | |
28 | + self.dpi = dpi # dpi は 100 で固定のようだ。なぜか dpi=xxx や set_dpi(xxx) などが反映されない。 | |
29 | + size = (float(figsize[0]) / dpi, float(figsize[1]) / dpi) | |
30 | + self.ax = pyplot.figure(figsize=size).add_subplot(111) | |
31 | + def show(self): | |
32 | + imgdata = StringIO.StringIO() | |
33 | + self.ax.figure.savefig(imgdata, format='png') | |
34 | + imgdata.seek(0) | |
35 | + im = Image.open(imgdata) | |
36 | + width, height = self.ax.figure.get_size_inches() | |
37 | + width, height = int(width * self.dpi), int(height * self.dpi) | |
38 | + im = im.resize((width,height),Image.ANTIALIAS) # アンチエイリアス付きで拡大縮小する | |
39 | + self.img = ImageTk.PhotoImage(im) # インスタンスで保持しないと show() を抜けた瞬間に画像が解放されてしまう | |
40 | + self.configure(image=self.img) | |
41 | + def resize(self,figsize=(300,300),dpi=50): | |
42 | + self.dpi = dpi | |
43 | + size = (float(figsize[0]) / dpi, float(figsize[1]) / dpi) | |
44 | + self.ax = pyplot.figure(figsize=size).add_subplot(111) | |
45 | + self.show() | |
46 | + def destroy(self): | |
47 | + Label.destroy(self) | |
48 | + pyplot.close(self.ax.figure) | |
49 | + self.ax = None # AxesSubplot クラスの明示的解放(これしないとゾンビが残る) | |
50 | + | |
51 | +class BasePlot(SimplePlot,PortHolder): | |
52 | + ''' | |
53 | + プロットの基本機能を提供する。オートスケール、書き出し、デコレーションなど。 | |
54 | + ''' | |
55 | + def destroy(self): | |
56 | + PortHolder.destroy(self) | |
57 | + SimplePlot.destroy(self) | |
58 | + def __init__(self,master=None,cnf={},**kw): | |
59 | + SimplePlot.__init__(self,master,cnf,**kw) | |
60 | + self.sig_in = Port(Variable(value=array([]))).bind(self._sig_in) | |
61 | + self.kwargs = Port(Variable(value={})).bind(self._sig_in) | |
62 | + self.left = Port(Variable(value=0.15)).bind(self._decolation_autoshow) | |
63 | + self.right = Port(Variable(value=0.9)).bind(self._decolation_autoshow) | |
64 | + self.bottom = Port(Variable(value=0.1)).bind(self._decolation_autoshow) | |
65 | + self.title = Port(Variable(value='')).bind(self._decolation_autoshow) | |
66 | + self.xlabel = Port(Variable(value='xlabel')).bind(self._decolation_autoshow) | |
67 | + self.ylabel = Port(Variable(value='ylabel')).bind(self._decolation_autoshow) | |
68 | + xlim = self.ax.get_xlim() | |
69 | + self.xmin = Port(Variable(value=xlim[0])).bind(self._xlim_fixed_autoshow) | |
70 | + self.xmax = Port(Variable(value=xlim[1])).bind(self._xlim_fixed_autoshow) | |
71 | + ylim = self.ax.get_ylim() | |
72 | + self.ymin = Port(Variable(value=ylim[0])).bind(self._ylim_fixed_autoshow) | |
73 | + self.ymax = Port(Variable(value=ylim[1])).bind(self._ylim_fixed_autoshow) | |
74 | + self.autoscalex = Port(Variable(value=True)).bind(self._sig_in) | |
75 | + self.autoscaley = Port(Variable(value=True)).bind(self._sig_in) | |
76 | + self.menu(self,popup=True,tearoff=0) | |
77 | + def configure(self,figsize=None,dpi=None,cnf={},**kw): | |
78 | + if not figsize == None and dpi == None: | |
79 | + self.resize(figsize) | |
80 | + elif not dpi == None and figsize == None: | |
81 | + self.resize(dpi) | |
82 | + elif not figsize == None and not dpi == None: | |
83 | + self.resize(figsize,dpi) | |
84 | + SimplePlot.configure(self,cnf,**kw) | |
85 | + def menu(self,master,cnf={},**kw): | |
86 | + menu = Menu(master,cnf,**kw) | |
87 | + menu.add_command(label="Save Figure",command=self._savefig) | |
88 | + menu.add_command(label="Popup Console",command=self._popup_console) | |
89 | + menu.add_command(label="Popup Decolation",command=self._popup_decoration) | |
90 | + return menu | |
91 | + def gui(self,master,cnf={},**kw): | |
92 | + self.plot(); self.show() | |
93 | + o = self.console(master); o.pack(side=BOTTOM) | |
94 | + return self | |
95 | + def _sig_in(self): | |
96 | + self.plot(self.sig_in.get().transpose()); self.show() | |
97 | + def _popup_decoration(self): | |
98 | + self.decoration(Toplevel(self)).pack() | |
99 | + def _popup_console(self): | |
100 | + self.console(Toplevel(self)).pack() | |
101 | + def _savefig(self): | |
102 | + fname = tkFileDialog.asksaveasfilename(title='Enter Filename') | |
103 | + self.ax.figure.savefig(fname) | |
104 | + def _xlim_fixed_autoshow(self): | |
105 | + self.autoscalex.set(False,mode=SLAVE) | |
106 | + self.ax.set_xlim(self.xmin.get(), self.xmax.get()) | |
107 | + self.show() | |
108 | + def _ylim_fixed_autoshow(self): | |
109 | + self.autoscaley.set(False,mode=SLAVE) | |
110 | + self.ax.set_ylim(self.ymin.get(), self.ymax.get()) | |
111 | + self.show() | |
112 | + def plot(self,*args,**keys): | |
113 | + self.ax.cla() | |
114 | + self._decolation() | |
115 | + kwargs = self.kwargs.get() | |
116 | + kwargs['scalex'] = self.autoscalex.get() | |
117 | + kwargs['scaley'] = self.autoscaley.get() | |
118 | + self.ax.plot(*args,**kwargs) | |
119 | + self._update_scale() | |
120 | + def replot(self): | |
121 | + self._sig_in() | |
122 | + def _decolation(self): | |
123 | + self.ax.figure.subplots_adjust(left=self.left.get()) | |
124 | + self.ax.figure.subplots_adjust(right=self.right.get()) | |
125 | + self.ax.figure.subplots_adjust(bottom=self.bottom.get()) | |
126 | + self.ax.set_title(self.title.get()) | |
127 | + self.ax.set_xlabel(self.xlabel.get()) | |
128 | + self.ax.set_ylabel(self.ylabel.get()) | |
129 | + def _decolation_autoshow(self): | |
130 | + self._decolation() | |
131 | + self.show() | |
132 | + def _update_scale(self): | |
133 | + xlim = self.ax.get_xlim() | |
134 | + self.xmin.set(xlim[0],mode=SLAVE) | |
135 | + self.xmax.set(xlim[1],mode=SLAVE) | |
136 | + ylim = self.ax.get_ylim() | |
137 | + self.ymin.set(ylim[0],mode=SLAVE) | |
138 | + self.ymax.set(ylim[1],mode=SLAVE) | |
139 | + def console(self,master): | |
140 | + o = GridPane(master) | |
141 | + o.entry_with_tcb('XLIM',BooleanVar(self.autoscalex),DoubleVar(self.xmin),DoubleVar(self.xmax)) | |
142 | + o.entry_with_tcb('YLIM',BooleanVar(self.autoscaley),DoubleVar(self.ymin),DoubleVar(self.ymax)) | |
143 | + return o | |
144 | + def decoration(self,master): | |
145 | + o = GridPane(master) | |
146 | + o.entry('PADL',DoubleVar(self.left)) | |
147 | + o.entry('PADR',DoubleVar(self.right)) | |
148 | + o.entry('PADB',DoubleVar(self.bottom)) | |
149 | + o.entry('TITLE',StringVar(self.title)) | |
150 | + o.entry('XLABEL',StringVar(self.xlabel)) | |
151 | + o.entry('YLABEL',StringVar(self.ylabel)) | |
152 | + return o | |
153 | + | |
154 | +class Constellation(BasePlot): | |
155 | + ''' | |
156 | + デシメーションなどの機能を多重継承して追加できるといいけどね。 | |
157 | + ''' | |
158 | + def __init__(self,master=None,cnf={},**kw): | |
159 | + BasePlot.__init__(self,master,cnf,**kw) | |
160 | + self.samplerate = Port(Variable(value=1.)).bind(self._sig_in) | |
161 | + self.style = Port(Variable(value='.')).bind(self._sig_in) | |
162 | + self.alpha = Port(Variable(value=1)).bind(self._sig_in) | |
163 | + self.begin = Port(Variable(value=0.)).bind(self._sig_in) # プロット開始(%) | |
164 | + self.end = Port(Variable(value=100.)).bind(self._sig_in) # プロット終了(%) | |
165 | + self.decimation = Port(Variable(value=1)).bind(self._sig_in) | |
166 | + self.xlabel.set('I Channel [mV]',mode=SLAVE) | |
167 | + self.ylabel.set('Q Channel [mV]',mode=SLAVE) | |
168 | + def _sig_in(self): | |
169 | + sig = self.sig_in.get() | |
170 | + b,e = self.begin.get(), self.end.get() | |
171 | + sig = sig[...,::self.decimation.get()] | |
172 | + sig = sig[...,(b / 100 * sig.shape[-1]):(e / 100 * sig.shape[-1])] | |
173 | + sig = sig.transpose() | |
174 | + kwargs = self.kwargs.get() | |
175 | + kwargs['alpha'] = self.alpha.get() | |
176 | + self.kwargs.set(kwargs,mode=SLAVE) | |
177 | + self.plot(real(sig), imag(sig), self.style.get()) | |
178 | + self.show() | |
179 | + self.master.update() | |
180 | + def decoration(self,master): | |
181 | + o = BasePlot.decoration(self,master) | |
182 | + o.entry('Plot Style',StringVar(self.style)) | |
183 | + return o | |
184 | + def console(self,master): | |
185 | + o = BasePlot.console(self,master) | |
186 | + o.entry('Sample Rate [MS/sec]',DoubleVar(self.samplerate)) | |
187 | + o.entry('Alpha Index',DoubleVar(self.alpha)) | |
188 | + o.entry('Decimation Index',IntVar(self.decimation)) | |
189 | + return o | |
190 | + | |
191 | +class XYPlot(BasePlot): | |
192 | + def __init__(self,master=None,cnf={},**kw): | |
193 | + BasePlot.__init__(self,master,cnf,**kw) | |
194 | + self.xaxis = Port(Variable(value=array([]))).bind(self._sig_in) | |
195 | + self.yaxis = Port(Variable(value=array([]))).bind(self._sig_in) | |
196 | + self.style = Port(Variable(value='-')).bind(self._sig_in) | |
197 | + self.xlabel.set('X Axis',mode=SLAVE) | |
198 | + self.ylabel.set('Y Axis',mode=SLAVE) | |
199 | + def _sig_in(self): | |
200 | + xaxis, yaxis = self.xaxis.get(), self.yaxis.get() | |
201 | + if xaxis.shape[-1] == yaxis.shape[-1]: | |
202 | + self.plot(xaxis, yaxis.transpose(), self.style.get()) | |
203 | + self.show() | |
204 | + | |
205 | +class Oscilloscope(BasePlot): | |
206 | + def __init__(self,master=None,cnf={},**kw): | |
207 | + BasePlot.__init__(self,master,cnf,**kw) | |
208 | + self.samplerate = Port(Variable(value=1.)).bind(self._sig_in) | |
209 | + self.decimation = Port(Variable(value=1)).bind(self._sig_in) | |
210 | + self.style = Port(Variable(value='-')).bind(self._sig_in) | |
211 | + self.xlabel.set('Time [us]',mode=SLAVE) | |
212 | + self.ylabel.set('Signal [mV]',mode=SLAVE) | |
213 | + def _sig_in(self): | |
214 | + sig = self.sig_in.get() | |
215 | + sig = sig[...,::self.decimation.get()] | |
216 | + samplerate = self.samplerate.get() / self.decimation.get() | |
217 | + time = 1. / samplerate * arange(sig.shape[-1]) | |
218 | + self.plot(time, sig.transpose(), self.style.get()) | |
219 | + self.show() | |
220 | + def console(self,master): | |
221 | + o = BasePlot.console(self,master) | |
222 | + o.entry('Sample Rate [MS/sec]',DoubleVar(self.samplerate)) | |
223 | + o.entry('Decimation Index',IntVar(self.decimation)) | |
224 | + return o | |
225 | + | |
226 | +class Spectrum(BasePlot): | |
227 | + def __init__(self,master=None,cnf={},**kw): | |
228 | + BasePlot.__init__(self,master,cnf,**kw) | |
229 | + self.samplerate = Port(Variable(value=1.)).bind(self._sig_in) | |
230 | + self.style = Port(Variable(value='-')).bind(self._sig_in) | |
231 | + self.decimation = Port(Variable(value=1)).bind(self._sig_in) | |
232 | + self.xlabel.set('Frequency [MHz]',mode=SLAVE) | |
233 | + self.ylabel.set('Power [dBm]',mode=SLAVE) | |
234 | + def _sig_in(self): | |
235 | + sig = self.sig_in.get() | |
236 | + samplerate = self.samplerate.get() / self.decimation.get() | |
237 | + style = self.style.get() | |
238 | + sig = sig[...,::self.decimation.get()] | |
239 | + # sig = sig[:2**int(log2(sig.size))] | |
240 | + f = fft(sig) | |
241 | + # f[0] = f[0] * .5 # 片側スペクトルを見たい場合にはこの行をアクティベート | |
242 | + v = fftpack.fftshift(f * 2.0 / sig.size) | |
243 | + freq = fftpack.fftshift(fftpack.fftfreq(sig.size,1./samplerate)) | |
244 | + value = 10 * log10(real(v*v.conj())/2/50) - 30 | |
245 | + self.plot(freq, value, style) | |
246 | + self.show() | |
247 | + self.master.update() | |
248 | + def console(self,master): | |
249 | + o = BasePlot.console(self,master) | |
250 | + o.entry('Sample Rate [MS/sec]',DoubleVar(self.samplerate)) | |
251 | + o.entry('Decimation Index',IntVar(self.decimation)) | |
252 | + return o | |
253 | + | |
254 | +def test(master=None): | |
255 | + from framework import App | |
256 | + o = App(master,Spectrum); o.pack() | |
257 | + osc = App(Toplevel(master),Oscilloscope); osc.pack() | |
258 | + o.sig_in.link(osc.sig_in) | |
259 | + o.samplerate.link(osc.samplerate) | |
260 | + o.samplerate.set(250.,mode=SLAVE) | |
261 | + osc.xmin.set(0) | |
262 | + osc.xmax.set(1000) | |
263 | + d = 1. / o.samplerate.get() | |
264 | + t = arange(1e+6) * d # [us] | |
265 | + y = 1000. * sin(2 * pi * 9.12 * t) # 10Vpp -> 30dBm 1Vpp -> 10dBm | |
266 | + y = y + 787.3 * (sin(2 * pi * 10 * t) > 0.) # [mV] | |
267 | + o.sig_in.set(y) | |
268 | + | |
269 | +if __name__ == '__main__': | |
270 | + test() | |
271 | + mainloop() |
@@ -0,0 +1,138 @@ | ||
1 | +# coding: utf-8 | |
2 | + | |
3 | +import Tkinter | |
4 | + | |
5 | +def popcnf(key,cnf): | |
6 | + item = None | |
7 | + if cnf.has_key(key): | |
8 | + item = cnf[key] | |
9 | + del cnf[key] | |
10 | + return item | |
11 | + | |
12 | +def root(title='tk'): | |
13 | + root = Tkinter.Tk() | |
14 | + root.title(title) | |
15 | + return root | |
16 | + | |
17 | +def toplevel(master=None,title='tk',cnf={},**kw): | |
18 | + if kw: cnf = Tkinter._cnfmerge((cnf,kw)) | |
19 | + top = Tkinter.Toplevel(master,cnf) | |
20 | + top.title(title) | |
21 | + return top | |
22 | + | |
23 | +def popup_button(master,command,label='',title='tk',cnf={},**kw): | |
24 | + if kw: cnf = Tkinter._cnfmerge((cnf,kw)) | |
25 | + popup = lambda: command(toplevel(master,title,cnf)).pack() | |
26 | + return Tkinter.Button(master,text=label,command=popup) | |
27 | + | |
28 | +class Entry(Tkinter.Entry): | |
29 | + def __init__(self,master=None,cnf={},**kw): | |
30 | + if kw: cnf = Tkinter._cnfmerge((cnf,kw)) | |
31 | + if cnf.has_key('textvariable'): | |
32 | + v = cnf['textvariable'] | |
33 | + del cnf['textvariable'] | |
34 | + Tkinter.Entry.__init__(self,master,cnf) | |
35 | + self.config(textvariable=v) | |
36 | + self.bind('<Return>',v.fixed) | |
37 | + self.bind('<Leave>',v.update) | |
38 | + else: | |
39 | + Tkinter.Entry.__init__(self,master,cnf) | |
40 | + | |
41 | +class Menu(Tkinter.Menu): | |
42 | + def __init__(self,master=None,cnf={},**kw): | |
43 | + if kw: cnf = Tkinter._cnfmerge((cnf,kw)) | |
44 | + popup = popcnf('popup',cnf) | |
45 | + Tkinter.Menu.__init__(self,master,cnf) | |
46 | + self.cbname_ctrl_b1 = None | |
47 | + self.cbname_b3 = None | |
48 | + if popup: | |
49 | + self.cbname_ctrl_b1 = master.bind("<Control-Button-1>", self._rclicked) | |
50 | + self.cbname_b3 = master.bind(self._right_button(), self._rclicked) | |
51 | + def _rclicked(self,e): | |
52 | + self.tk_popup(e.x_root, e.y_root) | |
53 | + def _right_button(self): | |
54 | + import platform | |
55 | + if platform.system() == 'Darwin': | |
56 | + rbtn = '<Button-2>' | |
57 | + else: | |
58 | + rbtn = '<Button-3>' | |
59 | + return rbtn | |
60 | + def destroy(self): | |
61 | + if not self.cbname_ctrl_b1 == None: | |
62 | + self.master.unbind('<Control-Button-1>',self.cbname_ctrl_b1) | |
63 | + if not self.cbname_b3 == None: | |
64 | + self.master.unbind(self._right_button(),self.cbname_b3) | |
65 | + Tkinter.Menu.destroy(self) | |
66 | + | |
67 | +class TriggerButton(Tkinter.Button): | |
68 | + def __init__(self,master=None,cnf={},**kw): | |
69 | + if kw: cnf = Tkinter._cnfmerge((cnf,kw)) | |
70 | + if cnf.has_key('variable'): | |
71 | + v = cnf['variable'] | |
72 | + del cnf['variable'] | |
73 | + Tkinter.Button.__init__(self,master,cnf) | |
74 | + self.config(command=v.fixed) | |
75 | + else: | |
76 | + Tkinter.Button.__init__(self,master,cnf) | |
77 | + | |
78 | +class PopupButton(Tkinter.Button): | |
79 | + def __init__(self,master=None,command=None,label='',title='tk',cnf={},**kw): | |
80 | + if kw: cnf = Tkinter._cnfmerge((cnf,kw)) | |
81 | + popup = lambda: command(toplevel(master,title)).pack() | |
82 | + Tkinter.Button.__init__(self,master,cnf) | |
83 | + self.config(text=label,command=popup) | |
84 | + | |
85 | +class TriggerCheckbutton(Tkinter.Checkbutton): | |
86 | + def __init__(self,master=None,cnf={},**kw): | |
87 | + if kw: cnf = Tkinter._cnfmerge((cnf,kw)) | |
88 | + if cnf.has_key('variable'): | |
89 | + Tkinter.Checkbutton.__init__(self,master,cnf) | |
90 | + self.config(command=cnf['variable'].fixed) | |
91 | + else: | |
92 | + Tkinter.Checkbutton.__init__(self,master,cnf) | |
93 | + | |
94 | +# Checkbuttonは必ずしも決まったトリガの使い方ではないのでオーバーライドしない。 | |
95 | +# Checkbutton(master,variable=comp.port.variable) | |
96 | + | |
97 | +class GridPane(Tkinter.Frame): | |
98 | + def entry(self,label='',*variables): | |
99 | + widgets = [Entry(self,textvariable=v) for v in variables] | |
100 | + self.append(label,*widgets) | |
101 | + def trigger(self,label='',*variables): | |
102 | + widgets = [TriggerButton(self,text=label,variable=v) for v in variables] | |
103 | + row = self.grid_size()[1] | |
104 | + for i,w in enumerate(widgets): w.grid(row=row,column=i+1,sticky='EW') | |
105 | + def popup(self,label='',command=None,title='tk',cnf={},**kw): | |
106 | + if kw: cnf = Tkinter._cnfmerge((cnf,kw)) | |
107 | + popup = lambda: command(toplevel(self.master,title,cnf)).pack() | |
108 | + widget = Tkinter.Button(self,text='console',command=popup) | |
109 | + self.append(label,widget) | |
110 | + def entry_with_tcb(self,label,booleanvar,*variables): | |
111 | + row = self.grid_size()[1] | |
112 | + frm = Tkinter.Frame(self) | |
113 | + TriggerCheckbutton(frm,variable=booleanvar).pack(side=Tkinter.LEFT) | |
114 | + Tkinter.Label(frm,text=label).pack(side=Tkinter.LEFT) | |
115 | + frm.grid(row=row,column=0) | |
116 | + widgets = [Entry(self,textvariable=v) for v in variables] | |
117 | + for i,w in enumerate(widgets): w.grid(row=row,column=i+1) | |
118 | + def triggered_checkbutton(self,label,booleanvar): | |
119 | + row = self.grid_size()[1] | |
120 | + TriggerCheckbutton(self,variable=booleanvar,text=label).grid(row=row,column=0) | |
121 | +# Tkinter.Label(self,text=label).grid(row=row,column=1) | |
122 | + def append(self,label='',*widgets): | |
123 | + row = self.grid_size()[1] | |
124 | + Tkinter.Label(self,text=label).grid(row=row,column=0) | |
125 | + for i,w in enumerate(widgets): w.grid(row=row,column=i+1) | |
126 | + # | |
127 | + def wappend(self,*widgets): | |
128 | + row = self.grid_size()[1] | |
129 | + for i,w in enumerate(widgets): w.grid(row=row,column=i) | |
130 | + def wtrigger(self,*labelvariables): | |
131 | + return [TriggerButton(self,text=l,variable=v) for l,v in labelvariables] | |
132 | + | |
133 | +class Projection: | |
134 | + @classmethod | |
135 | + def db(cls): | |
136 | + from scipy import log10 | |
137 | + return {'forward' : lambda x: 10*log10(x), | |
138 | + 'backward' : lambda x: 10**(x/10.)} |
@@ -0,0 +1,42 @@ | ||
1 | +# coding: utf-8 | |
2 | +import Tkinter | |
3 | +# PyLAFをロードするとTkinterが暗黙でインポートされるが、 | |
4 | +# Pythonに不慣れなユーザが混乱しないよう、わかりやすさの為にTkinterを明示的にインポートした | |
5 | +from PyLAF import * | |
6 | +from scipy import savetxt | |
7 | + | |
8 | +class FGSIN(EasyComponent): | |
9 | + PLOTTER = Oscilloscope | |
10 | + def __init__(self,master=None): | |
11 | + EasyComponent.__init__(self,master) | |
12 | + self.mag = EasyPort(Variable(1.),'Magnitude [mV]').bind(self.trigger) | |
13 | + self.freq = EasyPort(Variable(0.01),'Frequency [MHz]').bind(self.trigger) | |
14 | + self.phase = EasyPort(Variable(0.),'Phase Offset [deg]').bind(self.trigger) | |
15 | + self.bias = EasyPort(Variable(0.),'Bias Voltage [mV]').bind(self.trigger) | |
16 | + self.dt = EasyPort(Variable(1.),'Time Step [us]').bind(self.trigger) | |
17 | + self.tend = EasyPort(Variable(500.),'End [us]').bind(self.trigger) | |
18 | + self._menu = [['File', ['Load', None], | |
19 | + ['Save', ['All', None], | |
20 | + ['As' , self.save]]], | |
21 | + ['Edit', ['Copy' , None], | |
22 | + ['Cut' , None], | |
23 | + ['Paste', None]] | |
24 | + ] | |
25 | + def trigger(self): | |
26 | + t = arange(0.,self.tend.get(),self.dt.get()) | |
27 | + self.sig_out.set(self.mag.get() * sin(2 * pi * self.freq.get() * t + self.phase.get() / 180. * pi) + self.bias.get()) | |
28 | + def save(self): | |
29 | + fname = tkFileDialog.asksaveasfilename(title='Save As?') | |
30 | + if fname != "": | |
31 | + file = open(fname,'w') | |
32 | + savetxt(file,self.sig_out.get().transpose(),delimiter=',') | |
33 | + file.close() | |
34 | + | |
35 | +def test(master=None): | |
36 | + o = App(master,FGSIN) | |
37 | + o.master.title('FGSIN'); o.pack() | |
38 | + o.component.trigger() | |
39 | + | |
40 | +if __name__ == '__main__': | |
41 | + test(Tkinter.Tk()) | |
42 | + Tkinter.mainloop() |
@@ -0,0 +1,37 @@ | ||
1 | +# coding: utf-8 | |
2 | +import Tkinter | |
3 | +from scipy import array | |
4 | +from PyLAF import EasyComponent, Embed, Port, Variable, Oscilloscope, Spectrum | |
5 | +from FGSIN import FGSIN | |
6 | + | |
7 | +class MIXER(EasyComponent): | |
8 | + PLOTTER = Oscilloscope | |
9 | + def __init__(self,master=None): | |
10 | + EasyComponent.__init__(self,master) | |
11 | + self.sig_a = Port(Variable(array([]))).bind(self.trigger) | |
12 | + self.sig_b = Port(Variable(array([]))).bind(self.trigger) | |
13 | + def trigger(self): | |
14 | + a, b = self.sig_a.get(), self.sig_b.get() | |
15 | + if a.shape == b.shape: | |
16 | + self.sig_out.set(self.sig_a.get() * self.sig_b.get()) | |
17 | + | |
18 | +def test_embed(master=None): | |
19 | + master.title('MODULATION:AM') | |
20 | + frm = Tkinter.Frame(master); frm.pack(side=Tkinter.RIGHT) | |
21 | + mx = Embed(frm,MIXER,text='MIXER:RF'); mx.pack(side=Tkinter.TOP) | |
22 | + vw = Embed(frm,Spectrum,text='FGSIN:IF'); vw.pack(side=Tkinter.TOP) | |
23 | + lo = Embed(master,FGSIN,text='FGSIN:LO'); lo.pack(side=Tkinter.RIGHT) | |
24 | + im = Embed(master,FGSIN,text='FGSIN:IF'); im.pack(side=Tkinter.RIGHT) | |
25 | + vw.xmin.set(0.); vw.xmax.set(.2) | |
26 | + lo.freq.set(0.1) | |
27 | + im.bias.set(1.0) | |
28 | + lo.dt.link(im.dt) | |
29 | + lo.tend.link(im.tend) | |
30 | + lo.sig_out.link(mx.sig_a) | |
31 | + im.sig_out.link(mx.sig_b) | |
32 | + mx.sig_out.link(vw.sig_in) | |
33 | + lo.component.trigger() | |
34 | + | |
35 | +if __name__ == '__main__': | |
36 | + test_embed(Tkinter.Tk()) | |
37 | + Tkinter.mainloop() |
@@ -0,0 +1,39 @@ | ||
1 | +# coding: utf-8 | |
2 | +from PyLAF import * | |
3 | +from MIXER import MIXER | |
4 | +from FGSIN import FGSIN | |
5 | + | |
6 | +class MODAM(Component): | |
7 | + def __init__(self,master=None): | |
8 | + Component.__init__(self,master) | |
9 | + self.fglo = fglo = FGSIN(self) | |
10 | + self.fgif = fgif = FGSIN(self) | |
11 | + self.mixer = mixer = MIXER(self) | |
12 | + fglo.dt.link(fgif.dt) | |
13 | + fglo.tend.link(fgif.tend) | |
14 | + fglo.sig_out.link(mixer.sig_a) | |
15 | + fgif.sig_out.link(mixer.sig_b) | |
16 | + fgif.bias.set(1.0) | |
17 | + fglo.freq.set(0.1) | |
18 | + def gui(self,master,cnf={},**kw): | |
19 | + frm = Frame(master) | |
20 | + out = Tkinter.Frame(frm); out.pack(side=Tkinter.RIGHT) | |
21 | + setattr(frm,'mixer',Embed(out,self.mixer,text='MIXER:RF')); frm.mixer.pack(side=Tkinter.TOP) | |
22 | + setattr(frm,'spana',Embed(out,Spectrum,text='Spectrum:RF')); frm.spana.pack(side=Tkinter.TOP) | |
23 | + setattr(frm,'fglo' ,Embed(frm,self.fglo,text='FGSIN:LO')); frm.fglo.pack(side=Tkinter.RIGHT) | |
24 | + setattr(frm,'fgif' ,Embed(frm,self.fgif,text='FGSIN:IF')); frm.fgif.pack(side=Tkinter.RIGHT) | |
25 | + self.mixer.sig_out.link(frm.spana.sig_in) | |
26 | + self.fglo.trigger() | |
27 | + self.fgif.trigger() | |
28 | + return frm | |
29 | + | |
30 | +def test(master=None): | |
31 | + master.title('MODULATION:AM') | |
32 | + master.option_add('*font','FixedSys 10') | |
33 | + o = App(master,MODAM); o.pack() | |
34 | + o.gui.spana.gui.resize((200,200)) # o.gui.spana.gui.configure(figsize=(200,200))でもいい | |
35 | + o.component.mixer.trigger() | |
36 | + | |
37 | +if __name__ == '__main__': | |
38 | + test(Tkinter.Tk()) | |
39 | + Tkinter.mainloop() |
@@ -0,0 +1,53 @@ | ||
1 | +# coding: utf-8 | |
2 | +from PyLAF import * | |
3 | +from MIXER import MIXER | |
4 | +from FGSIN import FGSIN | |
5 | +from functools import partial | |
6 | + | |
7 | +class MODAM(Component): | |
8 | + def __init__(self,master=None): | |
9 | + Component.__init__(self,master) | |
10 | + self.fglo = fglo = FGSIN(self) | |
11 | + self.fgif = fgif = FGSIN(self) | |
12 | + self.mixer = mixer = MIXER(self) | |
13 | + fglo.dt.link(fgif.dt) | |
14 | + fglo.tend.link(fgif.tend) | |
15 | + fglo.sig_out.link(mixer.sig_a) | |
16 | + fgif.sig_out.link(mixer.sig_b) | |
17 | + fgif.bias.set(1.0) | |
18 | + fglo.freq.set(0.1) | |
19 | + self._menu = [['Sub', ['FGLO', partial(self.popup,master=master,comp=self.fglo)], | |
20 | + ['SPANA', partial(self.popup_spana,master=master)]]] | |
21 | + def gui(self,master,cnf={},**kw): | |
22 | + frm = Frame(master) | |
23 | + setattr(frm,'mixer',Embed(frm,self.mixer,text='MIXER:RF')); frm.mixer.pack(side=Tkinter.RIGHT) | |
24 | + setattr(frm,'fgif' ,Embed(frm,self.fgif,text='FGSIN:IF')); frm.fgif.pack(side=Tkinter.RIGHT) | |
25 | + self.fgif.trigger() | |
26 | + return frm | |
27 | + def guib(self,master,cnf={},**kw): | |
28 | + frm = Frame(master) | |
29 | + setattr(frm,'mixer',Embed(frm,self.mixer,text='MIXER:RF')); frm.mixer.pack(side=Tkinter.RIGHT) | |
30 | + setattr(frm,'fgif' ,Embed(frm,self.fgif,text='FGSIN:IF')); frm.fgif.pack(side=Tkinter.RIGHT) | |
31 | + setattr(frm,'fglo' ,Embed(frm,self.fglo,text='FGSIN:LO')); frm.fglo.pack(side=Tkinter.RIGHT) | |
32 | + self.fgif.trigger() | |
33 | + self.fglo.trigger() | |
34 | + return frm | |
35 | + def menu(self,master,cnf={},**kw): | |
36 | + menu = EasyMenu(master,list=self._menu,cnf=cnf,**kw) | |
37 | + return menu | |
38 | + def popup(self,master,comp): | |
39 | + App(Tkinter.Toplevel(master),comp).pack() | |
40 | + comp.trigger() | |
41 | + def popup_spana(self,master): | |
42 | + o = App(Tkinter.Toplevel(master),Spectrum); o.pack() | |
43 | + self.mixer.sig_out.link(o.component.sig_in) | |
44 | + self.mixer.sig_out.set(self.mixer.sig_out.get()) | |
45 | + | |
46 | +def test(master=None): | |
47 | + master.title('MODULATION:AM') | |
48 | + o = App(master,MODAM,gui=False); o.pack() | |
49 | + o.configure(gui=o.component.guib) | |
50 | + | |
51 | +if __name__ == '__main__': | |
52 | + test(Tkinter.Tk()) | |
53 | + Tkinter.mainloop() |
@@ -0,0 +1,44 @@ | ||
1 | +# coding: utf-8 | |
2 | +from PyLAF import * | |
3 | +from MIXER import MIXER | |
4 | +from FGSIN import FGSIN | |
5 | +from functools import partial | |
6 | + | |
7 | +class MODAM(Component): | |
8 | + def __init__(self,master=None): | |
9 | + Component.__init__(self,master) | |
10 | + self.fglo = fglo = FGSIN(self) | |
11 | + self.fgif = fgif = FGSIN(self) | |
12 | + self.mixer = mixer = MIXER(self) | |
13 | + fglo.dt.link(fgif.dt) | |
14 | + fglo.tend.link(fgif.tend) | |
15 | + fglo.sig_out.link(mixer.sig_a) | |
16 | + fgif.sig_out.link(mixer.sig_b) | |
17 | + fgif.bias.set(1.0) | |
18 | + fglo.freq.set(0.1) | |
19 | + self._menu = [['Sub', ['FGLO', partial(self.popup,master=master,comp=self.fglo)], | |
20 | + ['SPANA', partial(self.popup_spana,master=master)]]] | |
21 | + def gui(self,master,cnf={},**kw): | |
22 | + frm = Frame(master) | |
23 | + setattr(frm,'mixer',Embed(frm,self.mixer,text='MIXER:RF')); frm.mixer.pack(side=Tkinter.RIGHT) | |
24 | + setattr(frm,'fgif' ,Embed(frm,self.fgif,text='FGSIN:IF')); frm.fgif.pack(side=Tkinter.RIGHT) | |
25 | + self.fgif.trigger() | |
26 | + return frm | |
27 | + def menu(self,master,cnf={},**kw): | |
28 | + menu = EasyMenu(master,list=self._menu,cnf=cnf,**kw) | |
29 | + return menu | |
30 | + def popup(self,master,comp): | |
31 | + App(Tkinter.Toplevel(master),comp).pack() | |
32 | + comp.trigger() | |
33 | + def popup_spana(self,master): | |
34 | + o = App(Tkinter.Toplevel(master),Spectrum); o.pack() | |
35 | + self.mixer.sig_out.link(o.component.sig_in) | |
36 | + self.mixer.sig_out.set(self.mixer.sig_out.get()) | |
37 | + | |
38 | +def test(master=None): | |
39 | + master.title('MODULATION:AM') | |
40 | + o = App(master,MODAM); o.pack() | |
41 | + | |
42 | +if __name__ == '__main__': | |
43 | + test(Tkinter.Tk()) | |
44 | + Tkinter.mainloop() |
@@ -0,0 +1,20 @@ | ||
1 | +# coding: utf-8 | |
2 | +import Tkinter | |
3 | +from PyLAF import App | |
4 | +from FGSIN import FGSIN | |
5 | +from MIXER import MIXER | |
6 | + | |
7 | +master = Tkinter.Tk() | |
8 | + | |
9 | +mx = App(master,MIXER); mx.master.title('MIXER:RF'); mx.pack() | |
10 | +lo = App(Tkinter.Toplevel(master),FGSIN); lo.master.title('FGSIN:LO'); lo.pack() | |
11 | +im = App(Tkinter.Toplevel(master),FGSIN); im.master.title('FGSIN:IF'); im.pack() | |
12 | +lo.freq.set(0.1) | |
13 | +im.bias.set(1.0) | |
14 | +lo.sig_out.link(mx.sig_a) | |
15 | +im.sig_out.link(mx.sig_b) | |
16 | +lo.dt.link(im.dt) | |
17 | +lo.tend.link(im.tend) | |
18 | +mx.component.trigger() | |
19 | + | |
20 | +Tkinter.mainloop() |
@@ -0,0 +1,21 @@ | ||
1 | +# coding: utf-8 | |
2 | +import Tkinter | |
3 | +from PyLAF import Embed | |
4 | +from FGSIN import FGSIN | |
5 | +from MIXER import MIXER | |
6 | + | |
7 | +master = Tkinter.Tk() | |
8 | +master.title('MODULATION:AM') | |
9 | + | |
10 | +mx = Embed(master,MIXER,text='MIXER:RF'); mx.pack(side=Tkinter.RIGHT) | |
11 | +lo = Embed(master,FGSIN,text='FGSIN:LO'); lo.pack(side=Tkinter.RIGHT) | |
12 | +im = Embed(master,FGSIN,text='FGSIN:IF'); im.pack(side=Tkinter.RIGHT) | |
13 | +im.bias.set(1.0) | |
14 | +lo.freq.set(0.1) | |
15 | +lo.dt.link(im.dt) | |
16 | +lo.tend.link(im.tend) | |
17 | +lo.sig_out.link(mx.sig_a) | |
18 | +im.sig_out.link(mx.sig_b) | |
19 | +mx.component.trigger() | |
20 | + | |
21 | +Tkinter.mainloop() |
@@ -0,0 +1,25 @@ | ||
1 | +# coding: utf-8 | |
2 | +import Tkinter | |
3 | +from PyLAF import Embed, App, Spectrum | |
4 | +from FGSIN import FGSIN | |
5 | +from MIXER import MIXER | |
6 | + | |
7 | +master = Tkinter.Tk() | |
8 | +master.title('MODULATION:AM') | |
9 | + | |
10 | +frm = Tkinter.Frame(master); frm.pack(side=Tkinter.RIGHT) | |
11 | +mx = Embed(frm,MIXER,text='MIXER:RF'); mx.pack(side=Tkinter.TOP) | |
12 | +lo = Embed(master,FGSIN,text='FGSIN:LO'); lo.pack(side=Tkinter.RIGHT) | |
13 | +im = Embed(master,FGSIN,text='FGSIN:IF'); im.pack(side=Tkinter.RIGHT) | |
14 | +im.bias.set(1.0) | |
15 | +lo.freq.set(0.1) | |
16 | +lo.dt.link(im.dt) | |
17 | +lo.tend.link(im.tend) | |
18 | +lo.sig_out.link(mx.sig_a) | |
19 | +im.sig_out.link(mx.sig_b) | |
20 | + | |
21 | +vw = App(Tkinter.Toplevel(master),Spectrum); vw.master.title('Spectrum:RF'); vw.pack(side=Tkinter.TOP) | |
22 | +mx.sig_out.link(vw.sig_in) | |
23 | +mx.component.trigger() | |
24 | + | |
25 | +Tkinter.mainloop() |