#! /usr/bin/python
# -*- coding: UTF-8 -*-

# IOFieldsinText : IOFieldsinText is a text widget which allow to have Input/Output Fields

# 0.1.2 (30 Mar 2013)
#  BUG fix
# 0.1.1 (11 Mar 2013)
#  BUG fix


# 0.1.0 (25 Feb 2013)
#  New release

# Copyright (c) 2013, Miki Ishimaru  <miki_ishimaruusers.sourceforge.jp>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
#    * Redistributions of source code must retain the above copyright
#      notice, this list of conditions and the following disclaimer.
#    * Redistributions in binary form must reproduce the above copyright
#      notice, this list of conditions and the following disclaimer in the
#      documentation and/or other materials provided with the distribution.
#    * Neither the name of the Miki Ishimaru nor the names of its contributors
#      may be used to endorse or promote products derived from this software
#      without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.



import Tkinter
import sys
import types


import UserDict

class Key_dict(UserDict.IterableUserDict):
  def __setitem__(self, x, y):
    self.data[x] = y
    self.ft.write_IOField(x)

class IOFieldsinText(Tkinter.Text):
  def __init__(self,master,text="",parameter=None,IFields_name=None,Fields_type=None,**kw):
     ###値の設定###
    self.default_str = "***" 
    self.text = text
    if parameter==None:parameter={}
    self.parameter=parameter 	# 入力枠の中身の保存用の辞書->fn_change_parameter
    self.output = 0
    if IFields_name==None:IFields_name=[]
    self.ifields_ls = IFields_name
    if Fields_type==None:Fields_type={}
    self.fields_type = self.fn_fields_type(Fields_type)
    self.fields_format = {}
    self.fields_format2 = {}
    self.fields_out = {}
    ###Textの作成###
    kw = self.fn_make_scrollbar(master,**kw)
    Tkinter.Text.__init__(self, master, **kw)
    self.set_text(self.text)
    self.fn_make_popup_menu()
    ###keyが押されたときのcallback###
    self.bind("<Delete>", self.cb_delete)
    self.bind("<BackSpace>", self.cb_backspace)
    self.bind("<Control-KeyPress-v>", self.cb_ctrl_v)
    self.bind("<Control-KeyPress-x>", self.cb_ctrl_x)
    self.bind("<Control-KeyPress-c>", self.cb_ctrl_c)
    self.bind("<Key>", self.cb_other_key)
    self.bind("<Button-2>", lambda e: "break")
    self.bind("<Button-3>", self.cb_popup_menu)		# ポップアップメニュー(右クリックメニュー)
 
  def set_text(self,text=""):
    u"""IOFieldsinTextのテキストを設定する
    """
    if text == "":text = ["IOFieldsinText receives no text!!"]
    else:
      text=text.split("\n")	# 改行で分割し、リストに
      text=[ii.strip("\t") for ii in text]	# 両側タブは除去して
#      text=[ii.strip(" ")  for ii in text] 	# 空白文字は除去して	【行の前の空白文字列だけ除去する】
    for x in text:
      self.insert(Tkinter.END,x)
      self.insert(Tkinter.END,"\n")
    self.fn_convert_to_fields()
  def fn_convert_to_fields(self):
    u"""テキストをFieldsに変換する
    Tkinter.Text.mark_set(name, index)
    name:markの名前(文字列)、index:マークを挿入する場所
  前の文字を増やしたり、消すと、マークも一緒に動く
  マークの場所の文字を消すと、そこにずれて入ってきた文字にマークが移る
    mark_ls'    :  [['name','index'],...]
    """
    index2 = "0.0"
    try:
      while True:
        parameter = self.parameter
        index1 = self.search("%",index2)
        if self.compare(index1, "<=", index2):break
        index1_next = self.fn_next_char(index1)
        if self.get(index1_next)=="%":	# "%%"の場合、無視する
          self.delete(index1_next)
          index2 = index1_next
        else:
          index3 = self.search(")",index1)
          index2 = self.search("[diouxXeEfFgGcrs]",index3,regexp=True)
          index4 = self.fn_next_char(index2)
          format = self.get(index1,index4)
          key = self.get(index1,index3)[2:]

          mark1,mark2 = self.fn_make_markname_for_OField(key)
          self.mark_set(mark1,index1)
          self.mark_set(mark2,index2)
           ###括弧の位置にマークをつける###
          if key in self.ifields_ls:	# 入力枠
            mark1i = "s_%s" % key
            mark2i = "e_%s" % key
            self.mark_set(mark1i,index1)
            self.mark_set(mark2i,index2)
          self.fields_format[mark1] = format
          self.fields_format2[mark1] = "%s:%s"%(key, format)
           ###初めのマーク:%の次に空白を入れ、%と括弧を消す(マークは空白に移る)###
          index1_next = self.fn_next_char(index1)
          self.insert(index1_next,u" ")
          self.delete(index1)
          self.delete(self.fn_next_char(index1))
           ###終わりのマーク:括弧の次に空白を入れ、空白を消す(マークは空白に移る)###
          index2 = self.index(mark2)	# ずれているので、もう一度探す
          index2_next = self.fn_next_char(index2)
          self.insert(index2_next,u" ")
          self.delete(index2)
           ###入力枠に色をつけ、アンダーラインを引く###
          mark1_index = self.index(mark1)
          mark2_index = self.fn_next_char(self.index(mark2))		# tagの範囲が0:n-1なので、1つ多めにする
          self.tag_add(key, mark1_index , mark2_index )
          if key in self.ifields_ls:	# 入力枠
            self.tag_config(key, foreground="blue" ,underline=1)
          else:			# 出力枠
            self.tag_config(key, foreground="darkgreen" )
          mark1_next = self.fn_next_char(self.index(mark1))
          self.delete(mark1_next,mark2)
    except SyntaxError:		# エラーが見たい場合はこちらを表示
#    except Exception as e:
      pass
       ###以下のprint文を表示させて、「_tkinter.TclError」が出れば、OK。searchで探すものがなくて、終わっている###
#      print 'type:' + str(type(e))
#      print 'args:' + str(e.args)
#      print 'message:' + e.message
    self.write_IOFields()   # Fieldを書き直している
  def cb_delete(self, event):
    u"""delete keyが押されたときに呼びだされる"""
    ans = self.fn_check_field(event)
    if ans == "break":return "break"
    elif (type(ans) == tuple) and (ans[3] == True): 	# 選択範囲があり、入力枠内にある  
      sel_first = ans[0][0]
      sel_last = ans[0][1]
      self.delete(sel_first,sel_last)
    elif len(ans) >= 2:	# 選択範囲はないが、Insertが入力枠内にある 
      self.delete(ans[0])
    self.fn_change_parameter(ans)
    return "break"
  def cb_backspace(self, event):
    u"""バックスペースキーが押されたときに呼びだされる"""
    ans = self.fn_check_field(event)
    if ans == "break":return"break"
    elif (type(ans) == tuple) and (ans[3] == True): 	# 選択範囲があり、入力枠内にある  
      sel_first = ans[0][0]
      sel_last = ans[0][1]
      self.delete(sel_first,sel_last)
    elif len(ans) >= 2:	# 選択範囲はないが、Insertが入力枠内にある 
      # insertは文字の前に付く。BackSpaceは、insertの前の文字を消す
      # insertがField内にあっても、pre_markの1つ後ろの場合、pre_markの文字(Field外)を消してしまうので、その確認をしている
      next_pre_mark = self.fn_next_char(self.index(ans[1]))
      insert =  ans[0]
      if next_pre_mark == ans[0]:return "break"
      event.widget.delete("%s-1c" % insert, insert)
    self.fn_change_parameter(ans)
    return "break"
  def cb_ctrl_c(self, event):
    """Cntr_cなどが押されたときに呼びだされる(が、何もしない)"""
    print dir(self)
    self.copy(event=None)	# コピー
    return "break"
  def cb_ctrl_v(self, event):
    """Cntr_vが押されたときに呼びだされる"""
    ans = self.fn_check_field(event)
    if ans == "break":return "break"
    self.paste(event=None)	# ペースト
    self.fn_change_parameter(ans)
    return "break"
  def cb_ctrl_x(self, event):
    """Cntr_xなどが押されたときに呼びだされる
    """
    ans = self.fn_check_field(event)
    if ans == "break":return "break"
    self.cut(event=None)	# カット
    self.fn_change_parameter(ans) 
    return "break"

  def cut(self, event=None):
    if self.tag_ranges(Tkinter.SEL):
      self.copy()
      self.delete(Tkinter.SEL_FIRST, Tkinter.SEL_LAST)
    
  def copy(self, event=None): 
    if self.tag_ranges(Tkinter.SEL): 
      text = self.get(Tkinter.SEL_FIRST, Tkinter.SEL_LAST)  
      self.clipboard_clear()              
      self.clipboard_append(text)
     
  def paste(self, event=None):
    text = self.selection_get(selection='CLIPBOARD')
    if text:
      self.insert(Tkinter.INSERT, text)
      self.tag_remove(Tkinter.SEL, '1.0', Tkinter.END) 
      self.see(Tkinter.INSERT)

  def cb_other_key(self, event):
    """Keyが押されたときに呼びだされる。入力枠の中であれば表示する
    """
    press_key = event.char
    if len(press_key) == 0:return    # キーの中でもcharでは受け付けられないものもある(例:Sift)この時の文字の長さは0になるため

    try:
       if 0 <= ord(press_key) < 32:return "break"
    except TypeError:pass


#    print press_key,ord(press_key)

    insert = Tkinter.INSERT
    ans = self.fn_check_field(event)
    if ans == "break":return "break"	# 入力枠外
    elif (type(ans) == tuple) and (ans[3] == True): 	# 選択範囲があり、入力枠内にある  
      sel_first = ans[1][0]
      sel_last = ans[1][1]
      self.delete(sel_first,sel_last)
      self.insert(insert,press_key)
    elif len(ans) >= 2:	# 選択範囲はないが、Insertが入力枠内にある 
      self.insert(insert,press_key)  # insert,premark,nextmark,true
    self.fn_change_parameter(ans)
    return "break"
  def cb_popup_menu(self, event):
    u"""ポップアップメニューのcallback"""
    self.menu_top.post(event.x_root,event.y_root)
    pass

  def fn_check_field(self, event):
    u"""入力枠の範囲内かを調べる"""
    ans = self.fn_check_field_sel(event)
    if ans == "break":return "break"	# 入力枠外
    elif ans :return ans	# 選択範囲があり、入力枠内にある 
    elif ans == False:		# 選択範囲がない
      insert =  self.index(Tkinter.INSERT)
      pre_mark = self.mark_previous(insert)
      if pre_mark:
        while (pre_mark == "insert")or(pre_mark == "current")or(pre_mark[:2] == "tk")or(pre_mark[:3] == "out"):
          pre_mark = self.mark_previous(pre_mark)
          if pre_mark == None:break
        if (pre_mark!=None)and(pre_mark[:2]=="s_"):
          next_mark = self.mark_next(pre_mark)
          while (next_mark == "insert")or(next_mark == "current")or(next_mark[:2] == "tk")or(next_mark[:3] == "out"):
            next_mark = self.mark_next(next_mark)
            if next_mark == None:break
          ###cb_deleteの場合###
          if (event.keysym=="Delete")and(next_mark[:2]=="e_")and(self.compare(next_mark,"<=",insert)):return "break"
            ###その他の場合###
          elif self.compare(next_mark,"<",insert):return "break"
        else:return "break"
      else:return "break"
      return (insert,pre_mark,next_mark,False)         
  def fn_check_field_sel(self,event):
    """範囲が選択されている場合に、その範囲が入力枠の中なら"True"を返す
       範囲が選択されていない場合だと、エラーが起こり、except文へ。"False"を返す
       範囲が選択されているが、入力枠の外だと、"break"を返す
      sel_first   :選択範囲の初めの文字のindex
      sel_last    :選択範囲の終わりの文字のindex
    """
    try:
      sel_first = self.index(Tkinter.SEL_FIRST)
      sel_last = self.index(Tkinter.SEL_LAST)
      pre_mark = self.mark_previous(sel_first)
      if pre_mark:
        while (pre_mark == "insert")or(pre_mark == "current")or(pre_mark[:2] == "tk")or(pre_mark[:3] == "out"):
          pre_mark = self.mark_previous(pre_mark)
        if (pre_mark!=None)and(pre_mark[:2]=="s_"):
          next_mark = self.mark_next(pre_mark)
          while (next_mark == "insert")or(next_mark == "current")or(next_mark[:2] == "tk")or(next_mark[:3] == "out"):
            next_mark = self.mark_next(next_mark)
          if self.compare(next_mark,"<",sel_last):return "break"
          else:return ((sel_first,sel_last),pre_mark,next_mark,True)  
        else:return "break"
      else:return "break"
    except:return False
  def get_text(self,form="IFields"):
    u"""IOFieldsinTextからTextを得る
	form = "all"		:テキスト全体を返す
	form = "IFields"	:テキスト中から入力枠の名前と中身の辞書を返す
    """    
    if form == "all":pass
    elif form == "IFields":
      dic = {}
      mark = self.mark_next("0.0")		# 文字"0.0"から次のマークを探す
      while mark:
        while (mark == "insert")or(mark == "current")or(mark[:2] == "tk")or(mark[:3] == "out"):# これらもマークとして判断されるが、引っかかると困る
          mark = self.mark_next(mark)
          if mark == None:break
        if (mark!=None)and(mark[:2]=="s_"):
          start_index = self.index(mark)
          mark = self.mark_next(mark)
          while (mark == "insert")or(mark == "current")or(mark[:2] == "tk")or(mark[:3] == "out"):
            mark = self.mark_next(mark)
            if mark == None:break
          if (mark!=None)and(mark[:2]=="e_"):
            end_index = self.index(mark)
            start_ls = start_index.split(".")
            start_ls[1] = int(start_ls[1])+1
            start_index = "%s.%s" % (start_ls[0],start_ls[1])
            dic["%s" % mark[2:]] = self.get(start_index,end_index)
            mark = self.mark_next(mark)
      return dic

  def write_IOField(self,key):
    u"""出力枠のデータを消して、新しいデータを入れる"""
    ###OFieldsの数の確認###
    try:
       n_max = self.fields_out[key]
    except KeyError:return
    ###parameterの中にあるかの確認###
    parameter = self.parameter
    value = parameter.get(key,None)
    if value == None:
      x = False
      parameter[key] = None 
    else: 
      x = True
     ###出力###
    for n in xrange(n_max):
      mark1 = "out_%s_%s" % (key,n)
      mark2 = "out_%s_%se" % (key,n)
      mark1_next = self.fn_next_char(self.index(mark1))  
      self.delete(mark1_next,mark2)
      if x:
        format = self.fields_format[mark1]
        ###Fields_Typeの確認###
        func = self.fields_type.get(key,None)
        if func != None:
          value = func(value,format,reverse=True)
          if value==None:value=u"formatが適切ではありません。(%s)" % format
          self.insert(mark1_next,value)
        else:
          try:value=format % parameter
          except ValueError:value=u"formatが適切ではありません。(%s)" % format
          self.insert(mark1_next,value)
        if key in self.ifields_ls:# 入力枠
          self.tag_config(key, foreground="blue" ,underline=1)
      else:
        self.insert(mark1_next,self.default_str)
        if key in self.ifields_ls:
          self.tag_config(key, foreground="red" ,underline=1)

  def write_IOFields(self):
    u"""出力枠のデータを消して、新しいデータを入れる
	self.fields_out:出力枠の名前をkeyとしてその枠の数が入った辞書
	key:self.fields_outのkey
	"""
    for key in self.fields_out:
      self.write_IOField(key)
  def get_dict(self):
    x = Key_dict()
    x.ft = self 
    x.data = self.parameter
    return x


  def fn_make_markname_for_OField(self,key):
    u"""出力枠のマークを通し番号にする
     """
#    n=self.output
#    mark = "out_%s_%s" % (key,n)
#    self.output = n+1
    n = self.fields_out.get(key,0)
    mark1 = "out_%s_%s" % (key,n)
    mark2 = "out_%s_%se" % (key,n)
    self.fields_out[key] = n+1
    return mark1,mark2

  def fn_next_char(self,index,n=1):
    u"""次の文字のindexを返す
     最後の文字だったら次の行にいくようにする(もう一つ引数:nを作って選択)
     もう一つ引数を作って、文字を返すようにする
     今後、markだったときにindexに直して使えるようにする
     """
    ls = index.split(".")
    if n==1:ls[1]=str(int(ls[1])+1)
    else:ls[1]=str(int(ls[1])-1)
    next_index = ".".join(ls)
    return next_index

  def fn_change_parameter(self,ans = None):
    u"""入力があった場合に辞書を上書きする【いずれ入力枠に変更があった場合に作り直す】
    key_eventが発生した後、ここに飛ぶ
    """
    if ans == None:		# いずれ消す
        dic = self.get_text()
        for k,x in dic.iteritems():   
          self.parameter[k] = x
    else:
      start_index = self.fn_next_char(self.index(ans[1]))
      end_index = self.index(ans[2])
      s = self.get(start_index,end_index)
      dic = self.fields_type           # 辞書に入れている s2float,s2intは後で表記<---この辞書は外部で作る
      func = dic.get(ans[1][2:],None)
      format = self.fields_format.get(ans[1][2:],None)
      if func != None:                        # 値が入ったとき実数や整数に変換できなければ赤に、変換できれば青にしている
         s = func(s,format)
         if s == None:
            self.tag_config(ans[1][2:], foreground="red" ,underline=1)
         else:
            self.tag_config(ans[1][2:], foreground="blue" ,underline=1)
      self.parameter[ans[1][2:]] = s
  def fn_fields_type(self,x):
    typ = type(x)
    if typ is types.DictType:return x
    elif typ is types.ListType:
      dic={}
      for y in x:dic[y]=None
      return dic

  def fn_make_scrollbar(self,master,**kw):
    """スクロールバーの作成と設定
    command    :self.xview,self.yview	  :スクロールバーの種類
    """
    options = {"command":self.yview}
    sc = Tkinter.Scrollbar(master,options)
    sc.pack(side=Tkinter.RIGHT, fill=Tkinter.Y)
    kw["yscrollcommand"]=sc.set
    return kw
  def fn_make_popup_menu(self):
    u"""ポップアップメニューの作成中"""
    self.menu_top = Tkinter.Menu(self,tearoff=False)                                 # このへん変更してるよ
    self.menu_2nd = Tkinter.Menu(self.menu_top,tearoff=0)

    self.menu_top.add_cascade (label=u'編集(E)', menu=self, underline=3)
    self.menu_top.add_separator()
#    self.menu_top.add_command(label=u'全てを選択(A)', command=self.select_all, underline=6, accelerator = 'Ctrl-A')
    self.menu_top.add_command(label=u'切り取り(X)', command=self.cb_ctrl_x, underline=5, accelerator = 'Ctrl-X')
    self.menu_top.add_command(label=u'コピー(C)', command=self.cb_ctrl_c, underline=4, accelerator = 'Ctrl-C')
    self.menu_top.add_command(label=u'ペースト(V)', command=self.cb_ctrl_v, underline=5, accelerator = 'Ctrl-V')
#    self.menu_top.add_command(label=u'カーソルのある行を削除', command=self.delete_line, accelerator = 'Shift-Del')


def s2float(s,format,reverse=False):
  u"""文字列sを実数に変換する
    出来ない場合はNoneを返す   
  """                        
  try:
    if reverse==False:return float(s) 
    else:return str(s)
  except ValueError:
    return None

def s2int(s,format,reverse=False):   
  u"""文字列sを実数に変換する
    出来ない場合はNoneを返す
  """                       
  try:
    if reverse==False:return int(s) 
    else:return str(s)
  except ValueError:
    return None

class S2xxx():
  def __init__(self, **dic):
    self.dic = dic
  def check(self, x):
    ls = [True]
    for k, v in self.dic.iteritems():
      a = getattr(self, k, None)
      if a != None:
        ls.append(a(x, v))
    if all(ls): return x
    else: return None  
  def check_fast(self, x):
    for k, v in self.dic.iteritems():
      a = getattr(self, k, None)
      if (a != None) and (not a(x, v)):return None   
    return x
  def ge(self, x, v):return x>=v	# x.__ge__(v)           # ex. "b":S2int(ge=1000)
  def gt(self, x, v):return x>v	# x.__gt__(v)
  def le(self, x, v):return x<=v	# x.__le__(v)
  def lt(self, x, v):return x<v	# x.__lt__(v)
  def eq(self, x, v):return x==v 	# x.__eq__(v)
  def ne(self, x, v):return x!=v	# x.__ne__(v)
  def xxx2s(self,format,s):
    lstype=self.dic.get("lstype",None)
    ls=format.split(")")
    ls[0]="%"
    format = "".join(ls)
    try:
      if lstype==None:return format % s
      else:
        s=map(lambda ss: format % ss, s)
        return ",".join(s)
    except ValueError:return None

class S2float(S2xxx):
  def __call__(self,s,format="%s",reverse=False):
    lstype=self.dic.get("lstype",None)
    if reverse==False:
      try:
        if lstype==None:
          x = float(s)
          return self.check_fast(x)
        else:
          s=s.split(",")
          x=map(lambda ss: float(ss), s)
          return map(lambda xx: self.check_fast(xx), x)
      except ValueError:return None
    else:return self.xxx2s(format,s)

class S2int(S2xxx):
  def __call__(self,s,format="%s",reverse=False):
    lstype=self.dic.get("lstype",None)
    if reverse==False:
      try:
        if lstype==None:
          x = int(s)
          return self.check_fast(x)
        else:
          s=s.split(",")
          x=map(lambda ss: int(ss), s)
          return map(lambda xx: self.check_fast(xx), x)
      except ValueError:
        return None
    else:return self.xxx2s(format,s)



class Test(Tkinter.Frame):
  u"""【動作確認用】
	Aくんの身長%(a)scm、Bくんの身長%(b)scm
	[計算結果]
	AくんとBくんの身長の差:%(difference)scm
	AくんとBくんの身長の差:%(difference)scm
	AくんとBくんの身長の平均:%(mean)scm

	入力枠(青)中の数値を変更し、再計算ボタンを押すと、出力枠(緑)に反映されます。
	-----------------
	【入力枠と出力枠の作り方】
	文章中に、%%(key)sと書き込むと、その場所はkeyという名前の枠となります。
	IOFieldsinTextを作成する際の辞書(IFields_name)にkeyを入れておくと、その枠は入力枠となります。
	逆に、keyが辞書に登録されていないと、出力枠となり、出力枠は複数個作ることができます。


	-----------------
	【機能確認用】
	(1)parameterという辞書に値が入っていない、またはNoneが入っている場合、***が入ります。
	・Cくんの身長%(c)scm
	(2)F_typeという辞書でFieldsの条件が設定できます。設定外の入力がされた場合、文字が赤くなります。
	・実数のみ許可:Dくんの身長%(d)scm
	・整数のみ許可:Eくんの身長%(e)scm
	・200cm以下の範囲のみ許可:Fくんの身長%(f)scm
	(3)F_typeという辞書でリストタイプを許可すると、Fields内において","で区切ることで複数の数値を入れることができます。
	・複数入力を許可:残りの人たちの身長%(g)scm
	(4)テキスト中でFieldにフォーマットを指定すると、そのフォーマットで表記されます
	・AくんとBくんの身長の差を8桁の整数表記にして、足りない桁は0で埋める[%%(difference)08d]:%(difference)08dcm
	・AくんとBくんの身長の平均を指数表記[%%(mean)e]:%(mean)ecm
  """
  def __init__(self, master=None):
    u"""windowを作る
    """
     ###値の設定###
    doc=self.__doc__	# ドキュメントを取り出す
    parameter = {"a":160,"b":170,"d":150.5,"e":150,"f":180,"g":[155,165,175]}                                                 
    IF_name=["a","b","c","d","e","f","g"]
    F_type={"c":s2float,"d":s2float,"e":S2int(),"f":S2float(le=200),"g":S2int(lstype=True)}
    ###windowの作成###
    Tkinter.Frame.__init__(self, master)
    self.ft = ft = IOFieldsinText(self,doc,parameter ,IFields_name=IF_name,Fields_type=F_type, wrap=Tkinter.WORD, height=30, )
    self.parameter = ft.get_dict()                                                    
    self.button=button=Tkinter.Button(self,text=u"再計算",command=self.recalculation)	# 再計算ボタン
    button.pack(side=Tkinter.BOTTOM)
    ft.pack(side=Tkinter.TOP, fill=Tkinter.BOTH, expand=True)
    ###計算####
    self.run()
  def run(self):
    u"""計算"""
    parameter = self.parameter
    a = int(parameter["a"])
    b = int(parameter["b"])
    parameter["difference"] = abs(a-b)
    parameter["mean"] = (a+b)/2
    ###出力枠の表示####
#    self.ft.write_IOFields()                                                        
  def recalculation(self):
    u"""再計算ボタンが押された場合"""
    self.run()


if __name__ == '__main__':
    f = Test()
    f.pack(expand=1,fill=Tkinter.BOTH)
    f.mainloop()