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

# pytistics_ex01 : example program

# 0.1.0 (17 Apr 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 ttk
Tkinter=ttk.Tkinter
import tkFont

import numpy as np
import scipy.stats as stats

import matplotlib
import matplotlib.pyplot as pyplot
import matplotlib.patches  as patches
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.widgets import Button
from matplotlib.font_manager import FontProperties
matplotlib.rcParams.update({'font.size': 10})

import sys
import types
import UserDict
import random
import copy



#import IOFieldsinText as IOFT
class Key_dict(UserDict.IterableUserDict):
  u"""IOFieldsinTextの入力枠の中身が変更されると、リアルタイムでその辞書が変更され、
	計算結果を辞書に入れると、出力枠が自動で変更される辞書を作る
	__setitem__	:辞書が変更された場合に呼ばれる。ただし、一部呼ばれない場合がある
			辞書に登録し、IOFieldsinText.write_IOFieldを呼び出してkeyの出力枠を変更する
	__call__	:辞書が関数として呼ばれた場合に、IOFieldsinText.write_IOFieldを呼び出してkeyの出力枠を変更する
	set_ft		:辞書の設定。IOFieldsinTextとdata(辞書)をインスタンス変数として保持する
	"""
  def __setitem__(self, key, value):
    self.data[key] = value
    self.IOFText.write_IOField(key)
  def __call__(self, *keys):
    for key in keys:
      self.IOFText.write_IOField(key)
  def set_ft(self, IOFText, data):
    self.IOFText=IOFText
    self.data=data

class IOFieldsinText(Tkinter.Text):
  def __init__(self,master,text="",IOFText_dict=None,IFields_name=None,Fields_type=None,**kw):
     ###値の設定###
    self.text = text				# text:Tkinter.Textに表示するテキスト(ただし、インスタンス変数としては未使用)
    if IOFText_dict==None:IOFText_dict={}	# IOFText_dict:IOFieldsinText用の辞書。入出力枠のデータを保持している{key:value}
    self.IOFText_dict=IOFText_dict
    if IFields_name==None:IFields_name=[]	# IFields_name:入力枠のkeyが入ったリスト
    self.ifields_ls = IFields_name
    if Fields_type==None:Fields_type={}		# Fields_type:入出力枠の設定が入った辞書{key:S2xxx}
    self.fields_type = self.fn_fields_type(Fields_type)
    self.fields_format = {}			# fields_format:入出力枠のフォーマット(%(key)s)が入った辞書{key:format}
    self.fields_format2 = {}			# fields_format2:fields_formatの予備(未使用)
    self.fields_out = {}				# fields_out:keyごとの出力枠の通し番号の最終値を登録した辞書(出力枠は複数個になる場合があるため)
    self.default_str = "***" 			# default_str:出力枠への値が正しくなかった場合に表示する文字列
    ###Textの作成###
    kw = self.fn_make_scrollbar(master,kw)
    Tkinter.Text.__init__(self, master, **kw)
     ###初期化用関数###
    self.fn_set_text(text)				# IOFieldsinTextのテキストを表示する
    self.fn_convert_to_fields()			# テキストのフォーマット化(%(key)s)された部分を出力枠に変換する
    self.fn_make_Popup_menu()			# ポップアップメニュー(self.menu_top)の設定、追加用
    ###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 fn_make_scrollbar(self,master,kw):
    """スクロールバーの作成と設定"""
    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_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')
#    menu_2nd = Tkinter.Menu(self.menu_top,tearoff=0)	# サブメニューを出す場合の例
#    self.menu_top.add_cascade (label=u'編集(E)', menu=menu_2nd, underline=3)
#    self.menu_top.add_separator()
  def fn_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")
  def fn_convert_to_fields(self):
    u"""テキストのフォーマット化(%(key)s)された部分を出力枠に変換する
	(覚え書き)
	Tkinter.Text.mark_set(name, index)
	name:markの名前(文字列)、index:マークを挿入する場所
	前の文字を増やしたり、消すと、マークも一緒に動く
	マークの場所の文字を消すと、そこにずれて入ってきた文字にマークが移る
	mark_ls'    :  [['name','index'],...]
    """
    index2 = "0.0"
    while True:
      parameter = self.IOFText_dict
      try:
        index1 = self.search("%",index2)
        if index1=="":break
      except:break
      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:
        try:
          index3 = self.search(")",index1)
          index2 = self.search("[diouxXeEfFgGcrs]",index3,regexp=True)
          if (index3=="")or(index2==""):break
        except:break
        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)
        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)
        index2=self.index(mark2)
    self.write_IOFields()   # Fieldを書き直している

   ##################################
   ###  外部から使われる関数  ###
   ##################################
  def changeO2IFields(self,key,fields_type=None):
    u"""出力枠を入力枠に変える
    """
    typ = type(key)
    if typ is types.ListType:key_ls=key
    else:key_ls=[key]
    if fields_type==None:fields_type={}
    typ = type(fields_type)
    if typ is types.DictType:pass
    else:fields_type={key:fields_type}
    for key in key_ls:
       ###出力枠につけたマークからindexを取り出す###
      num = self.fields_out[key]
      if num!=1:pass	# keyに対して出力枠が複数個存在する -> エラーか何かが出るようにする(現在は1つ目の出力枠のみ入力枠にしている)	【問題】
      mark1 = "out_%s_%s" % (key,0)
      mark2 = "out_%s_%se" % (key,0)
      index1 = self.index(mark1)
      index2 = self.index(mark2)
       ###入力枠にマークをつける###
      mark1i = "s_%s" % key
      mark2i = "e_%s" % key
      self.mark_set(mark1i,index1)
      self.mark_set(mark2i,index2)
      ###keyを入力枠のリストに登録する###
      self.ifields_ls.append(key)
      ###fields_typeが指定されていた場合、辞書に登録する###
      f_type = fields_type.get(key,None)
      if f_type!=None:
        self.fields_type[key]=f_type

  def write_IOField(self,key):
    u"""辞書に登録しているkeyの値をIOFieldsinTextに反映する"""
    ###OFieldsの数の確認###
    try:n_max = self.fields_out[key]
    except KeyError:return
    ###self.IOFText_dictの中にあるかの確認###
    parameter = self.IOFText_dict
    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"""辞書の値を全てIOFieldsinTextに反映する
	self.fields_out:出力枠の名前をkeyとしてその枠の数が入った辞書
	"""
    for key in self.fields_out:
      self.write_IOField(key)
  def get_popup_menu(self):
    u"""ポップアップメニューの作成"""
    return self.menu_top
  def get_dict(self):
    key_dict = Key_dict()
    key_dict.set_ft(self, self.IOFText_dict)
    return key_dict

   ###########################
   ###   callback関数   ###
   ###########################
  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

   ###########################
   ###   utility関数     ###(他の関数から二次的に呼ばれる)
   ###########################
  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 fn_make_markname_for_OField(self,key):
    u"""出力枠のマークを通し番号にする
     """
    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が発生した後、ここに飛ぶ
    """
    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.IOFText_dict[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 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():
  func=str
  def __init__(self, **dic):
    self.dic = dic
    lstype=self.dic.get("lstype",None)
    if lstype=="tplinls":self.__call__=self.call_tplinls
    elif lstype==True:self.__call__=self.call_list
  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 __call__(self,s,format="%s",reverse=False):
    if reverse==False:
      try:
        x = self.func(s)
        return self.check_fast(x)
      except ValueError:return None
    else:
      format = self.key_delete_in_format(format)
      try:return format % s
      except ValueError:return None
  def call_list(self,s,format="%s",reverse=False):
    if reverse==False:
      s=s.split(",")
      try:
        x=map(lambda ss: self.func(ss), s)
        return map(lambda xx: self.check_fast(xx), x)
      except ValueError:return None
    else:
      format = self.key_delete_in_format(format)
      try:
        s=map(lambda ss: format % ss, s)
        return ",".join(s)
      except ValueError:return None
  def call_tplinls(self,s,format="%s",reverse=False):
    if reverse==False:
      s1=s.split(",")
      s3=map(lambda s2: tuple(s2.split(":")), s1)
      try:
        x1=map(lambda s4: map(lambda s5: self.func(s5), s4), s3)
        return map(lambda x2: map(lambda x3: self.check_fast(x3), x2), x1)
      except ValueError:return None
    else:
      format = self.key_delete_in_format(format)
      try:
        s=map(lambda s1: map(lambda s2: format % s2, s1), s)
        s=map(lambda s1: ":".join(s1), s)
        return ",".join(s)
      except ValueError:return None
  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 key_delete_in_format(self,format):
    ls=format.split(")")
    ls[0]="%"
    format = "".join(ls)
    return format
class S2float(S2xxx):
  func=float
class S2int(S2xxx):
  func=int

#import pytistics_base
class StatsNotebook(ttk.Notebook):
  def __init__(self,master=None,common_dict=None,title=""):
    u"""外側のNotebookの作成と、共通の辞書の作成
	statsnotepages	:各StatsNotepage(各ページ)が入ったリスト
	common_dict		:各ページで共通の要素を入れる辞書
	common_dict["count"] :1ページ目の変更回数
	"""
     ###共通の辞書の作成###
    if common_dict==None:common_dict={}
    self.common_dict=common_dict
    if "count" not in self.common_dict:self.common_dict["count"]=0
    ###ttk.Notebookの作成###
    ttk.Notebook.__init__(self,master,width=640, height=600)
    if title!="":self.master.title(title)
    self.pack(side=Tkinter.TOP,fill=Tkinter.BOTH,expand=1)
    self.bind("<<NotebookTabChanged>>",self.cb_TabChanged)
    self.statsnotepages=[]
     ###フォントの設定###
    if sys.platform == 'win32':
      fontproperties = FontProperties(fname=r'C:\WINDOWS\Fonts\MSGOTHIC.TTC')                          # HG 明朝系
    else:
      fontproperties = FontProperties(fname=r'/usr/share/fonts/truetype/sazanami/sazanami-mincho.ttf') # HG 明朝系
    self.common_dict["fontproperties"]=fontproperties
    self.common_dict["font"]=tkFont.Font(family="Courier", size=9)
  def cb_TabChanged(self,event):
    u"""タブが切り替えられた場合のコールバック"""
    index=self.index("current")
    self.statsnotepages[index].cb_TabChanged(event)
  def fn_make_Page(self,name,statsnotepage):
    u"""StatsNotepageをこのNotebookに作る"""
    self.add(statsnotepage, text=name, padding=1)
    self.statsnotepages.append(statsnotepage)
  def main(self):
    self.mainloop()

class StatsNotepage(Tkinter.Frame):
  def __init__(self,master=None,name="",local_dict=None):
    u"""内側のNotebookの作成と、このページ独自の辞書の作成
    """
    Tkinter.Frame.__init__(self,master)
    master.fn_make_Page(name,self)
    ###共通データ###
    self.common_dict=getattr(master,"common_dict",{"count":0})
    ###個々のデータ###
    if local_dict==None:local_dict={}
    self.local_dict=local_dict
    if "count" not in self.local_dict:self.local_dict["count"]=-1
    self.local_dict["name"]=name		# ページの名前
    self.figure_dict={}			# 図の辞書
    self.canvas_dict = {}  		# キャンバスの辞書(グラフ関係)
    ###IOFieldsinText用の辞書###
    self.IOFields_dict={}
    ###Widgetの作成###
    self.fn_make_Widget()
    self.fn_make_Popup_menu()
     ###各ページ毎の設定###
    self.init_IOFields()
  def fn_make_Widget(self):
    u"""Widgetの作成
	figure用のNotebookと、ボタン、IOFeildsinTextの作成
    """
    ###PanedWindow(ページ全体に入れる)###
    pane=ttk.PanedWindow(self,orient=Tkinter.VERTICAL)  # "orient"並べ方:VERTICAL:垂直、HORIZONTAL:平行
    pane.pack(side=Tkinter.TOP,fill=Tkinter.BOTH,expand=1,padx=0,pady=0)
    ###notebook(各ページに入れる図のためのNotebook)###
    notebook=ttk.Notebook(pane,height=400)
    pane.add(notebook)
    ###sub_frame(ボタン、IOFieldinTextをまとめている)###
    sub_frame=Tkinter.Frame(pane) 
    pane.add(sub_frame)
    ###buttonの設定###
    button_frame=Tkinter.Frame(sub_frame,height=1)	# 真ん中のボタンが入るフレーム
    button_frame.pack(side=Tkinter.TOP,fill=Tkinter.X,expand=0,padx=0,pady=0)
    ###IOFieldsinTextの設定###
    text_frame=Tkinter.Frame(sub_frame)
    text_frame.pack(side=Tkinter.TOP,fill=Tkinter.BOTH,expand=1,padx=0,pady=0)
    doc=self.__doc__	# ドキュメントストリング
    ftext=IOFieldsinText(text_frame,doc,self.IOFields_dict , wrap=Tkinter.WORD, height=10 ) 
    ftext.pack(side=Tkinter.TOP,fill=Tkinter.BOTH,expand=1,padx=0,pady=0)
     ######
    self.notebook=notebook
    self.fn_set_buttons(button_frame)
    self.FText=ftext
    self.IOFields_dict = ftext.get_dict()                                                    
     ###フォントの設定###
    self.local_dict["fontproperties"]=self.common_dict["fontproperties"]
    self.local_dict["font"]=self.common_dict["font"]
  def init_IOFields(self):
    u"""継承先で設定"""
    pass
  def recalc(self):
    u"""継承先で設定"""
    pass
  def cb_TabChanged(self,event=None):
    u"""タブが選択された場合のコールバック
	StatsNotebook.cb_TabChangedから呼ばれる
    """
    count = self.common_dict["count"]
    mycount = self.local_dict["count"]
    if mycount < count:
      self.calculate()
      self.draw_figure()
      self.local_dict["count"] = count
  def common_dict_update(self):
    count = self.common_dict["count"]
    self.common_dict["count"] = self.local_dict["count"] = count+1
  def get_text(self):
    u"""IOFieldsinTextから文字列取得"""
    return self.FText.get('1.0',Tkinter.END)
  def add_text(self,x):
    u"IOFieldsinTextの文章を全て削除"
    self.FText.delete('1.0', Tkinter.END)
  def clear_text(self,x):
    u"IOFieldsinTextの最後の位置へ文字列入力"
    self.FText.insert(Tkinter.END,x)
  def set_text(self,text):
    u"""IOFieldsinTextへ文字列入力。ただし、表示されている文字列を全て削除してから"""
    self.clear_text(text)
    self.add_text(text)
  def get_ax(self,name,pos=""):
    if (name,"ax") in self.figure_dict:ax = self.figure_dict[(name,"ax")]
    else:
      fig = self.figure_dict[(name,"fig")]
      ax=fig.add_subplot(*pos)
      ax.grid(True)
      self.figure_dict[(name,"ax")] = ax
    return ax

  def fn_make_Popup_menu(self):
    u"""右クリックメニューの作成
        "cb_popup_"を名前に含む関数は、右クリックメニューになる
	"""
    ls1 = []
    for i in dir(self):
      if i.find("cb_popup_")==0:			# Cbで始まっているもの(関数に限定した方が良い)
        f=getattr(self,i)				# 関数オブジェクトを取得し,押された時の実行関数とする
        ss=f.__doc__				# 関数のドキュメント
        ss=ss.split("\n")				# 改行で分割し、リストに
        ss=[ii.strip("\t") for ii in ss]		# 両側タブは除去して
        pos=ss[0].split(",")			# 1行目は順番:カンマで分割し、
        pos=[int(ii)for ii in pos]		# 整数にして
        label = ss[1]
        ls1.append((label,f,pos))
    ls1.sort()
    ###右クリックメニュー作成###
    menu=self.FText.get_popup_menu()
    for (label,f,pos) in ls1:
      menu.add_command(label=label  ,command=f )
    ###サブメニュー拡張用###
#      sub_menu=Tkinter.Menu(menu, tearoff=0)
#      sub_menu.add_command(label=u"書き込み"	,command=self.cb_Store_text)
#      menu.add_cascade(label="Option", menu=sub_menu)
  def fn_get_fig_pos(self,f):
    ss=f.__doc__				# 関数のドキュメント
    ss=ss.split("\n")			# 改行で分割し、リストに
    ss=[ii.strip("\t") for ii in ss]	# 両側タブは除去して
    ss=[ii.strip(" ") for ii in ss] 	# 空白文字は除去して
    pos=ss[0].split(",")			# 1行目はボタン座標:カンマで分割し、
    pos=[int(ii)for ii in pos]		# 整数にして
    pos[2]=pos[1]*pos[2]+pos[3]+1		#   縦分割数、横分割数、縦座標、横座標として計算して
    pos=tuple(pos[:3])			#   (縦分割数、横分割数、番地) を計算する
    return pos
  def draw_figure(self):
    u"""グラフの作成。軸は作らない
        "Fig"を名前に含む関数は、グラフになる。(例)Fig2_1
        クラスから探し、ドキュメントを取り出す。ドキュメントは軸の情報を持ち、1行目は軸の座標、2行目は"なし"となる。
        関数名の"_"の前はグラフの名前となり、sub_draw_figureに渡して、グラフを作る
        関数を呼び出し、情報を渡すと、そこで軸や線が作られる。
    """
    ls1 = []
    for i in dir(self):
      if i.find("Fig")==0:			# Figで始まっているもの(関数に限定した方が良い)
        f=getattr(self,i)			# 関数オブジェクトを取得し,押された時の実行関数とする
        pos = self.fn_get_fig_pos(f)
        tab_name = i.split("_")[0]       	# "_" の前はタブラベル、後は軸の番号
         ###グラフの作製###
        if tab_name in self.figure_dict: fig = self.figure_dict[tab_name]
        else:
          frame=Tkinter.Frame(self.notebook)
          self.notebook.add(frame, text=tab_name, padding=1)
          fig=Figure(figsize=(6,4), dpi=80)
          fig.set_facecolor('w')
          if title:fig.suptitle(title)
          canvas = FigureCanvasTkAgg(fig, master=frame)
          toolbar=NavigationToolbar2TkAgg( canvas, frame )
          canvas.get_tk_widget().pack(side=Tkinter.TOP, fill=Tkinter.BOTH, expand=1,padx=1,pady=1)
          #toolbar.update()
         ###グラフの登録###
          self.canvas_dict[tab_name] = canvas
          self.figure_dict[tab_name]=fig
        self.figure_dict[(i,"fig")] = fig
        self.figure_dict[(i,"tab_name")] = tab_name
        ls1.append((i,f,pos,tab_name))
#    ls1.sort()
    ###関数呼び出し(軸を作る等)###
    for (i,f,pos,tab_name) in ls1:
        f(i,pos)
        self.canvas_dict[tab_name].draw_idle()
  def fn_set_buttons(self,buttonframe):
    u"""ボタンの作成。
        "bt_"を名前に含む関数は、ボタンになる。クラスから探し、ドキュメントを取り出す。
    """
    for i in dir(self):
      if i.find("bt_")==0:			# bt_で始まっているもの(関数に限定した方が良い)を取り出す
        f=getattr(self,i)			# 関数オブジェクトを取得し,押された時の実行関数とする
        ss=f.__doc__				# 関数のドキュメント
        ss=ss.split("\n")			# 改行で分割し、リストに
        bt_name=ss[0].strip("\t")		# 両側タブは除去して
        bt_name=bt_name.strip(" ")	 	# 空白文字は除去して
        button = Tkinter.Button(buttonframe,text=bt_name,command=f)
        button.pack(side=Tkinter.LEFT)

class StatsPage01(StatsNotepage):
  u"""このプログラムは、練習問題と解答という形式を利用して、統計についての処理を行います。
	文字が青色になっている箇所は入力枠で、緑色の出力枠に計算結果が表示されます。
	入力枠を書き換え、再計算ボタンを押すと、出力枠に反映され、グラフが再描画されます。

	このページは、度数図を作成を目的とした処理を行います。


	【問題】
	1号機と2号機で同一製品を加工している。これらの製品は機械別に分けられて、それぞれの箱に入れられる。
	各箱の中から各100個ずつ抜き取って検査した結果は次の通りであった。
	(1)No.順にグラフに表せ
	(2)全体の度数図を描け
	(3)1号機、2号機のそれぞれの度数図を描け

	データ 
	※機械の種類(1号機or2号機)と、不良個数を1組として50箱分のデータを示している
	 機械の種類と不良個数を":"で区切り、それぞれの箱ごとに","で区切っている
	※機械の種類は必ずしも交互ではない
	%(data1)s


	【解答】
	(1)Fig.1にNo.順のグラフを示す
	 横軸がNo.、縦軸が不良個数となっている。
	(2)Fig.2に全体の度数図を示す
	 機械の種類に関係なく、すべての箱の不良個数の度数図となっている。
	(3)Fig.3に1号機、Fig.4に2号機の度数図をそれぞれ示す
 """
  def init_IOFields(self):
    u"""このページ独自の初期設定
    """
    ###FieldsTextの設定###
    IFields_name=["data1"]
    Fields_type={"data1":S2int(lstype="tplinls")}
    self.FText.changeO2IFields(IFields_name,Fields_type)
     ###計算用の設定###
    dic=self.IOFields_dict
    dic["bo_mu"]=bo_mu=3
    dic["bo_sigma"]=bo_sigma=5
    dic["bo_num"]=bo_num=50
    dic["bo_data"] = data = np.random.randint(0, 8, bo_num)
    machine_ls=[1,2]
    ls = []
    for i in range(bo_num):
      machine=random.choice(machine_ls)
      ls.append((machine,data[i]))
    self.IOFields_dict["data1"]=ls

  def bt_1_recalc(self,random=False):
    u"""再計算
	入力されたデータの値を使って再計算し、グラフを書き直す
	"""
    self.calculate()
    self.draw_figure()
  def calculate(self,random=False):
    u"""データを初期化する。ただし、テキスト中の定数はされない。また、変更があれば、反映される。
    【平均や標準偏差をテキストとして持っているのを実数に変えている。->実数に変えれないものがあったとき、なにか表示させるようにする】
        parameter     :  必要な定数が入った辞書
    """
    dic = self.IOFields_dict
    ls2 = dic["hist_all"]=[]
    ls3 = dic["hist_1"]=[]
    ls4 = dic["hist_2"]=[]
    num=0
    ls = dic.get("data1",None)
    for tpl in ls:
      ls2.append(tpl[1])
      if tpl[0]==1:ls3.append(tpl[1])
      else:ls4.append(tpl[1])
      num=num+1
    dic["num"]=num
  def sub_fig(self,ax,ls):
    num_ls=[0,0,0,0,0,0,0,0,0,0,0]
    for x in ls:
      num_ls[x]=num_ls[x]+1
    x = np.arange(11)
    y = tuple(num_ls)
    bars = ax.bar(x, y, width=1, color='g',  alpha=0.3)
    ax.set_xticks(x + 0.5)	# x軸のラベルを棒グラフの中心におく
    ax.set_xticklabels(("0","1","2","3","4","5","6","7","8","9","10"))	# x軸のラベルを変える
    for bar in bars:
      height = bar.get_height()
      ax.text(bar.get_x()+bar.get_width()/2., 1.05*height, '%d'%int(height),ha='center', va='bottom')
    num_ls.sort()
    ax.set_ylim(0,num_ls[-1]+1)
  def Fig1_1(self,name,pos="",dic=None):    # 自分の関数名、axisの位置(ポジション)
    u"""1,1,0,0
	No.順にグラフに表す
	横軸がNo.、縦軸が不良個数
    """
    ###軸の作製・取得###
    ax = self.get_ax(name,pos)
    ax.clear()
    ax.set_autoscalex_on(True)
    ###グラフ###
    dic = self.IOFields_dict
    x = np.arange(dic["num"])
    y = tuple(dic["hist_all"])
    ax.bar(x, y, width=1, color='g',  alpha=0.3)
  def Fig2_1(self,name,pos="",dic=None):    # 自分の関数名、axisの位置(ポジション)
    u"""1,1,0,0
    全体の度数図
    """
    ###軸の作製・取得###
    ax = self.get_ax(name,pos)
    ax.clear()
    ax.set_autoscalex_on(True)
    ###度数図###
    dic = self.IOFields_dict
    ls = dic["hist_all"]
    self.sub_fig(ax,ls)

  def Fig3_1(self,name,pos="",dic=None):    # 自分の関数名、axisの位置(ポジション)
    u"""1,1,0,0
    1号機の度数図
    """
    ###軸の作製・取得###
    ax = self.get_ax(name,pos)
    ax.clear()
    ax.set_autoscalex_on(True)
    ###度数図###
    dic = self.IOFields_dict
    ls = dic["hist_1"]
    self.sub_fig(ax,ls)
  def Fig4_1(self,name,pos="",dic=None):    # 自分の関数名、axisの位置(ポジション)
    u"""1,1,0,0
    2号機の度数図
    """
    ###軸の作製・取得###
    ax = self.get_ax(name,pos)
    ax.clear()
    ax.set_autoscalex_on(True)
    ###度数図###
    dic = self.IOFields_dict
    ls = dic["hist_2"]
    self.sub_fig(ax,ls)
class StatsPage02(StatsNotepage):
  u"""このページは、パレード図の作成を目的とした処理を行います。

	【問題】
	品質管理セミナー教室の参加者%(num)s人を業種別に分類したところ次のようになった。
	このデータをもとにしてパレード図を作成せよ。

	%(01_key)s%(01_value)s人、%(02_key)s%(02_value)s人、%(03_key)s%(03_value)s人、%(04_key)s%(04_value)s人、
	%(05_key)s%(05_value)s人、%(06_key)s%(06_value)s人、%(07_key)s%(07_value)s人、%(08_key)s%(08_value)s人、
	%(09_key)s%(09_value)s人、%(10_key)s%(10_value)s人、%(11_key)s%(11_value)s人、%(12_key)s%(12_value)s人、
	%(13_key)s%(13_value)s人、%(14_key)s%(14_value)s人、%(15_key)s%(15_value)s人


	【解答】
	Fig.1にパレード図を示す。
	
 """
  def init_IOFields(self):
    u"""タブが選択されたときに呼びだされる。
    """
     ###その他の設定###
    IFields_name=["01_key","02_key","03_key","04_key","05_key","06_key","07_key","08_key",
			"09_key","10_key","11_key","12_key","13_key","14_key","15_key",
			"01_value","02_value","03_value","04_value","05_value","06_value","07_value","08_value",
			"09_value","10_value","11_value","12_value","13_value","14_value","15_value"]
    Fields_type={	"01_value":S2int(),"02_value":S2int(),"03_value":S2int(),"04_value":S2int(),
			"05_value":S2int(),"06_value":S2int(),"07_value":S2int(),"08_value":S2int(),
			"09_value":S2int(),"10_value":S2int(),"11_value":S2int(),"12_value":S2int(),
			"13_value":S2int(),"14_value":S2int(),"15_value":S2int()}
    self.FText.changeO2IFields(IFields_name,Fields_type)
    data_ls=[	["01_key",u"電気機器"],["02_key",u"一般機械"],["03_key",u"鉄鋼"],["04_key",u"非鉄金属"],["05_key",u"金属製品"],["06_key",u"輸送機器"],
			["07_key",u"精密機器"],["08_key",u"化学"],["09_key",u"医薬品"],["10_key",u"石油製品"],["11_key",u"ゴム製品"],["12_key",u"窯業・土石"],
			["13_key",u"土木・建築"],["14_key",u"官公庁"],["15_key",u"団体"],
			["01_value",31],["02_value",7],["03_value",1],["04_value",1],["05_value",11],["06_value",10],["07_value",10],["08_value",6],
			["09_value",4],["10_value",2],["11_value",3],["12_value",5],["13_value",1],["14_value",4],["15_value",2]]
    for ls in data_ls:
        key=ls[0]
        self.IOFields_dict[key]=ls[1]
    self.IOFields_dict["IFields_name"]=IFields_name
  def calculate(self,random=False):
    u"""データを初期化する。ただし、テキスト中の定数はされない。また、変更があれば、反映される。
    【平均や標準偏差をテキストとして持っているのを実数に変えている。->実数に変えれないものがあったとき、なにか表示させるようにする】
        parameter     :  必要な定数が入った辞書
    """
    dic = self.IOFields_dict
    num=0
    ls=[]
    for key in dic["IFields_name"]: 
      keys=key.split("_")
      if (len(keys)==2)and(keys[1]=="key"):
        s="_".join([keys[0],"value"])
        value=dic[s]
        kind=dic[key]
        ls.append([value,kind])
        num=num+value
    dic["num"]=num
    ls.sort(reverse=True)
    key_ls=dic["key_ls"]=[]
    value_ls=dic["value_ls"]=[]
    for x in ls: 
      key_ls.append(x[1])
      value_ls.append(x[0])
  def bt_1_recalc(self,random=False):
    u"""再計算
	入力されたデータの値を使って再計算し、グラフを書き直す"""
    self.calculate(random)
    self.draw_figure()
  def Fig1_1(self,name,pos="",dic=None):    # 自分の関数名、axisの位置(ポジション)
    u"""1,1,0,0
	パレード図を表示する
	横軸は種類、縦軸は人数と累積百分率(%)
    """
     ###軸の作製・取得###
    ax = self.get_ax(name,pos)
    ax.clear()
    ax.set_autoscalex_on(True)
    fontproperties=self.local_dict["fontproperties"]
     ###棒グラフの作成###
    dic=self.IOFields_dict
    key_ls = dic["key_ls"]
    x = np.arange(len(key_ls))
    y = tuple(dic["value_ls"])
    bar = ax.bar(x, y, 1, color='g', alpha=0.3)
    ax.set_ylabel(u'人数', fontproperties=fontproperties)
     ###折れ線グラフの作成###
    ax2 = ax.twinx()
    ax2.clear()
    xx = np.arange(len(key_ls)+1)
    yy = [0]
    ls = [0]
    for v in range(len(y)):
      ls.append(ls[v]+y[v])
      i=(float(ls[v]+y[v])/float(dic["num"]))*100
      yy.append(i)
    plot = ax2.plot(xx, yy, "o-")
    ax2.set_ylabel(u'累積百分率(%)', fontproperties=fontproperties)
    ax.set_xticks(x + 0.5)
    ax.set_xticklabels( tuple(key_ls), fontproperties=fontproperties )
    ax.set_xlim(0,len(key_ls))


if __name__=="__main__":
  common_dict = {}
  title="pytistics example 01"
  rn=StatsNotebook(common_dict=common_dict,title=title)
  local_dict01={}
  local_dict02={}
  StatsPage01(rn,"Page1",local_dict=local_dict01)
  StatsPage02(rn,"Page2",local_dict=local_dict02)
  rn.main()