Commit MetaInfo

Revisãoa0254635c1acf7d0070b0ef58a78719d049ebc33 (tree)
Hora2013-03-04 03:19:33
Autorbijoux
Commiterbijoux

Mensagem de Log

restore CanvasWidget

Mudança Sumário

Diff

diff -r 6dc5985e8a75 -r a0254635c1ac src/experiment/vtk_tree_pylafiii.py
--- a/src/experiment/vtk_tree_pylafiii.py Mon Mar 04 01:50:45 2013 +0900
+++ b/src/experiment/vtk_tree_pylafiii.py Mon Mar 04 03:19:33 2013 +0900
@@ -1,6 +1,6 @@
11 # coding: utf-8
22
3-#TODO: キャンバスウィジェット
3+#TODO: オートトリガ
44 #TODO: マップの保存とリストア
55 #TODO: パラメータの保存
66 #TODO: ウィンドウレシピとフレームワークメニュー
diff -r 6dc5985e8a75 -r a0254635c1ac src/pylafiii/__init__.py
--- a/src/pylafiii/__init__.py Mon Mar 04 01:50:45 2013 +0900
+++ b/src/pylafiii/__init__.py Mon Mar 04 03:19:33 2013 +0900
@@ -3,3 +3,4 @@
33 import chef
44 import data
55 import vtkext
6+import tkext
diff -r 6dc5985e8a75 -r a0254635c1ac src/pylafiii/chef.py
--- a/src/pylafiii/chef.py Mon Mar 04 01:50:45 2013 +0900
+++ b/src/pylafiii/chef.py Mon Mar 04 03:19:33 2013 +0900
@@ -14,6 +14,8 @@
1414 import Tkinter
1515 import ttk
1616 import ingredient as laf
17+import tkext
18+import vtkext
1719 import data
1820 from ingredient import rule,event,Logic,Port
1921
@@ -115,11 +117,6 @@
115117 cls = sys.modules[module_name].__dict__[cls_name] # モジュール名とクラス名からクラスオブジェクトを取得する
116118 dic[cls_name] = cls
117119 return dic
118-# def getcls(self,element):
119-# module_name = element.getAttribute('module')
120-# cls_name = element.getAttribute('cls')
121-# cls = sys.modules[module_name].__dict__[cls_name]
122-# return cls, cls_name
123120
124121 class CanvasItem(object):
125122 def __init__(self,canvas,tag=None,loc=None):
@@ -152,12 +149,6 @@
152149 # 先にキャンバスが開放されてしまったときに出るエラーの抑制
153150 if self.canvas:
154151 self.canvas.delete(id)
155-# try:
156-# self.canvas.delete(id)
157-# except Tkinter.TclError:
158-# pass
159-# except AttributeError:
160-# pass
161152 del self.cid[:]
162153 #FIXME: debug code. eliminate in actual version.
163154 # オブジェクトが開放されるかどうかのチェックコード。実行バージョンではガーベジコレクションが働くよう無効にする。
@@ -211,6 +202,120 @@
211202 ox,oy = args
212203 self.cid.append(self.canvas.create_window(x+ox,y+oy,tags=self.tag,**kw))
213204
205+#FIXME: CanvasLogic と共通している部分を抜き出す
206+class CanvasWidget(object):
207+ class Menu(Tkinter.Menu):
208+ def __init__(self,master=None,obj=None,cnf={},**kw):
209+ Tkinter.Menu.__init__(self,master,cnf,**kw)
210+ self.add_command(label='remove',command=obj.remove_logic)
211+ def __init__(self,canvas,widget,tag=None,loc=None):
212+ #canvas._items.append(self)
213+ self._canvas = weakref.ref(canvas)
214+ self._widget = weakref.ref(widget)
215+ self._start = None
216+ self._items = []
217+ self._tkevent_id = {}
218+ if tag:
219+ self.tag = tag
220+ else:
221+ self.tag = str(uuid.uuid4())
222+ if loc:
223+ self.loc = loc
224+ else:
225+ self.loc = [200*random.random()+100,200*random.random()+50]
226+ # ウィジェットのポートが未生成だと困るので
227+ xi = laf.getproxports(widget)
228+ xi[xi.keys()[0]].name(widget)
229+ #
230+ self._items.append(canvas.create_window(*self.loc,window=widget))
231+ self.myports = weakref.WeakValueDictionary()
232+ self.generate_canvas_ports()
233+ self.bind_children(widget,'<Shift-Button1-Motion>',self.leftdrag)
234+ self.bind_children(widget,'<Shift-Button1-ButtonRelease>',self.leftrelease)
235+ self.bind_children(widget,'<Shift-Button-2>',lambda e:self.canvas.rmenu_queue.append((self.Menu,self,e)))
236+ @property
237+ def canvas(self):
238+ return self._canvas()
239+ @property
240+ def widget(self):
241+ return self._widget()
242+ def bind_children(self,obj,event,func):
243+ try:
244+ obj.bind(event,func)
245+ except AttributeError:
246+ pass
247+ for k,o in obj.children.items():
248+ self.bind_children(o,event,func)
249+ def bind(self,sequence,func):
250+ self._tkevent_id[sequence] = self.canvas.tag_bind(self.tag,sequence,func)
251+ def leftrelease(self,e):
252+ if self._start is not None:
253+ self._start = None
254+ def leftdrag(self,e):
255+ if self._start is None:
256+ self._start = [e.x_root,e.y_root]
257+ return
258+ x0,y0 = self._start
259+ dx,dy = e.x_root-x0,e.y_root-y0
260+ self._start = [e.x_root,e.y_root]
261+ self.move(dx,dy)
262+ # 配下のポートも同時に動かす
263+ for item in [v for v in self.myports.itervalues()]:
264+ item.move(dx,dy)
265+ def move(self,dx,dy):
266+ for tag in self._items:
267+ self.canvas.move(tag,dx,dy)
268+ x,y = self.loc
269+ self.loc = (x+dx,y+dy)
270+ def destroy(self):
271+ self.event = None
272+ for sequence,id in self._tkevent_id.iteritems():
273+ self.canvas.tag_unbind(self.tag,sequence,id)
274+# if self in self.canvas._items: # CanvasLinkの廃棄処理が完了する前にもう一度呼び出されてしまうバグへの応急処置
275+# self.canvas._items.remove(self)
276+#
277+ def erase(self):
278+ for id in self._items:
279+ self.canvas.delete(id)
280+ del self._items[:]
281+ def __del__(self):
282+ # イベントをバインドした状態だとオブジェクトが消去されないことに注意
283+ self.erase()
284+ # print 'removed', self.__class__.__name__
285+ @property
286+ def proxyports(self):
287+ return laf.getproxports(self.widget)
288+ def generate_canvas_ports(self):
289+ def generate(ports,x,y,xoffset=0,yoffset=0,direction=Tkinter.LEFT):
290+ yoffset = -16 * (len(ports) - 1) / 2
291+ keys = ports.keys()
292+ keys.sort()
293+ for key in keys:
294+ actprt = ports[key]
295+ myport = CanvasPort(self.canvas,actprt,direction=direction,tag=self.tag,loc=(x+xoffset,y+yoffset),name=key)
296+ self.myports[key] = myport
297+ yoffset += 16
298+ x,y = self.loc
299+ lports,rports = {},{}
300+ #FIXME: ユーザが任意に左右を振り分けられるようにする
301+ for name,port in laf.getports(self.widget).iteritems():
302+ # 右に表示するか左に表示するか振り分け
303+ if isinstance(port,laf.ActualRule):
304+ rports[name] = port
305+ else:
306+ lports[name] = port
307+ # 座標計算
308+ x0,y0,x1,y1 = self.canvas.bbox(self._items[0])
309+ if x1 - x0 == 1 and y1 - y0 == 1:
310+ width,height = self.widget.winfo_reqwidth(),self.widget.winfo_reqheight()
311+ x0,x1 = x0-width/2,x1+width/2
312+ y0,y1 = y0-height/2,y1+height/2
313+ # ポートアイテムを生成する
314+ generate(lports,x0,y,-4,direction=Tkinter.LEFT)
315+ generate(rports,x1,y,4,direction=Tkinter.RIGHT)
316+ def remove_widget(self):
317+ self.widget.destroy()
318+ self.destroy()
214319
215320 class CanvasLogic(CanvasItem):
216321 class Menu(Tkinter.Menu):
@@ -228,7 +333,6 @@
228333 self._logic = weakref.ref(logic)
229334 self.myports = weakref.WeakValueDictionary()
230335 self.draw(self.shape)
231- #self.generate_myports()
232336 self.bind('<Button1-Motion>',self.leftdrag)
233337 self.bind('<Button1-ButtonRelease>',self.leftrelease)
234338 self.bind('<Double-Button-1>',self.popup_equipment)
@@ -281,17 +385,12 @@
281385 def generate(ports,xoffset=0,yoffset=0,direction=Tkinter.LEFT):
282386 # ポートを生成する
283387 # 生成したポートは self.myports に格納する
284- #FIXME: 単純化のために最初のポートの位置を中心に合わせたのを直しておくように
285- #yoffset = -16 * (len(ports) - 1) / 2
286- yoffset = 0
287- #
288388 keys = ports.keys()
289389 keys.sort() # ターミナルの配置を英数降順で
290390 for key in keys:
291391 actprt = ports[key]
292- myport = CanvasPort(self.canvas,actprt,direction=direction,tag=self.tag,loc=(x+xoffset,y+yoffset),name=key)
392+ myport = CanvasPort(self.canvas,actprt,direction=direction,tag=self.tag,loc=(x+xoffset,y),name=key)
293393 self.myports[key] = myport
294- yoffset += 16
295394 x,y = self.loc
296395 lports,rports = {},{}
297396 #FIXME: ユーザが任意に左右を振り分けられるようにする
@@ -334,32 +433,6 @@
334433 px,py = o.loc
335434 o.move(0,y+yoffset-py)
336435 yoffset +=16
337-#
338-
339-# def generate_myports(self):
340-# # ポートアイテムを生成する
341-# def generate(ports,xoffset=0,yoffset=0,direction=Tkinter.LEFT):
342-# yoffset = -16 * (len(ports) - 1) / 2
343-# keys = ports.keys()
344-# keys.sort() # ターミナルの配置を英数降順で
345-# for key in keys:
346-# actprt = ports[key].actualport(self.logic)
347-# myport = CanvasPort(self.canvas,actprt,direction=direction,tag=self.tag,loc=(x+xoffset,y+yoffset),name=key)
348-# #FIXME: remove this line
349-# # self.canvas.following_terminals[attr] = weakref.ref(myport)
350-# self.myports.append(myport)
351-# yoffset += 16
352-# x,y = self.loc
353-# lports,rports = {},{}
354-# #FIXME: current status
355-# for name,port in laf.getproxports(self.logic).iteritems():
356-# # 右に表示するか左に表示するか振り分け
357-# if isinstance(port,laf.Rule):
358-# rports[name] = port
359-# else:
360-# lports[name] = port
361-# generate(lports,-50,direction=Tkinter.LEFT)
362-# generate(rports,50,direction=Tkinter.RIGHT)
363436 def popup_equipment(self,e): # for Tk Event
364437 logic = self.logic
365438 module = inspect.getmodule(logic)
@@ -416,158 +489,11 @@
416489 else:
417490 value = getattr(logic,k).get()
418491 l = Tkinter.Label(self,text=k); l.grid(column=0,row=i)
419- e = Entry(self); e.grid(column=1,row=i)
492+ e = tkext.Entry(self); e.grid(column=1,row=i)
420493 setattr(e,'value',value)
421494 laf.link(v,laf.port(e,'value'))
422495 i += 1
423-
424-##TODO: 結構トリッキーなのでドキュメントにしておくこと
425-##FIXME: 縁ありフレームにする
426-#class SmartPanel(Tkinter.LabelFrame,object):
427-# # データクラスの変更を補足してデータ更新を促すクラス
428-# @event
429-# def receiver(self):
430-# setattr(self.logic,self.portid,getattr(self.logic,self.portid))
431-# def __init__(self,master=None,logic=None,portid=None,*args,**kw):
432-# Tkinter.LabelFrame.__init__(self,master,text=portid,*args,**kw)
433-# self._logic = weakref.ref(logic)
434-# self.portid = portid
435-# @property
436-# def logic(self):
437-# return self._logic()
438-##FIXME: 適切な名前へ!
439-#class SmartControlPanel(Tkinter.Frame,object):
440-# # データクラスの変更を捕捉する機能を有するパネル
441-# @event
442-# def receiver(self):
443-# self.updated()
444-# def updated(self):
445-# setattr(self.logic,self.name,getattr(self.logic,self.name))
446-# def __init__(self,*args,**kw):
447-# if kw.has_key('logic'):
448-# logic = kw['logic']
449-# del kw['logic']
450-# Tkinter.Frame.__init__(self,*args,**kw)
451-# self.logic = weakref.ref(logic)
452-# self.name
453-# members = editable(logic)
454-# i = 0
455-# for k,v in members.iteritems():
456-# value = getattr(logic,k)
457-# if isinstance(value,data.SpecificData):
458-# #TODO: より汎用性のある方式へ変更しなければいけない
459-# p = SmartControlPanel(self,logic=value); p.grid(column=0,row=i,columnspan=2)
460-# else:
461-# l = Tkinter.Label(self,text=k); l.grid(column=0,row=i)
462-# e = Entry(self); e.grid(column=1,row=i)
463-# setattr(e,'value',value)
464-# laf.link(laf.port(logic,k),laf.port(e,'value'))
465-# i += 1
466-
467496
468-class Entry(Tkinter.Entry,object):
469- def __init__(self,master=None,format='%s',cnf={},**kw):
470- Tkinter.Entry.__init__(self,master,cnf,**kw)
471- self.format = format
472- self._buffer = None
473- self._updated = False
474- self._callback_id = self.bind('<Return>',self._backward) # RETURNキーが押されたら値を確定する
475- self._idle_loop()
476- def _idle_loop(self):
477- if self._updated:
478- self._forward(self._buffer)
479- self._updated = False
480- self.after(50,self._idle_loop)
481- @event
482- def value(self):
483- self._buffer = self.value
484- self._updated = True
485- def _forward(self,value):
486- self.delete(0,len(self.get()))
487- self.insert(0,self.format % value)
488- def _backward(self,*args):
489- container = laf.port(self,'value').container
490- try:
491- self.value = type(container.value)(self.get())
492- except ValueError:
493- self._buffer = container.value
494- self._updated = True
495-
496-#FIXME:
497-class StayEventDispatcher:
498- def __init__(self,obj):
499- self.isstayed = False
500-
501-#class SelectedPorts(object):
502-# selection = Port([])
503-# def select(self,actprt):
504-# self.selection.append(actprt)
505-# def isselected(self,actprt):
506-# return actprt in self.selection
507-
508-#FIXME:
509-#class Tracker:
510-# '''
511-# ある実体と化身との対応を双方向で追跡する。実体へは参照を張らないので生存には影響しない。
512-# '''
513-# def __init__(self,reverse=False):
514-# self.reverse = reverse
515-# self.substances = weakref.WeakKeyDictionary()
516-# if self.reverse:
517-# self.phantoms = weakref.WeakValueDictionary()
518-# def track(self,substance,phantom):
519-# self.substances[substance] = phantom
520-# if self.reverse:
521-# self.phantoms[phantom] = substance
522-# def insubstances(self,substance):
523-# return self.substances.has_key(substance)
524-# def getsubstance(self,phantom):
525-# return self.substances[phantom]
526-# def inphantoms(self,phantom):
527-# return self.phantoms.has_key(phantom)
528-# def getphantom(self,substance):
529-# return self.phantoms[substance]
530-#
531-#class SelectedPortsWidget(ttk.Treeview,object):
532-# selection = Port([])
533-# def __init__(self,*args,**kw):
534-# ttk.Treeview.__init__(self,selectmode='browse',*args,**kw)
535-# #self.forward = weakref.WeakKeyDictionary() # following items
536-# #self.reverse = {} # アイテムIDから実体への逆参照リスト
537-# self.tracker = Tracker()
538-# #self.Menu(self,name='menu')
539-# #self.bind('<Button-2>',self.popup_menu)
540-# self.loop()
541-# def loop(self):
542-# self.populate()
543-# self.after(500,self.loop)
544-# def populate(self):
545-# if not self.selection: return # selectionが空っぽであればなにもしない
546-# # コンポーネントをツリーから削除する
547-# candidate = []
548-# for iid in self.get_children():
549-# if not self.tracker.inphantoms(iid):
550-# candidate.append(iid)
551-# print self.get_children()
552-# self.insert('','end',text='test')
553-## for id,v in self.reverse.iteritems():
554-## if v() is None: candidate.append(id)
555-## for id in candidate:
556-## self.delete(id)
557-## del self.reverse[id]
558-## # コンポーネントをツリーに追加する
559-## if self.forward.has_key(logic):
560-## iid = self.forward[logic]
561-## else:
562-## iid = ''
563-## # コンポーネントをツリーに追加する
564-## for k,v in logic.children.iteritems():
565-## if not self.forward.has_key(v):
566-## id = self.insert(iid,'end',text=v.__class__.__name__)
567-## self.forward[v] = id
568-## self.reverse[id] = weakref.ref(v)
569-## self.populate_children(v)
570-
571497 class CanvasPort(CanvasItem):
572498 class Menu(Tkinter.Menu):
573499 def __init__(self,master=None,obj=None,cnf={},**kw):
@@ -651,20 +577,6 @@
651577 自身をアンリンクする
652578 '''
653579 self.substance.unlink()
654- #FIXME: garbage?
655-# canvas_node = self.canvas.following_nodes[node]()
656-# for canvas_link in canvas_node._canvas_links:
657-# if self is canvas_link()._canvas_terminal():
658-# canvas_node._canvas_links.remove(canvas_link())
659-# canvas_link().destroy()
660-# break
661-# # node を複製してすり替える
662-# new_node = self.canvas.chef.pylaf.Node(node.value)
663-# self.attribute.unregister()
664-# self.attribute.register(new_node)
665-# # 新しくCanvasNodeインスタンスを生成して関連づける
666-# self.generate_canvas_nodes()
667-#
668580 def evaluate_terminal(self):
669581 aprt = self.substance
670582 getattr(aprt.owner,aprt.name)
@@ -685,11 +597,6 @@
685597 if self.substance == None:
686598 self.destroy()
687599 return
688- #FIXME: garbage?
689-# if self.node == None:
690-# if self not in self.canvas.scrap: # CanvasLinkを生成するときに余分にイベントを発行してしまうバグの影響で複数回リストへ登録してしまうことがあるので緊急対処
691-# self.canvas.scrap.append(self)
692-# return
693600 # --
694601 # 必要に応じてリンクアイテムを生成する
695602 # --
@@ -720,14 +627,6 @@
720627 actualitems = [linkitem().portitem for linkitem in self.linkitems]
721628 for pitem in portitems:
722629 if pitem not in actualitems: # 追跡するべきポートアイテムの中から未追跡のアイテムがあれば
723- #FIXME: どんなケースで再帰呼び出しが発生する?
724- #try:
725- # self.__check_recursive
726- #except AttributeError:
727- # self.__check_recursive = None
728- #else:
729- # del self.__check_recursive
730- # return
731630 self.linkitems.append(CanvasLink(self.canvas,self,pitem)) # 追跡を開始する
732631 def destroy(self):
733632 # 関連する link を先に破壊する
@@ -895,8 +794,9 @@
895794 klass = inspect.getmembers(mdl,lambda x:inspect.isclass(x))
896795 for k,cls in klass:
897796 for c in inspect.getmro(cls):
898- if c is laf.Logic:
899- result[cls] = None
797+ if c is laf.Logic or c is tkext.tkWidget:
798+ if cls is not laf.Logic and cls is not tkext.tkWidget and cls is not vtkext.vtkLogic:
799+ result[cls] = None
900800 return result
901801
902802 class ClassChooser(ttk.Treeview,object):
@@ -1002,8 +902,12 @@
1002902 cls = self.chosenclass
1003903 if not cls:
1004904 return
1005- logic = cls(self.root)
1006- self._location_buffer[logic] = self.rmenu.loc
905+ if isancestor(cls,Logic) or isancestor(cls,vtkext.vtkLogic):
906+ logic = cls(self.root)
907+ self._location_buffer[logic] = self.rmenu.loc
908+ if isancestor(cls,tkext.tkWidget):
909+ loc = self.rmenu.loc
910+ CanvasWidget(self,cls(self),loc=loc)
1007911 def cook_collabomap(self,recipe):
1008912 collabomap = recipe.collabomap
1009913 root = self.root
@@ -1012,4 +916,9 @@
1012916 logic = self.root.children[name]
1013917 loc = int(e.getAttribute('locx')),int(e.getAttribute('locy'))
1014918 self._location_buffer[logic] = loc
1015-
\ No newline at end of file
919+
920+def isancestor(cls,ancestor):
921+ for c in inspect.getmro(cls):
922+ if c is ancestor:
923+ return True
924+ return False
diff -r 6dc5985e8a75 -r a0254635c1ac src/pylafiii/tkext.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pylafiii/tkext.py Mon Mar 04 03:19:33 2013 +0900
@@ -0,0 +1,36 @@
1+# coding: utf-8
2+
3+import Tkinter
4+import ingredient as laf
5+from ingredient import event
6+
7+class tkWidget(object): pass
8+
9+class Entry(Tkinter.Entry,tkWidget):
10+ def __init__(self,master=None,format='%s',cnf={},**kw):
11+ Tkinter.Entry.__init__(self,master,cnf,**kw)
12+ self.format = format
13+ self._buffer = None
14+ self._updated = False
15+ self._callback_id = self.bind('<Return>',self._backward) # RETURNキーが押されたら値を確定する
16+ self._idle_loop()
17+ def _idle_loop(self):
18+ if self._updated:
19+ self._forward(self._buffer)
20+ self._updated = False
21+ self.after(50,self._idle_loop)
22+ @event
23+ def value(self):
24+ self._buffer = self.value
25+ self._updated = True
26+ def _forward(self,value):
27+ self.delete(0,len(self.get()))
28+ self.insert(0,self.format % value)
29+ def _backward(self,*args):
30+ container = laf.port(self,'value').container
31+ try:
32+ self.value = type(container.value)(self.get())
33+ except ValueError:
34+ self._buffer = container.value
35+ self._updated = True
36+
Show on old repository browser