{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "760ff5e1-d1fd-4ac0-af62-2af18738d476",
   "metadata": {},
   "source": [
    "# Python 入門講座　第9/10回:  仰せのままに(as you wish)。\n",
    "\n",
    "[Archiver Appliance WEB]: http://www-cont.j-parc.jp/archive/ArchiveDataClient-ap.php"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b9526377-a9af-4b61-a87a-6767abe8cad3",
   "metadata": {},
   "source": [
    "## 本日の目標\n",
    "\n",
    "前回の.pltファイルからグラフを作成するプログラムに下図のようなユーザーインタフェースを作成する。\n",
    "\n",
    " 1. ファイルダイアログを使って入力となる.pltファイルを選択\n",
    " 1. グラフ作成ボタンをおしてグラフを表示\n",
    " 1. 作成されたグラフをファイルに保存する。\n",
    " \n",
    " \n",
    "[Tcl/Tk widget samples]: http://pages.cpsc.ucalgary.ca/~saul/personal/archives/Tcl-Tk_stuff/tcl_examples/"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "48bf43d5-24b6-4dcc-8a09-a714565684b4",
   "metadata": {},
   "source": [
    "\n",
    "![作成するGUI window](./_images/GUI_outline.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "517fb5c8-aa34-460a-9cc9-45b52d2f7928",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2022-02-09 17:12:52.305 Python[29590:3167700] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to /var/folders/3h/lqzhtkt17vv47gbtv470wcp40000gn/T/org.python.python.savedState\n"
     ]
    }
   ],
   "source": [
    "!python3 Plt2PngTk.py"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7432b08f-04f5-4676-849f-6df4c6bf4dc9",
   "metadata": {},
   "source": [
    "## 本日の講座の概要\n",
    "\n",
    " 1. PythonでGUIアプリケーションをつくるには？\n",
    " 1. tkinterとは\n",
    "    1. 簡単な例\n",
    "    1. tkinterの構成\n",
    "    1. tkinter/ttkのウィジェット\n",
    " 1. `class` を使いこなす。\n",
    " 1. matplotlib をtkinterに埋め込む"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e06319d3-b614-4697-80cc-4486e8bdf93c",
   "metadata": {},
   "source": [
    "## PythonでGUIアプリケーションをつくるには\n",
    "\n",
    "GUIアプリケーションを作成するのには様々な方法がありますが、\n",
    "複数のプラットフォーム(Linux, Windows,Macos)で\n",
    "（ほぼ）同じように動作するGUIアプリケーションが作成できるプログラムライブラリ(フレームワーク）が\n",
    "存在します。よく知られたフレームには次のようなものがあります。\n",
    "\n",
    "    Tcl/Tk\n",
    "    GTK - The GIMP Toolkit\n",
    "    Qt　- 公式には「キュート」と呼ぶ。\n",
    "    wxWidgets\n",
    "    FLTK (軽いが機能が限定される(らしい)。使ったことないです。すいません。）\n",
    "\n",
    "これらのフレームワークはいずれも`python`から利用するためのモジュールが提供されています。\n",
    "`Tcl/Tk`を利用するための python モジュール　`tkinter` は pythonの**標準**配布パッケージの中に含まれていることから、よく使われています。(ドキュメントについては、公式ページでも”標準ドキュメントが頼りないものだとしても、代わりとなる、リファレンス、チュートリアル、書籍その他が入手可能です。”と述べています。）\n",
    "\n",
    "[python3 document tkinter]: https://docs.python.org/ja/3/library/tkinter.html\n",
    "\n",
    "[Tkinter 8.5 reference]: https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/index.html\n",
    "\n",
    "なお、J-PARC制御計算機では Python2/tkinter, wxPython, gtk　が利用可能です。\n",
    "\n",
    "[PAGE tutorial]: https://www.youtube.com/watch?v=oULe0h0Jl3g\n",
    "\n",
    "### Tkinterとtkinter\n",
    "\n",
    "Tkウィジェットのためのモジュールは、python2では`Tkinter`と呼ばれていました。python3への移行にさいして、モジュールの構成などが見直され、\n",
    "Tkのためのモジュールは`ttkinter`となりました。その後、`tkinter`はpython2にもバックポートされました(ただし別途にインストールが必要です）。\n",
    "\n",
    "つまり、`tkinter`を使うようにすれば、python3/python2の両方で動作するPythonプログラムを作れるということです。\n",
    "\n",
    "#### GUI builderについて\n",
    "\n",
    "Tkの画面／窓を画面上で設計するためのツールもいくつか存在するようです。[PAGE][PAGE home]はそのようなツールの一つです([GUI作成教育ビデオ][Tkinter Course - Create Graphic User Interfaces in Python Tutorial])。Python3で動作します。公式にサポートされているのは、LinuxとWindowsですが、macosでも動作するようです。\n",
    "\n",
    "[PAGE home]:http://page.sourceforge.net/#Short_Description\n",
    "\n",
    "[Tkinter Course - Create Graphic User Interfaces in Python Tutorial]: https://www.youtube.com/watch?v=YXPyB4XeYLA"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cf23fe80-3d8b-4181-a8c2-b52e83cceb39",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "追記：PySimpleGUIというTk,Qt,Gtk,Wxなどを使ったユーザインタフェースを簡単に使うためのモジュールも出てきています。単純なアプリケーション作成には便利そうですが、規模が大きくなってきた時に耐えられるのか？という心配があります。また、標準ライブラリではないため、継続性が心配です。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d78a7702-923b-4a5d-bf04-5097bde83309",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "#### Tcl/Tk：名前の由来\n",
    "Wikipediaからの引用\n",
    "```\n",
    "なお、Tcl 言語の名前は「ツールコマンド言語」を意味する英語「tool command language」に由来し、Tk の名前は「ツールキット」を意味する英語「toolkit」に由来する。\n",
    "```\n",
    "\n",
    "#### ウィジェット(widget)\n",
    "\n",
    "\"The Quick Python Book\", Daryl Harms & Kenneth McDonald,2000, Mannig, USA.には、\n",
    "  \"The first basic idea behind Tk is the concept of a widget, which is short for *window gadget*.\n",
    "  \n",
    "と記載されています。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "949fb209-dbbd-4959-938e-47ebea711653",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## tkinterとは\n",
    "### ”Hello World”： グラフィカルユーザーインタフェース版\n",
    "\n",
    "まずは、画面に表示されたボタンを押すと、端末に \"Hello World\"と表示するプログラムをtkinterを使って作ってみました。\n",
    "\n",
    "![画面](./_images/HelloWorldGUI.png)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "612007f3-63eb-460a-b483-b03d36bd5151",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2022-02-09 17:12:56.533 Python[29641:3167876] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to /var/folders/3h/lqzhtkt17vv47gbtv470wcp40000gn/T/org.python.python.savedState\n"
     ]
    }
   ],
   "source": [
    "!python3 HelloWorldTk.py"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21c27f58-acbf-44df-86fa-1c5942cd3630",
   "metadata": {},
   "source": [
    "この画面 は tkinterの三つの部品(ウィジェット）からできています。　二つのボタンとそれらが収められている窓(tk)です。\n",
    " \n",
    " ![widgets in Hello world GUI](./_images/HelloWorldGUI-design.png)\n",
    " \n",
    "上のボタン(Push Me!)を押すと、何かが起こります。下のボタン(Quit)を押すと、プログラムは終了します。\n",
    " \n",
    "実際のプログラムをみてみましょう。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "1d7fead0-7b1c-4b10-a023-b8d6d8078d48",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting HelloWorldTk.py\n"
     ]
    }
   ],
   "source": [
    "%%file HelloWorldTk.py\n",
    "#!python3\n",
    "#-*- coding:utf-8 -*-\n",
    "import tkinter, tkinter.ttk as ttk # tkinterモジュール、ttkモジュール\n",
    "from tkinter.ttk import Button # ttkのButtonウィジェットを使います。\n",
    "from tkinter.constants import *\n",
    "\n",
    "def hello(root):\n",
    "    root.title(\"Hello World in Tk\") #窓に名前をつけます。\n",
    "    # ボタンの部品を作ります\n",
    "    # btn =Button(root, text=\"Push me!\", command=print_hello)\n",
    "    msg=\"Hello World!\"\n",
    "    btn =Button(root, text=\"Push me!\", command=lambda msg=msg:print(msg))\n",
    "    qbtn=Button(root, text=\"Quit\",     command=root.quit)\n",
    "    # 部品を配置します。\n",
    "    btn.grid(row=0,  column=0)\n",
    "    qbtn.grid(row=1, column=0)\n",
    "\n",
    "def print_hello():\n",
    "    \"\"\"\n",
    "    btnが押された時に、実行されます(callback関数）。\n",
    "    端末に\"Hello World.\"と印刷します。\n",
    "    \"\"\"\n",
    "    print(\"Hello\",\"World.\")\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    tk=tkinter.Tk() # 部品を収める窓を作ります。\n",
    "    hello(tk) # 窓の中に部品を作ります。\n",
    "    tk.mainloop() # Event loopを起動します。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "1192c6dd-b8f2-40a8-892f-61db0b2a5480",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2022-03-13 08:10:29.518 Python[6588:2184215] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to /var/folders/3h/lqzhtkt17vv47gbtv470wcp40000gn/T/org.python.python.savedState\n"
     ]
    }
   ],
   "source": [
    "!python3 HelloWorldTk.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "c63501c1-9027-4ced-87b0-300d1a460521",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\u001b[0;31m#!python3\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m\u001b[0;31m#-*- coding:utf-8 -*-\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m\u001b[0;32mimport\u001b[0m \u001b[0mtkinter\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtkinter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mttk\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mttk\u001b[0m \u001b[0;31m# tkinterモジュール、ttkモジュール\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m\u001b[0;32mfrom\u001b[0m \u001b[0mtkinter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mttk\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mButton\u001b[0m \u001b[0;31m# ttkのButtonウィジェットを使います。\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m\u001b[0;32mfrom\u001b[0m \u001b[0mtkinter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconstants\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m\u001b[0;32mdef\u001b[0m \u001b[0mhello\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mroot\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0mroot\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtitle\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Hello World in Tk\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m#窓に名前をつけます。\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0;31m# ボタンの部品を作ります\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0;31m# btn =Button(root, text=\"Push me!\", command=print_hello)\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0mmsg\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"Hello World!\"\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0mbtn\u001b[0m \u001b[0;34m=\u001b[0m\u001b[0mButton\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mroot\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtext\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"Push me!\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcommand\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mlambda\u001b[0m \u001b[0mmsg\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmsg\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmsg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0mqbtn\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mButton\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mroot\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtext\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"Quit\"\u001b[0m\u001b[0;34m,\u001b[0m     \u001b[0mcommand\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mroot\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0;31m# 部品を配置します。\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0mbtn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrow\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m  \u001b[0mcolumn\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0mqbtn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrow\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcolumn\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m\u001b[0;32mdef\u001b[0m \u001b[0mprint_hello\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0;34m\"\"\"\u001b[0m\n",
       "\u001b[0;34m    btnが押された時に、実行されます(callback関数）。\u001b[0m\n",
       "\u001b[0;34m    端末に\"Hello World.\"と印刷します。\u001b[0m\n",
       "\u001b[0;34m    \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Hello\"\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\"World.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m\u001b[0;32mif\u001b[0m \u001b[0m__name__\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m\"__main__\"\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0mtk\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtkinter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mTk\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# 部品を収める窓を作ります。\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0mhello\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtk\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# 窓の中に部品を作ります。\u001b[0m\u001b[0;34m\u001b[0m\n",
       "\u001b[0;34m\u001b[0m    \u001b[0mtk\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmainloop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# Event loopを起動します。\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%pycat HelloWorldTk.py"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1dcf6f0-b7ab-4240-b804-026b2da480d9",
   "metadata": {},
   "source": [
    "## mainloopと callback関数\n",
    "\n",
    "Tkなどのグラフィカルユーザーインターフェースを持つプログラムでは、\n",
    "ユーザーからのマウス、キーボードなどの入力や、その他の装置からの通知(例えば、EPICSのCA monitor エベント）を\n",
    "監視する mainloop が動作しています。\n",
    "\n",
    "mainloopはこれらのエベントが発生すると、このエベントを待っている部品(ウィジェット)の指定された\n",
    "コールバック関数を実行します。 上記の例では、`Button`を作成した時に指定した `command=print_hello` などの`print_hello`や`root.quit`がコールバック関数です。\n",
    "\n",
    "![Tk main loop](./_images/TkMainloop-Wm.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30305b52-b663-44a6-9f09-6fa322b9b5b6",
   "metadata": {},
   "source": [
    "### commandに引数を渡すことはできないの？\n",
    "\n",
    "(講座中に出た質問に対するお答えとして）\n",
    "\n",
    "ボタンウィジェットのcommand引数に渡す関数には引数がありません。ボタンを作るごとにcallback関数を作ると、同じようなcallback関数を複数作ることになってしまいます。異なる部分をパラメータ化して、callback関数を共通化することができれば便利です。\n",
    "\n",
    "これをPythonで実現するには：\n",
    "\n",
    " 1. 必要な情報を取り込んだ新しいボタンクラスを定義し、そのメンバ関数をcommandに指定する。\n",
    " 1. 関数内関数を定義して、既定引数としてパラメータを渡す。\n",
    " 1. 同様に、`lambda` 式を使い、既定引数としてパラメータを渡す。\n",
    "\n",
    "といった方法が考えられます。`lambda`式を使う方法では、\n",
    "\n",
    "```\n",
    "msg=\"special message\"\n",
    "btn =Button(root, text=\"Push me!\", command=lambda msg=msg:print(msg))\n",
    "```\n",
    "\n",
    "とすることで、`Button`ウィジェットに実行時のパラメータを渡すことができます。　渡すパラメータを変更可能なオブジェクトとすることで、動的に動作を変更することも可能です。もっともこのような場合には、Classを使った方法をとることが多いでしょう。\n",
    "\n",
    "蛇足：\n",
    "command引数のように関数そのものを実引数とできるのは、Pythonでは関数そのものもプログラムで処理できるデータ(オブジェクト）となっているからです。 関数そのものをデータとして処理できることを使って、pythonで\"関数プログラミング\"的な手法を利用できますが、これはまた別のお話し、いつか別のときにお話しすることにいたしましょう。\n",
    "\n",
    "#### `lambda` 式\n",
    "\n",
    "`lambda`式の名前 `lambda`は数学での`lambda` 算法から来ています。チャーチ(Alonzo Church)の`lambda`算法はチューリングのチューリング機械と同様に、計算可能性の議論の為に導入されました。`lambda`式と類似の概念はLISP(LAMBDA), javascript(無名関数)などのプログラム言語でも利用されています。\n",
    "\n",
    "Pythonでの`lamda`式は、関数定義と似た機能を持っていますが、処理の本体が一つの式であるという強い制限があります。\n",
    "\n",
    "関数定義を使うと二つの引数`x,y`からその和を求める関数は、\n",
    "\n",
    "``` python\n",
    "def sum(x,y):\n",
    "    return x+y\n",
    "```\n",
    "となりますが、同様の関数オブジェクトを\n",
    "\n",
    "``` python\n",
    "sum_l=lambda x,y: x+y\n",
    "```\n",
    "\n",
    "と定義することができます。\n",
    "実際にこの`sum_l`を使ってみます。\n",
    "\n",
    "```\n",
    ">>> sum_l=lambda x,y : x+y\n",
    ">>> sum_l(1,2)\n",
    "3\n",
    "```\n",
    "\n",
    "`lambda`式の値は関数オブジェクトそのものですから、\n",
    "\n",
    "``` python\n",
    ">>> (lambda x,y : x+y)(3,4)\n",
    "7\n",
    "```\n",
    "というような使い方も可能です。\n",
    "\n",
    "#### bind\n",
    "\n",
    "ウィジェットには`.bind`というメソッドも用意されており、マウスのクリック／リリースやキーボードのキー押下などのエベントのそれぞれに対して、callback関数を定義することができます。このcallback関数には、callback関数を呼び出す要因となったエベントを表すエベントオブジェクトが引数として渡されます。 \n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7960765c-909f-4496-a357-f2a87d4c1706",
   "metadata": {},
   "source": [
    "## 部品の実体(ウィジェット）を作る\n",
    "\n",
    "`btn=ttk.Button(root, text=\"Push me!\", command=print_hello)`\n",
    "\n",
    "ここから、プログラムの中を見ていきます。\n",
    "\n",
    "この文で`btn`に代入されているのはどんなデータなのでしょう？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "3eb76cb1-ec2b-4125-96e1-54fecd850a88",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "btn = <tkinter.ttk.Button object .!button>\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2022-02-09 17:13:10.893 Python[29582:3167633] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to /var/folders/3h/lqzhtkt17vv47gbtv470wcp40000gn/T/org.python.python.savedState\n"
     ]
    }
   ],
   "source": [
    "import tkinter, tkinter.ttk as ttk\n",
    "root=tkinter.Tk()\n",
    "btn=ttk.Button(root, text=\"Push me!\")\n",
    "print(f\"{btn = !r}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c90ffeed-d257-41ab-b984-02d4454a2d44",
   "metadata": {},
   "source": [
    "というように、`tkinter.ttk.Button` クラスのオブジェクト(`object`)であることがわかります。\n",
    "では、`クラス`や`オブジェクト`とはなんでしょうか？"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "208face9-7af5-4e3a-bf73-17b76c016d72",
   "metadata": {},
   "source": [
    "## 部品（ウィジェット）は クラス\n",
    "\n",
    "クラス(class)とオブジェクト(object)（インスタンス,instance, とも）はオブジェクト志向プログラム(OOP:Object Oriented Program)と呼ばれる方法で中心となる考え方です。プログラムをデータとそのデータに特有な処理をセットにしたソフトウェア部品を組み合わせるように作る考え方がオブジェクト志向プログラムと呼ばれます。関数は与えられたデータに対する処理を表しますが、データと処理を同時にもっているのがオブジェクトという考え方です。\n",
    "\n",
    "文字型のデータはその一部だけを取り出したり、複数のデータを結合するなど特有の機能が用意されています。このような考え方を発展させて、問題にあったデータとそれを取り扱うアルゴリズムを纏めて、新しいデータ型を生む出す仕組みが、クラスとオブジェクだと考えていいでしょう。\n",
    "\n",
    "先ほどのtkinterのプログラム例では二つのオブジェクト(`btn`と`qbtn`)が作られました。\n",
    "これらのオブジェクトでは、ラベルはそれぞれ異なりますし、押した時の動作も異なりますが、その他は同じような働きをしています。つまり、これらの部品がオブジェクト(表示窓の中に使われる部品、オブジェクトということで、 ウィジェット(Widget=window+object)と呼ばれます)。 `ttk.Button`は、これらのオブジェクトがもつ同じ性質(同じクラス）につけられた名前(クラス名)です。\n",
    "\n",
    "`クラス`と`オブジェクト`の関係は次のよう書き表されます。\n",
    "\n",
    " * クラス(class): 設計図 = データ(property, field, member) + 処理(method, member function)\n",
    " * オブジェクト（object): 設計図に基づいて作られたソフトウェア部品\n",
    "\n",
    "HelloWoldTk.pyでは\n",
    "\n",
    " * `tkinter.Tk`や`tkinter.Button`がクラス\n",
    " * `root=Tk()`や`btn=ttk.Button(...)`がオブジェク\n",
    " \n",
    "です。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c9f36e9b-b497-4e13-acc2-8f436b13ccc2",
   "metadata": {},
   "source": [
    "### Tk/TTk のウィジェット\n",
    "tkiniterで使える　Tkのウィジェットには以下のものがあります。\n",
    "　　　　　　\n",
    " * 独立した窓 : Tk, Toplevel\n",
    " * 部品枠: Canvas, Frame, PanedWindow, LabelFrame　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　\n",
    " * テキスト: Entry, Label, Listbox, Message\n",
    " * 制御: Button, Checkbutton, Radiobutton, Scale,　Scrollbar,　Spinbox,　Menu,　Menubutton\n",
    "\n",
    "テーマ付きウィジェット(ttk)としてさらに、\n",
    "\n",
    " * 部品枠: Notebook,　Separator,　Sizegrip, Treeview,　Labelframe\n",
    " * 制御: Combobox, LabeledScale,　Progressbar\n",
    "\n",
    "といったウィジェットが定義されています。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ff5816c6-6870-4b16-8a6d-85e63f714855",
   "metadata": {},
   "source": [
    "#### 余談：ラジオボタン\n",
    "Googleで\"Radio button　画像\"と検索してみても、でてくるのは計算機上のユーザーインタフェースの画像ばかりでした。\n",
    "Wikipediaにあった[ラジオボタン](https://commons.wikimedia.org/wiki/File:Car_Radio_of_Analog_Era.jpg)の画像がこちら。(このラジオボタンは四角いですけれど）\n",
    "\n",
    "![ラジオボタン](./_images/320px-Car_Radio_of_Analog_Era.jpeg)\n",
    "\n",
    "[ラジオボタンを、\"正しくは\"Radial Button\"と信じている人も既に出ているもようです。](http://webmender.com/archives/000014.html)　(実物のラジオボタンを見たことなければ、そういう人が出てくるのも不思議ではないですが。）\n",
    "\n",
    "本来の話に戻りましょう。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5362aacf-519a-42b3-9d8e-553d694460cb",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "### `root=tkinter.Tk()`\n",
    "\n",
    "この文も`tkinter.Tk`クラスのオブジェクトを作成し、それを変数`root`に割り当てています。\n",
    "```\n",
    "dirt(root)\n",
    "```\n",
    "とすることで、`root` オブジェクトが持つメンバ変数や、メソッドのリストを見ることができます。\n",
    "\n",
    "```\n",
    "help(root)\n",
    "```\n",
    "でより詳細な説明が端末に出力されます。情報量が多すぎて、一気に読むのは難しいでしょう。まあ、全てを一気に理解しなくても、tkinterのアプリケーションを作ることはできます。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9681bde9-7c33-430d-a07e-381c67a894df",
   "metadata": {},
   "source": [
    "### ttk モジュールのクラス\n",
    "\n",
    "`tkinter`, `tkinter.ttk`には グラフィカルユーザインタフェースを作成するための部品(クラス）が\n",
    "あらかじめ用意されています。　次の図は `tkinter.ttk`モジュールで定義されているクラスの関係を示した図です。\n",
    "\n",
    "[pyreverse -p ttk -f PUB_ONLY -ASk --colorized --max-color-depth=8 -o png tkinter.ttk]: ./_images/classes_ttk.png\n",
    "\n",
    "\n",
    "[<img src=\"./_images/classes_ttk.png\" width=\"800pt\"/>][pyreverse -p ttk -f PUB_ONLY -ASk --colorized --max-color-depth=8 -o png tkinter.ttk]\n",
    "\n",
    "[pyreverse: class diagram generator]: https://docs.oracle.com/cd/E56342_01/html/E54074/pyreverse-1.html\n",
    "[pyreverse in pylint]:https://pylint.pycqa.org/en/latest/additional_commands/index.html\n",
    "\n",
    "これらの部品(ウィジェット）を`Tk()`の窓の中に配置し、それらの部品がユーザーの操作に対して、どう反応するかを指定していくのが、グラフィカルユーザインタフェースのプログラムの中心作業となります。　\n",
    "\n",
    "ttkは`Themed Tk`の名前が示すように、`style`の集合体である`theme`を変えることで、tkkに基づいたウィジェットを使ったアプリケーションの見映えを簡単に変えることができるようになります。\n",
    "\n",
    "[tkinter class diagram]: ./_images/classes_tkinter.png\n",
    "\n",
    "\n",
    "[tkinterのクラス図はこちら <img src=\"./_images/classes_tkinter.png\" width=\"300pt\"/> ][tkinter class diagram]\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4833f8ee-d351-4654-b2c2-582c01e1480e",
   "metadata": {},
   "source": [
    "## Tcl/Tk -> python/tkinter\n",
    "\n",
    "[TkDocs]:https://tkdocs.com/tutorial/concepts.html\n",
    "\n",
    "`python tkinter`を利用しているとき、その背後では`Tcl/Tk`のインタプリタが動作しています。\n",
    "このインタプリタを使って、pythonからTcl/Tkのプログラム文を実行できます。また、Tcl/Tkインタプリタ内の変数の値を読み出したり、設定することも可能です。\n",
    "\n",
    "### Tcl/Tk 変数の読み書き\n",
    "`ptyhon`プログラムが接続中の`Tcl/Tk`のインタプリタ中の変数の現在値を, `.getvar()`メソッドを通じて読み込むことも\n",
    "できます。`.setvar()`で変数名と新しい値を与えると、その名前をもつTcl/Tkインタプリタの変数の値が変更されます。\n",
    "\n",
    "```\n",
    " root.getvar( \"tk_patchLevel\")\n",
    "```\n",
    "\n",
    "\n",
    "### Tcl/Tkコマンドの実行\n",
    "\n",
    "`Tk`のオブジェクト`root`で、`root.call(\"command\",\"arg1\",...)`を実行することで、Tcl/TkのコマンドをPythonプログラム中から実行できます。\n",
    "\n",
    "また、Tcl/Tkの文を文字列として、Tcl/Tkのインタプリターに実行させることもできます。\n",
    "\n",
    "``` pytho\n",
    "  root=tkinter.Tk()\n",
    "  root.call(\"expr\", \"$tk_patchLevel\")\n",
    "```\n",
    "\n",
    "### Tkのコマンドとtkinterの対応\n",
    "\n",
    "`tkinter`, `tkinter.ttk`のウィジェットを使いこなすためには、それぞれの[ウィジェットの説明書][TkDocs]を読む必要があります。　`tkinter`,`tkinter.ttk`は `Tcl/Tk`のために用意された部品をPythonから使えるようにして実現されています。場合によっては使いたい部品の説明書が`tkinter`向けには用意されていないかもしれませんが、`Tcl/Tk`向けの説明書は必ず存在します。幸いなことに、Tcl/Tkでの　Tk ウィジェットの説明をpythonでの `tkinter` での使い方に読み替えることができます。\n",
    "\n",
    "まず、Tcl/Tkのウィジェット名はtkinterの同名のクラスに読み替えます。\n",
    "```\n",
    "# ウィジェットの生成\n",
    "button   .fred   -fg red -text \"hi there\" \n",
    "                            =====> fred=Button(fg=\"red\", text=\"Hi There\")\n",
    "\n",
    "button .panel.fred          =====>  fred = Button(panel)\n",
    "```\n",
    "\n",
    "インスタンスメソッドの実行はメンバ関数の呼び出しに読み替える。\n",
    "```\n",
    ".fred invoke                =====>  fred.invoke()\n",
    "```\n",
    "\n",
    "pack, grid, placeは関数呼び出しに読み替える。\n",
    "\n",
    "``` \n",
    "pack .fred                  =====>  fred.pack()\n",
    "```\n",
    "\n",
    "wm,winfo 呼び出しはWdigetのメソッドに読み替える。\n",
    "\n",
    "```\n",
    "wm title . \"win title\"  ===> root.wm_title(\"win title\")\n",
    "winfo name .btn ====> btn.winfo_name()\n",
    "```\n",
    "\n",
    "\n",
    "\n",
    "注：\n",
    "\n",
    "TclのarrayはPythonでいうところの辞書型データ(連想配列). Pythonのarrayとは全く別物なので、注意。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "16ce4a6d-dd53-4e48-831e-87bf7f1f8f0a",
   "metadata": {},
   "source": [
    "### [Tk コマンド][Tk commands] と　tkinterの対応表\n",
    "[Tk commands]: https://www.tcl.tk/man/tcl/TkCmd/console.html\n",
    "\n",
    "[Tk-tkinter対応表]: ./_images/Tk-tkinter対応表.png\n",
    "\n",
    "Tk8.6のコマンドとtkinterでそれに対応するクラス／関数／メソッドの一覧表を作成してみました。表のエントリはmanページの項目に対応しており、必ずしもコマンド名とは限りません(変数名や一般的な説明も含まれます）。\n",
    "\n",
    "[![Tk-tkinter対応表]][Tk-tkinter対応表]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "07956bcc-a4dc-403b-a692-1236a8414a7f",
   "metadata": {},
   "source": [
    "### Tkのドキュメントについて\n",
    "\n",
    "[上記のweb][TkDocs Introduction]でも\n",
    "\n",
    "```\n",
    "The general state of Tk documentation (outside the Tcl-oriented reference documentation, which is excellent) is unfortunately not at a high point these days. This is particularly true for developers using Tk from languages other than Tcl or working on multiple platforms.\n",
    "```\n",
    "\n",
    "と言っています。 これらのTcl/Tkドキュメントを python/tkinter のプログラム開発で参照する必要が出てくることも考えられます。\n",
    "\n",
    "[TkDocs Introduction]: https://tkdocs.com/tutorial/intro.html"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8bd7857c-0255-4be0-843f-9e12cec7d329",
   "metadata": {},
   "source": [
    "## GridとPack\n",
    "\n",
    "Tkの ウィジェットをウィンドウの中に配置するのには、`grid`, `pack`, `place`という配置マネージャーが標準で用意されています。`pack`は簡単に使えて便利ですが、複雑な配置を設定するには、ちょっと工夫が必要です。`pack`は縦一列あるいは横一列にウィジェットを並べる場合にはとても便利です。一方、`grid`は２次元の格子状にウィジェットを配置するのに適しています。 (最近のTutorialでは`grid`がまず使われいます。）\n",
    "\n",
    "`pack`,`grid`ではウィジェットのサイズを自動的に調整してくれる機能があります。\n",
    "\n",
    "`place`はウィジェットの表示する領域内での位置を正確にしていすることができます。この三つの配置マネージャーの中では、最大の自由度を持っていますが、表示が適切になされるかどうかは、設定次第です。\n",
    "\n",
    "`pack`と`grid`を使い分けて組み合わせることも可能です。(`pack`と`grid`の混ぜ合わせができない場合もありますので、これには注意が必要です）\n",
    "\n",
    "[python3の公式ドキュメントのtkinter解説ページ][python3 document tkinter]では`Packer`を中心に説明されていますが、その他の(Tkの解説書を含む）最近の文書では`Grid`を中心に解説しています。 私の印象としては、簡単なアプリケーションの場合は、`Packer`が便利ですが、ウィジェットの数が増えてくると、配置のためだけに数多くの`Frame`を使う必要があり、管理が大変になります。こういった場合は`Grid`の方が便利でしょう。ということで、初めから`Grid`を使うように最近は誘導しているのかなと思います。　複雑なウィジェットの配置が必要になった場合は、`Notebook`や`PanedWindow`などの複合ウィジェットを使うことも考えましょう。\n",
    "\n",
    "[python3 document tkinter]: https://docs.python.org/ja/3/library/tkinter.html\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e37efe39-261d-4d1c-aa14-d7838e162843",
   "metadata": {},
   "source": [
    "## Geometry Manager: GridとPackの基本的な使い方(考え方）\n",
    "\n",
    "Tkのウィジェットは`Tk`と`Toplevel`を除き、それが所属するウィジェット(master)をもっています。通常ウィジェットを作成する際の最初の引数として与えられます。省略した場合はデフォルトのrootオブジェクトがmasterになります。ウィジェットのマスターオブジェクトは`.master`メンバ変数で確認できます。\n",
    "(`pack`,`grid`の呼び出し時に`in_`引数で`mater`オブジェクトを指定することもできます。)\n",
    "\n",
    "\n",
    "`Grid`や`Pack`では、同じオブジェクトを`master`とするウィジェットを、与えられた条件を考慮してウィジェットの位置や大きさを\n",
    "自動的に調整して、配置してくれます。\n",
    "\n",
    "![Grid_Pack](./_images/Grid_or_Pack.png)\n",
    "\n",
    "\n",
    "### Grid\n",
    "Gridでは、ウィジェットを配置する窓／ウィジェットを格子状に分けて考えます。\n",
    "複数の格子にまたがるウィジェットを配置することができます（`columnspan`, `rowspan`)。\n",
    "    \n",
    "    * .grid(row, column, rawsspan, columnspan,　sticky)\n",
    "        * row, column: 行と列を0から始まる番号で指定します。\n",
    "        * rawsspan, columnspan：縦横の占める列数／行数を指定します。\n",
    "        * sticky:　ウィジェットがそれを含む枠に対して小さい時の配置についてのパラメータ（N,S, E, W)\n",
    "        \n",
    "### Pack\n",
    "Packでは、同じ窓に配置するウィジェットをpack呼び出しの順に詰めていきます。希望する配置を実現するために必要に応じてFrameウィジェットを組み合わせていきます。　Gridでも必要に応じて中間的な`Frame`ウィジェットをつかうことができます。\n",
    "\n",
    "    * .pack(side, anchor, expand, fill)\n",
    "        * side: マスターのウィジェットに埋め込んでいく向きを指定します。(tkintre.TOP,tkintre.BOTTOM, tkintre.LEFT,tkintre.RIGHT)\n",
    "        * anchor:　ウィジェットを配置する区画の位置を指定します。( n, ne, e, se, s, sw, w, nw, or center）\n",
    "        * expand:ウィジェットのサイズを調整するか否か(True False)\n",
    "        * fill: サイズを変更する向き(None, tkintre.X, tkintre.Y, tkintre.BOTH)\n",
    "\n",
    "\n",
    "### Place\n",
    "masterに対する位置をピクセル単位で指定できます。全てのウィジェットの配置を完全に制御することができます。\n",
    "反面、一部のウィジェットの大きさを変更した場合や、さまざまな環境で窓自体の大きさが変わる環境でも適切に配置するためには、工夫が必要です。\n",
    "\n",
    "### 複合ウィジェット\n",
    "高度なウィジェットの配置は、Grid/Packerを複雑に組み合わせるよりも、paned Window, Notebookなどのウィジェットを\n",
    "使うことを考えましょう。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "054b7f5a-15ad-4519-9a73-aacde19cadd9",
   "metadata": {},
   "source": [
    "#### 余談：GridとPack\n",
    "[TkDocs](https://tkdocs.com/tutorial/concepts.html#geometry)には次のような記述があります。\n",
    "\n",
    "```\n",
    "We'll go into more detail in a later chapter, but grid was introduced several years after Tk became popular. Before that, an older geometry manager named pack was most commonly used. It's equally powerful but much harder to use, making it onerous to create layouts that look appealing today. Unfortunately, much of the example Tk code and documentation out there uses pack instead of grid (a good clue to how current it is). The widespread use of pack is a leading reason that so many Tk user interfaces look terrible. Start new code with grid, and upgrade old code when you can.\n",
    "```\n",
    "ということで、これから Tk を学ばれる方は`Grid`だけを使うのが良さそうです。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "31ef78b8-e7cb-44a1-b721-7e09e277be60",
   "metadata": {},
   "source": [
    "次の例のように、`Grid`と`Pack`のどちらでも、目指す配置を実現することはできます。\n",
    "画面構成をあらかじめ決定して作成するには、`Grid`の方が便利でしょう。`Pack`は融通が効きますが、望む配置を実現するためには、表示されない`Frame`ウィジェットをを使うことになりがちです。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "7e7433d7-12d5-4740-9182-8a410edd4a53",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting HelloWorldTkGrid.py\n"
     ]
    }
   ],
   "source": [
    "%%file HelloWorldTkGrid.py\n",
    "#!python3\n",
    "# -*- coding:utf-8 -*-\n",
    "\n",
    "import tkinter, tkinter.ttk as ttk\n",
    "\n",
    "def hello(root):\n",
    "    def print_hello():\n",
    "        print(\"Hello\",\"World.\")\n",
    "    btn=ttk.Button(root, text=\"Push me!\", command=print_hello)\n",
    "    qbtn=ttk.Button(root, text=\"Quit\", command=root.destroy)\n",
    "    #\n",
    "    btn.grid(row=0,  column=0)\n",
    "    qbtn.grid(row=0, column=1)\n",
    "\n",
    "tk=tkinter.Tk()\n",
    "hello(tk)\n",
    "tk.mainloop()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "c4964e4c-52a1-43b0-91e3-c185394aadc2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2022-02-09 17:13:11.155 Python[29649:3168051] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to /var/folders/3h/lqzhtkt17vv47gbtv470wcp40000gn/T/org.python.python.savedState\n"
     ]
    }
   ],
   "source": [
    "!python3 HelloWorldTkGrid.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "dbfcfa2c-c503-4705-867e-950e0115c15e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting HelloWorldTkPack.py\n"
     ]
    }
   ],
   "source": [
    "%%file HelloWorldTkPack.py\n",
    "#!python3\n",
    "# -*- coding:utf-8 -*-\n",
    "\n",
    "import tkinter, tkinter.ttk as ttk\n",
    "from tkinter import RIGHT,LEFT,TOP,BOTTOM\n",
    "def hello(root):\n",
    "    def print_hello():\n",
    "        print(\"Hello\",\"World.\")\n",
    "    btn=ttk.Button(root, text=\"Push me!\", command=print_hello)\n",
    "    qbtn=ttk.Button(root, text=\"Quit\", command=root.destroy)\n",
    "    #\n",
    "    btn.pack(side=LEFT)\n",
    "    qbtn.pack(side=LEFT)\n",
    "\n",
    "tk=tkinter.Tk()\n",
    "hello(tk)\n",
    "tk.mainloop()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "b052fbd3-8d26-4587-9320-8613d782c7e7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2022-02-09 17:13:12.987 Python[29652:3168078] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to /var/folders/3h/lqzhtkt17vv47gbtv470wcp40000gn/T/org.python.python.savedState\n"
     ]
    }
   ],
   "source": [
    "!python3 HelloWorldTkPack.py"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5b4ec759-3123-44f9-8686-4eaaae628b7f",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "## TkとToplevel\n",
    "`Tk`クラスと`Toplevel`クラスはWm(Window manager)クラスを継承していて、デスクトップ上に独立した窓として表示されます。(その他のウィジェットはTkあるいは他のウィジェットの中に表示されます。）\n",
    "\n",
    "`tkinter.Tk`と`tkinter.Toplevel`は　どちらも画面上に独立した窓を開きます。ではこの「二つのものは同じか？ 」というとそうではありません。\n",
    "Toplevelはウィジェットの一つででもあります。\n",
    "したがって上位にTkのマスターが存在します。異なるTkウィジェットは窓を閉じても、他のTkウィジェットに影響しません。\n",
    "\n",
    "通常、一つのアプリケーションには`root`(従って、`Tk()`呼び出し）は一つだけです。`root`の他に新たな窓が必要な場合には`Toplevel()`を呼び出します。\n",
    "この場合には、`root`のウィンドウを閉じると、アプリケーション全体が停止することを忘れないようにしましょう。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "be2f6df5-c8b7-45bc-80de-97302daf797c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting HelloWorldApp.py\n"
     ]
    }
   ],
   "source": [
    "%%file HelloWorldApp.py\n",
    "#!python3\n",
    "# -*- coding:utf-8 -*-\n",
    "# 公式ドキュメント中の”簡単な Hello World プログラム”\n",
    "import tkinter as tk, tkinter.ttk as ttk\n",
    "\n",
    "class Application(tk.Frame):\n",
    "    def __init__(self, master=None,msg=\"Hi there, everyone!\"):\n",
    "        super().__init__(master)\n",
    "        self.master = master\n",
    "        self.msg=msg\n",
    "        self.grid()\n",
    "        self.create_widgets()\n",
    "\n",
    "    def create_widgets(self):\n",
    "        self.hi_there = tk.Button(self)\n",
    "        self.hi_there[\"text\"] = \"Hello World\\n(click me)\"\n",
    "        self.hi_there[\"command\"] = self.say_hi\n",
    "        self.quit = tk.Button(self, text=\"QUIT\", fg=\"red\",font=\"Times 36 bold\",\n",
    "                              command=self.master.destroy)\n",
    "        self.hi_there.pack(side=\"top\")    \n",
    "        self.quit.pack(side=\"bottom\")\n",
    "\n",
    "    def say_hi(self):\n",
    "        print(self.msg)\n",
    "\n",
    "root = tk.Tk()\n",
    "root.title(\"Root\")\n",
    "app = Application(master=root)\n",
    "\n",
    "top= tk.Toplevel(master=root)\n",
    "top.title(\"Toplevel\")\n",
    "oapp=Application(master=top)\n",
    "\n",
    "oroot= tk.Tk()\n",
    "oroot.title(\"Other Tk\")\n",
    "more=Application(master=oroot)\n",
    "\n",
    "root.mainloop()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "896ad28b-8450-48fa-b532-aee30d2678d0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2022-02-09 17:13:14.350 Python[29655:3168103] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to /var/folders/3h/lqzhtkt17vv47gbtv470wcp40000gn/T/org.python.python.savedState\n"
     ]
    }
   ],
   "source": [
    "!python3 HelloWorldApp.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "0a73569e-86c1-4705-affa-4b18b7849cff",
   "metadata": {},
   "outputs": [],
   "source": [
    "%gui tk\n",
    "#!python3\n",
    "# -*- coding:utf-8 -*-\n",
    "# 公式ドキュメント中の”簡単な Hello World プログラム”\n",
    "import tkinter as tk, tkinter.ttk as ttk\n",
    "\n",
    "class Application(tk.Frame):\n",
    "    def __init__(self, master=None,msg=\"Hi there, everyone!\"):\n",
    "        super().__init__(master)\n",
    "        self.master = master\n",
    "        self.msg=msg\n",
    "        self.grid()\n",
    "        self.create_widgets()\n",
    "\n",
    "    def create_widgets(self):\n",
    "        self.hi_there = tk.Button(self)\n",
    "        self.hi_there[\"text\"] = \"Hello World\\n(click me)\"\n",
    "        self.hi_there[\"command\"] = self.say_hi\n",
    "        self.quit = tk.Button(self, text=\"QUIT\", fg=\"red\",font=\"Times 36 bold\",\n",
    "                              command=self.master.destroy)\n",
    "        self.hi_there.pack(side=\"top\")    \n",
    "        self.quit.pack(side=\"bottom\")\n",
    "\n",
    "    def say_hi(self):\n",
    "        print(self.msg)\n",
    "\n",
    "root = tk.Tk()\n",
    "root.title(\"Root\")\n",
    "app = Application(master=root)\n",
    "\n",
    "top= tk.Toplevel(master=root)\n",
    "top.title(\"Toplevel\")\n",
    "oapp=Application(master=top)\n",
    "\n",
    "oroot= tk.Tk()\n",
    "oroot.title(\"Other Tk\")\n",
    "more=Application(master=oroot)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1476119b-ec8c-4ceb-936e-00a447f982ab",
   "metadata": {},
   "source": [
    "### Tk and Totplevel\n",
    "\n",
    "`Tk()`で作成された`root`を大元の親に持つオブジェクトは,`root`に対応する窓を閉じると同時に消えてしまいます。\n",
    "\n",
    "![TkとToplevelの関係][Tk and Totplevel]  TkはTcl/Tkインタプリタと結び付けられています。\n",
    "\n",
    "[Tk and Totplevel]: ./_images/Tk_and_Toplevel.png"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "905a5f87-95ef-4877-a551-93d685487ffa",
   "metadata": {},
   "source": [
    "## 新しいWidget部品を作る。\n",
    "\n",
    "複数のウィジェットを組み合わせて作った表示画面を部品化（ウィジェット化）することで、\n",
    "これらの部品を多数並べた表示画面を簡単に作れるようになります。\n",
    "これは、**新しいウィジェット**を **クラス** として定義することで実現できます。\n",
    "\n",
    "複数の部品を組み合わせたウィジェットを作る場合は、`Frame`ウィジェットを基に作ることがよくあります。\n",
    " (このように、別の部品を基にしてして、新しい部品をつくることを、クラスの**継承** (inheritance) と呼びます。)\n",
    " \n",
    "\n",
    "### 初期化メンバ関数: \\_\\_init\\_\\_\n",
    "    \n",
    "クラス(設計図)からオブジェクト（部品, 実体）を作る際には、初期化メンバ関数`.__init__`が呼び出されます。\n",
    "\n",
    "初期化メンバ関数`.__init__`の最初の引数は、これから作成されるオブジェクトその物と決まっています。\n",
    "Pythonではこの引数を慣例的に`self`とすることが多いです。しかし、`this`でも良いし、`me`などでも構いません。\n",
    "(しかし、色々変わるのは長期的にみるとプログラムの読みやすさを若干とはいえ損なうということも考えておく必要があります。）\n",
    "\n",
    "### メンバ変数\n",
    "\n",
    "初期化メンバ関数の中では、このクラスが持つ、メンバ変数 （上記の例では、`self.msg`, `self.btn`, `self.qbtn`）を初期化しておきます。\n",
    "\n",
    "pythonではクラスのオブジェクトの実体化は、クラス名を関数であるかのように呼び出すことで実現されます。\n",
    "これによって、オブジェクトの実体が作成され、`.__init__`関数による初期化が実行されたのち、\n",
    "そのオブジェクトがこの関数呼び出しの結果として返されます。\n",
    "\n",
    "```\n",
    "obj=HellolWorldApp(tk)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "82d88d01-9821-47ae-ae3f-94f76766ce5a",
   "metadata": {},
   "source": [
    "## クラス定義／オブジェクト作成／オブジェクト利用\n",
    "![クラスとオブジェクト](./_images/OOP-fundamental.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "e09abbb6-4de3-4896-aa5d-2db5bd3eca80",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting HelloWorldTkApp.py\n"
     ]
    }
   ],
   "source": [
    "%%file HelloWorldTkApp.py\n",
    "#!python3\n",
    "# -*- coding:utf-8 -*-\n",
    "\n",
    "import tkinter, tkinter.ttk as ttk\n",
    "from tkinter import RIGHT,LEFT,TOP,BOTTOM, N,S,W,E\n",
    "\n",
    "class HellolWorldApp(ttk.Frame): # ttk.Frameを親クラスにして新しいクラスHelloWorldAppを定義します(継承）。\n",
    "    def __init__(self, master=None, msg=\"Hello World.\"):\n",
    "        self.__msg=msg # self.__msg is a private member variable. \n",
    "        #外枠を作ります。superは親クラスのメソッドを呼び出すことを指示しています。\n",
    "        super().__init__(master, padding=\"6p\", relief=tkinter.RIDGE)\n",
    "        # このウィジェット(Frame)の内部の部品を作ります。\n",
    "        self.btn =ttk.Button(self, text=\"Push me!\", command=self.say_hello)\n",
    "        self.qbtn=ttk.Button(self, text=\"Quit\", command=master.destroy)\n",
    "        #内部の部品を配置します。ここではgridを使って配置します。packをつかっても\n",
    "        self.btn.grid(row=0)\n",
    "        self.qbtn.grid(row=1)\n",
    "\n",
    "    def say_hello(self):\n",
    "        print(self.__msg)\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    tk=tkinter.Tk()\n",
    "    #部品を作り、配置します。\n",
    "    \n",
    "    HellolWorldApp(tk).grid() #オブジェクトを作成し、.grid()メソッドを呼び出して画面に表示します。\n",
    "    HellolWorldApp(tk,msg=\"Good bye!\").grid() #オブジェクトを作成し、.grid()メソッドを呼び出して画面に表示します。\n",
    "    app=HellolWorldApp(tk)\n",
    "    try:\n",
    "        print(app.__msg)\n",
    "    except:\n",
    "        print(\"privete msg:\",app._HellolWorldApp__msg)\n",
    "    tk.mainloop()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "3c15b695-f99f-43c9-af14-3ee670e07d1a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2022-02-09 17:13:17.274 Python[29659:3168133] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to /var/folders/3h/lqzhtkt17vv47gbtv470wcp40000gn/T/org.python.python.savedState\n",
      "privete msg: Hello World.\n"
     ]
    }
   ],
   "source": [
    "!python3 HelloWorldTkApp.py"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "27871cde-d052-45ce-b7a6-e6c87eee52da",
   "metadata": {},
   "source": [
    "一度ウィジェットをクラス化(部品化)すれば、同じような動作をする部品を複数作成することは容易にできるようになります。\n",
    "rootウィンドウとToplevelの ウィンドウにそれぞれHelloWorldAppのウィジェトを作成します。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "d6a20028-efd1-4016-a888-e1aecfb5cb5d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting HelloWorldTkApp_test.py\n"
     ]
    }
   ],
   "source": [
    "%%file HelloWorldTkApp_test.py\n",
    "from  HelloWorldTkApp import HellolWorldApp\n",
    "import tkinter as tk\n",
    "\n",
    "root=tk.Tk() #rootウィンドウを作ります。\n",
    "HellolWorldApp(root).grid() #オブジェクトを作成し、.grid()メソッドをよびだしています。\n",
    "\n",
    "top=tk.Toplevel() #独立した窓をもう一つ作成。\n",
    "#この窓にHelloWorldTkAppをメッセージを指定して作成します。\n",
    "HellolWorldApp(top,\n",
    "                 msg=\"Welcome to the python/tkinter world.\"\n",
    "                ).pack()\n",
    "root.title(\"Tk Root\") #それぞれの窓のタイトルを指定します。\n",
    "top.title(\"Top Level\")\n",
    "root.mainloop() # tkinterアプリケーションが動作を始めます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "43978d4e-6927-4816-a39d-3f0a87a736c2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2022-02-09 17:13:19.746 Python[29662:3168169] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to /var/folders/3h/lqzhtkt17vv47gbtv470wcp40000gn/T/org.python.python.savedState\n"
     ]
    }
   ],
   "source": [
    "!python3 HelloWorldTkApp_test.py"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3efe1c8d-cd14-40ab-b991-877f7491054b",
   "metadata": {},
   "source": [
    "## Tk Variable\n",
    "\n",
    "GUIアプリケーションを作成する際には、複数のウィジェットの値を連携させる必要がしばしば出てきます。たとえば、`スライダー`ウィジェットを操作すると、\n",
    "それに対応した`Text`あるいは`Entry`ウィジェットの値が変化する。ラジオボタンの状態に応じて、別のウィジェットの見栄え(色、形、画像など）を変更\n",
    "するといった具合です。 Tkにはこのような場合のプログラムを簡単にしてくれる、`Tk Variable` が用意されています。\n",
    "\n",
    "Tkな特徴的な機能の一つにTk `Variable`があります。 `Variable`には四つのサブクラス（子クラス）が定義されています。\n",
    "\n",
    "    BooleanVar, DoubleVar, IntVar, StringVar\n",
    "    \n",
    "これらの`Variable`クラスから作られたオブジェクトは、それぞれ、　ブール値(`True`あるいは`False`)、浮動小数点数、整数、文字列　のデータ型に対応しています。\n",
    "\n",
    "`Variable`は複数のウィジェットが、プログラムの実行中に変化する(動的）共通のデータを持っているときに有効です。 `Variable`を使うことで、Tkではキー入力やマウスクリックのエベントを明示的に`.bind`で処理する必要が軽減されています。\n",
    "\n",
    "`Tk Variable`を使って、 複数のウィジェットを連携させてみます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "6f2c3d45-5e47-41f9-b21a-6139dd84edc6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting Variable_sample.py\n"
     ]
    }
   ],
   "source": [
    "%%file Variable_sample.py\n",
    "import tkinter, tkinter.ttk as ttk\n",
    "from tkinter import Tk, Toplevel, Text\n",
    "from tkinter import Variable, BooleanVar, IntVar, StringVar, DoubleVar\n",
    "from tkinter.ttk import Entry, Label, Scale, Progressbar\n",
    "\n",
    "root=tkinter.Tk()\n",
    "style = ttk.Style()\n",
    "style.theme_use('aqua')\n",
    "\n",
    "tv=StringVar()\n",
    "tv.set(\"Enter string\")\n",
    "e=Entry(textvariable=tv)\n",
    "l=Label(textvariable=tv)\n",
    "\n",
    "dv=DoubleVar()\n",
    "\n",
    "sc=  Scale(variable=dv,to=1024)\n",
    "sc1= Scale(variable=dv,to=1024)\n",
    "dl= Label(textvariable=dv, anchor=\"w\")\n",
    "pb= Progressbar(variable=dv, maximum=1024)\n",
    "\n",
    "l.grid()\n",
    "e.grid()\n",
    "\n",
    "dl.grid()\n",
    "sc.grid()\n",
    "pb.grid()\n",
    "sc1.grid()\n",
    "\n",
    "root.mainloop()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "28e898dd-ba42-4a4c-adb4-62d4b85cc0c2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2022-02-09 17:13:23.707 Python[29668:3168254] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to /var/folders/3h/lqzhtkt17vv47gbtv470wcp40000gn/T/org.python.python.savedState\n"
     ]
    }
   ],
   "source": [
    "!python3 Variable_sample.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "203a77c7-8b51-4d20-96ca-cddac9a07edd",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting HelloWorldTkApp_tes2t.py\n"
     ]
    }
   ],
   "source": [
    "%%file HelloWorldTkApp_tes2t.py\n",
    "from  HelloWorldTkApp import HellolWorldApp\n",
    "import tkinter\n",
    "\n",
    "tk=tkinter.Tk()\n",
    "top=tkinter.Toplevel()\n",
    "HellolWorldApp(tk).grid() #オブジェクトを作成し、.grid()メソッドをよびだしています。\n",
    "HellolWorldApp(top,\n",
    "                 msg=\"Welcome to the python/tkinter world.\"\n",
    "                ).pack()\n",
    "\n",
    "tk2=tkinter.Tk()\n",
    "HellolWorldApp(\n",
    "    tk2, msg=\"Other Tk.\").pack()\n",
    "tk.title(\"Tk\")\n",
    "top.title(\"Top Level\")\n",
    "tk2.title(\"Other Tk\")\n",
    "tk.mainloop()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f371d1f-ed1e-486d-ae3c-3f39c56c4317",
   "metadata": {},
   "source": [
    "## `Canvas` ウィジェット\n",
    "\n",
    "`Canvas`ウィジェットを使うと、幾何学的な要素（直線、円、多角形、曲線）を画面上に自由に配置して描画することができます。\n",
    "次に見るように、`matplotlib`のグラフもこの`tkinter.Canvas`ウィジェットに書き込む形で`tkinter`アプリケーションに埋め込まれます。\n",
    "\n",
    "以下に`Canvas`ウィジェットを使ったプログラムを示します。`Canvas`内に円（楕円）、アーク(円周の一部）をいくつか配置しています。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "6e3af5f4-75c8-44ad-b583-74eb88ad3f5d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting TkAnpanman.py\n"
     ]
    }
   ],
   "source": [
    "%%file TkAnpanman.py\n",
    "import tkinter as tk, tkinter.ttk as ttk\n",
    "from tkinter import Canvas, Tk\n",
    "\n",
    "WIDTH, HEIGHT = 400, 420\n",
    "\n",
    "class AnpanMan(Canvas):\n",
    "    def __init__(self,root, width=WIDTH, height=HEIGHT):\n",
    "        super().__init__(root,width=width,height=height) # in python3\n",
    "\n",
    "    def draw(self):\n",
    "        輪郭=self.create_oval(200-180, 210-180, 200+180, 210+180, width=5, fill=\"bisque\")\n",
    "        左眉=self.create_arc(125-50, 150-50, 125+50, 150+50, extent=180, start=0, style='arc',width=5)\n",
    "        左目=self.create_oval(125-20, 170-20, 125+20, 170+20, fill=\"black\",width=5)\n",
    "        右眉=self.create_arc(275-50, 150-50, 275+50, 150+50, extent=180 , start=0, style='arc',width=5)\n",
    "        右目=self.create_oval(275-20, 170-20, 275+20, 170+20, fill='black', width=5)\n",
    "        鼻=self.create_oval(200-40, 250-40, 200+40, 250+40, fill='red', width=1)\n",
    "        左頬=self.create_arc(90-40, 250-40, 90+40, 250+40, extent=180, start=-90, style='arc',width=5)\n",
    "        右頬=self.create_arc(310-40, 250-40, 310+40, 250+40, extent=180, start=90, style='arc',width=5)\n",
    "        口=self.create_arc(200-60, 305-60, 200+60, 305+60, extent=-180, start=0, \n",
    "                           style='chord', fill=\"DeepPink\", width=5) \n",
    "        \n",
    "if __name__ == \"__main__\":\n",
    "    root=Tk()\n",
    "    app=AnpanMan(root)\n",
    "    app.draw()\n",
    "    app.grid()\n",
    "    root.mainloop()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "e3bc72c7-378e-46f0-b837-2a8f96c9efe6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2022-02-09 17:13:27.159 Python[29672:3168304] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to /var/folders/3h/lqzhtkt17vv47gbtv470wcp40000gn/T/org.python.python.savedState\n"
     ]
    }
   ],
   "source": [
    "!python3 TkAnpanman.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "2a7509ce-d857-4fdb-b0f2-7adb3da26141",
   "metadata": {},
   "outputs": [],
   "source": [
    "%gui tk\n",
    "from TkAnpanman import AnpanMan, Tk\n",
    "if __name__ == \"__main__\":\n",
    "    root=Tk()\n",
    "    app=AnpanMan(root)\n",
    "    app.draw()\n",
    "    app.grid()\n",
    "    root.mainloop()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d93afdfe-e12f-4f4a-a344-65c444d7e675",
   "metadata": {},
   "source": [
    "### `Canvas`の要素\n",
    "`Canvas`の内部には、以下の要素を作成できます。作成には、`canvas.create_xxx`メソッドを使います。(`xxx`はそれぞれの要素の型名がはいります。）\n",
    "\n",
    " * line　: 折線あるいは曲線(`bezier` or `raw`)。矢印(`arrow`)もつけられます。\n",
    " * arc　:`style`には`pieslice` (default), `arc`,`chord`　のいずれかを指定します。\n",
    " * oval :\n",
    " * polygon\n",
    " * rectangle\n",
    " * text\n",
    " * image :PhotoImage/BitmaImage\n",
    " * window : 同じTk/Toplevelの系列に属するウィジェットをCanvasの領域に埋め込むための領域。 (window=<Widget>)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0bf3966a-e112-4f46-9904-e577d912f5f5",
   "metadata": {},
   "source": [
    "## matplotlibのグラフをTk のアプリケーションに埋め込む。\n",
    "\n",
    "おおむね`matplotlib`のグラフを`tkinter`のアプリケーションに埋め込むための準備が整ったので、その説明を始めます。\n",
    "\n",
    "matplotlibはグラフを作成する仕組み(backend)を複数用意しています。`Tk`のアプリケーションにmatplotlibのグラフを埋め込むために、Tkをつかったbackend `TkAgg`を使います。　`TkAgg`ではグラフを`tkinter.Canvas`ウィジェットの中に書き込みます。この`Canvas`ウィジェットをアプリケーションのウィジェット中に配置すれば、`matplotlib`のグラフが`tkinter`のアプリケーションの中に埋め込まれたことになります。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "07e1060f-1afe-44af-ad20-0c09a7e926c3",
   "metadata": {},
   "source": [
    "### matplotlib backend\n",
    "\n",
    "まず`matplotlib backend`について、簡単に説明します。\n",
    "\n",
    "matplotlibのbackedはディスプレイへの描画を担当するライブラリ(Gtk,Qt,Wxなど）と高品位な2Dグラフィックスを生成するライブラリ（AggおよびCairo）の組み合わせを選択できます。\n",
    "\n",
    "```\n",
    "    - interactive backends:\n",
    "          GTK3Agg, GTK3Cairo, GTK4Agg, GTK4Cairo, \n",
    "          QtAgg,　QtCairo, Qt5Agg, Qt5Cairo, \n",
    "          TkAgg, TkCairo, \n",
    "          WX, WXAgg, WXCairo, \n",
    "          MacOSX, nbAgg, WebAgg\n",
    "    \n",
    "    - non-interactive backends:\n",
    "          agg, cairo, pdf, pgf, ps, svg, template\n",
    "```\n",
    "\n",
    "`nbAgg`はIPython/Jupyterのnotebook用backendです。`webAgg`は ウェブブラウザにグラフを出力します。\n",
    "\n",
    "`Agg`と`Cairo`はいずれも高品位な2Dグラフィックスを生成するライブラリです。`Cairo`は`matplotlib`とは別にインストールする必要があります。\n",
    "https://www.tutorialspoint.com/matplotlib-backend-differences-between-agg-and-cairo によれば、`Agg`と`Cairo`の違いは、次の\n",
    "表のようになっています。\n",
    "\n",
    "\n",
    "|Renderer |File types|Description|\n",
    "|:-:|:-:|:-: |\n",
    "|AGG | Png | Raster graphics − high-quality images using the Anti-Grain Geometry engine|\n",
    "|Cairo| png, ps, pdf, svg| Raster or vector graphics − using the Cairo library |\n",
    "\n",
    "\n",
    "`Tk`をbackendの描画エンジンとして使う場合にも`TkAgg`および`TkCairo`を使うことができます。\n",
    "\n",
    "[![matplotlib backends]][matplotlib backends]\n",
    "\n",
    "[matplotlib backends]: ./_images/mpl-backends.png"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c49234c0-bddd-4aa5-85fd-80048717f4a3",
   "metadata": {},
   "source": [
    "### FigureCanvasTkAgg\n",
    "\n",
    "`matplotlib`の`FigureCanvasTkAgg`関数を使い、`matplotlib`のFigureオブジェクトを、`tkinter`の`Canvas`ウィジェットに結びつけます。\n",
    "matplot側ではこの`Figure`オブジェクトに描画すると、`tkinter`の`Canvas`の中にグラフが現れると言うわけです。\n",
    "\n",
    "サンプルプログラム(下記)の\n",
    "```\n",
    "        self.fig=Figure(figsize=(16,9), dpi=72)\n",
    "        self.canvas=FigureCanvasTkAgg(self.fig, master=self)  # A tk.DrawingArea.\n",
    "```\n",
    "でこれが実現されています。\n",
    "\n",
    "### Tk Dialog\n",
    "\n",
    "このプログラムでは、`askopenfilename`や`showinfo`など`Tk/tkinter`が提供するダイアログ(アプリケーションとは別の窓を開いてユーザーとのインタラクションを\n",
    "行う）を利用しています。　`tkinter.messagebox`,`tkinter.filedialog`,`tkinter.simpledialog`によく使われるダイアログが用意されています。\n",
    "これらのダイアログを使うことで、開発の効率が上がるでしょう。また、これらのダイアログのもとになっているクラスを継承することで、独自のダイアログを作成することもできます。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32282fa4-d058-46e5-bdc4-9f2357a5d145",
   "metadata": {},
   "source": [
    "### `messagebox`の関数\n",
    "\n",
    "    askokcancel(title=None, message=None, **options)\n",
    "        Ask if operation should proceed; return true if the answer is ok\n",
    "    \n",
    "    askquestion(title=None, message=None, **options)\n",
    "        Ask a question\n",
    "    \n",
    "    askretrycancel(title=None, message=None, **options)\n",
    "        Ask if operation should be retried; return true if the answer is yes\n",
    "    \n",
    "    askyesno(title=None, message=None, **options)\n",
    "        Ask a question; return true if the answer is yes\n",
    "    \n",
    "    askyesnocancel(title=None, message=None, **options)\n",
    "        Ask a question; return true if the answer is yes, None if cancelled.\n",
    "    \n",
    "    showerror(title=None, message=None, **options)\n",
    "        Show an error message\n",
    "    \n",
    "    showinfo(title=None, message=None, **options)\n",
    "        Show an info message\n",
    "    \n",
    "    showwarning(title=None, message=None, **options)\n",
    "        Show a warning message\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b570947e-571b-4e8c-963a-781377531e76",
   "metadata": {},
   "source": [
    "### `filedialog`の関数\n",
    "\n",
    "    askdirectory(**options)\n",
    "        Ask for a directory, and return the file name\n",
    "    \n",
    "    askopenfile(mode='r', **options)\n",
    "        Ask for a filename to open, and returned the opened file\n",
    "    \n",
    "    askopenfilename(**options)\n",
    "        Ask for a filename to open\n",
    "    \n",
    "    askopenfilenames(**options)\n",
    "        Ask for multiple filenames to open\n",
    "        \n",
    "        Returns a list of filenames or empty list if\n",
    "        cancel button selected\n",
    "    \n",
    "    askopenfiles(mode='r', **options)\n",
    "        Ask for multiple filenames and return the open file\n",
    "        objects\n",
    "        \n",
    "        returns a list of open file objects or an empty list if\n",
    "        cancel selected\n",
    "    \n",
    "    asksaveasfile(mode='w', **options)\n",
    "        Ask for a filename to save as, and returned the opened file\n",
    "    \n",
    "    asksaveasfilename(**options)\n",
    "        Ask for a filename to save as\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "26115e23-7e73-4301-b9ab-1d103261e945",
   "metadata": {},
   "source": [
    "### `simpledialog`の関数\n",
    "\n",
    "    askfloat(title, prompt, **kw)\n",
    "        get a float from the user\n",
    "        \n",
    "        Arguments:\n",
    "        \n",
    "            title -- the dialog title\n",
    "            prompt -- the label text\n",
    "            **kw -- see SimpleDialog class\n",
    "        \n",
    "        Return value is a float\n",
    "    \n",
    "    askinteger(title, prompt, **kw)\n",
    "        get an integer from the user\n",
    "        \n",
    "        Arguments:\n",
    "        \n",
    "            title -- the dialog title\n",
    "            prompt -- the label text\n",
    "            **kw -- see SimpleDialog class\n",
    "            \n",
    "        Return value is an integer\n",
    "    \n",
    "    askstring(title, prompt, **kw)\n",
    "        get a string from the user\n",
    "        \n",
    "        Arguments:\n",
    "        \n",
    "            title -- the dialog title\n",
    "            prompt -- the label text\n",
    "            **kw -- see SimpleDialog class\n",
    "        \n",
    "        Return value is a string"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "af426a1f-6c79-4894-be94-9d687b0a46f6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting Plt2PngTk.py\n"
     ]
    }
   ],
   "source": [
    "%%file Plt2PngTk.py\n",
    "#!python3\n",
    "# -*- coding:utf-8 -*-\n",
    "\n",
    "import tkinter, tkinter.ttk as ttk\n",
    "from tkinter import Frame\n",
    "from tkinter.ttk import Button, Entry\n",
    "from tkinter.filedialog import askopenfile,askopenfilename\n",
    "from tkinter.messagebox import showinfo\n",
    "from tkinter import N,S,E,W, X, Y, BOTH\n",
    "import os\n",
    "\n",
    "import matplotlib\n",
    "matplotlib.use(\"macosx\")\n",
    "\n",
    "from matplotlib.backends.backend_tkagg import (\n",
    "    FigureCanvasTkAgg, NavigationToolbar2Tk)\n",
    "# Implement the default Matplotlib key bindings.\n",
    "# from matplotlib.backend_bases import key_press_handler\n",
    "from matplotlib.figure import Figure\n",
    "\n",
    "from plt2png import *\n",
    "\n",
    "class Plt2Graph(Frame):\n",
    "    def __init__(self,master=None):\n",
    "        super(Plt2Graph, self).__init__(master) #親から継承した __init__関数を実行。python3ではsuper().__init_(master)で良い。\n",
    "        self.master.title(\"plt to png\")\n",
    "        self.setup() # 内部のウィジェットをつくり、\n",
    "        self.grid()  # 配置します。\n",
    "\n",
    "    def setup(self): # 内部のウィジェットを作ります。\n",
    "        self.pltfnvar=tkinter.StringVar() #StringVarを使って、描画する.pltファイルを管理します。\n",
    "        self.pltfn=Entry(self, textvariable=self.pltfnvar) # EntryウィジェットとStringVarを接続しておきます。\n",
    "        self.selectbtn=Button(self,text=\"選択\", command=self.choosefile) #ファイルダイアログを使ってファイルを選択するためのボタン\n",
    "        #\n",
    "        self.fig=Figure(figsize=(16,9), dpi=72,layout='tight')\n",
    "        self.canvas=FigureCanvasTkAgg(self.fig, master=self)  # A tk.DrawingArea.\n",
    "        #self.canvas.draw()\n",
    "        self.drawbtn=Button(self,text=\"グラフ作成\",\n",
    "                            command=self.draw, state=[\"disabled\"]) #初期状態ではボタンを押せない\n",
    "        self.savebtn=Button(self,text=\"グラフ保存\", \n",
    "                            command=self.savefig, state=[\"disabled\"]) #初期状態ではボタンを押せない\n",
    "        \n",
    "        self.quitbtn=Button(self,text=\"終了\",\n",
    "                            command=self.master.quit)\n",
    "        \n",
    "    def grid(self,*args,**env): #　Grid manageで内部のウィジェットを配置します。\n",
    "        super(Plt2Graph, self).grid(*args,**env)\n",
    "        self.columnconfigure(0, weight=1)\n",
    "        self.pltfn.grid(row=0, column=0, sticky=(N,S,E,W))\n",
    "        self.selectbtn.grid(row=0,column=1,  sticky=(N,S,W,E))\n",
    "        self.canvas.get_tk_widget().grid(row=1, column=0, \n",
    "                                         rowspan=2, sticky=(N,S,E,W))\n",
    "        self.drawbtn.grid(row=1, column=1,sticky=(N,E,W))\n",
    "        self.savebtn.grid(row=2, column=1,sticky=(N,E,W))\n",
    "        self.quitbtn.grid(row=3, column=1,sticky=(S,E,W))\n",
    "        self.rowconfigure(2, weight=1)\n",
    "\n",
    "    def draw(self): # drawbtnが押された時の動作\n",
    "        fn=self.pltfnvar.get() # String Variable の現在の値をファイル名として使います。\n",
    "        if fn:\n",
    "            plt2png(fn, fig=self.fig) # self.figに`.plt`ファイルの中身を描画する。\n",
    "        else:\n",
    "            print(\"\\a\")\n",
    "        self.canvas.draw() # FigureにリンクしているCanvasウィジェットを更新する。\n",
    "        self.savebtn.state([\"!disabled\"]) # グラフを更新したので、保存できるようにする。\n",
    "    \n",
    "    def choosefile(self): # selectbtn が押された時の動作\n",
    "        infl=askopenfilename(filetypes=[(\"gnuplot\",\"plt\")]) #表示するデータファイル(.plt)をダイアログを使って選択する。\n",
    "        self.pltfnvar.set(infl) # 選択結果のStringVarに保存する。\n",
    "        self.fig.clf() #Figureをクリア\n",
    "        self.savebtn.state([\"disabled\"]) # グラフを更新するまで、保存できない。\n",
    "        if infl:\n",
    "            self.drawbtn.state([\"!disabled\"]) # ファイル名が決まったので、グラフを更新できる。\n",
    "        else:\n",
    "            self.drawbtn.state([\"disabled\"]) # ファイル名が決まったので、グラフを更新できる。\n",
    "        self.canvas.draw() #画面更新\n",
    "        \n",
    "    def savefig(self): # savebtnが押された時の動作\n",
    "        fn,ext=os.path.splitext(self.pltfnvar.get())\n",
    "        ofn=os.path.extsep.join([fn,\"png\"])\n",
    "        self.fig.savefig(ofn)\n",
    "        showinfo(message=\"Saved to {}.\".format(ofn),) # 保存したファイルの名前をダイアログに表示する。\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    root=tkinter.Tk()\n",
    "    app=Plt2Graph(root)\n",
    "    app.mainloop()     "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "54fd4985-283c-4f81-aa21-9f92df34ae4a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2022-02-09 17:13:40.282 Python[29678:3168401] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to /var/folders/3h/lqzhtkt17vv47gbtv470wcp40000gn/T/org.python.python.savedState\n"
     ]
    }
   ],
   "source": [
    "!python3 Plt2PngTk.py"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8aa028cc-4bff-463e-8909-714256d584db",
   "metadata": {
    "slideshow": {
     "slide_type": "notes"
    }
   },
   "source": [
    "#### 余談: macosでのtkinterの運命やいかに？\n",
    "macos 12のシステムに含まれているpython(/usr/bin/python)では、\n",
    "\n",
    "```\n",
    "Python 3.8.2 (default, Feb 25 2021, 09:38:33) \n",
    "[Clang 12.0.5 (clang-1205.0.22.6)] on darwin\n",
    "Type \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n",
    ">>> import tkinter\n",
    ">>> b=tkinter.Button()\n",
    "DEPRECATION WARNING: The system version of Tk is deprecated and may be removed in a future release. Please don't rely on it. Set TK_SILENCE_DEPRECATION=1 to suppress this warning.\n",
    ">>> 2021-12-08 12:36:44.446 Python[4866:46928] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to /var/folders/3h/lqzhtkt17vv47gbtv470wcp40000gn/T/com.apple.python3.savedState\n",
    "```\n",
    "\n",
    "とのメッセージが出力されます。Python.orgから別途インストールしたpython(/usr/local/bin/pythonなど）ではこのメッセージは現れません。しかし、Tkそのものがシステムからサポートされなくなった場合、こちらのバージョンでの動作が維持されるかどうかは、不明です。Tk本体もシステムとは別にインストールするなどの手間は増えることになるでしょう。\n",
    "\n",
    "そういえば、「AppleはmacOS 12 Montereyからスクリプト言語「PHP」のバンドルを終了するようです。」は実際に実行され、macos 12ではシステムからPHPはなくなりました。これに伴い、pukiwikiも、そのままでは、使えなくなってしまいました。　(現在はmoimoinを中心に使っているので、問題ないのですが。)\n",
    "\n",
    "Appleは Webkitを使っており、そのWebkitにはpythonインタフェースもあるらしいのです。しかし、macos上で動作するwebkit用のpythonモジュールが見つかっていません。もう少し色々調査が必要です。pythonでelectronを使ったアプリケーションをかけるといいいなとか思うのですが。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ba31bab0-0904-4984-abef-1a208f4cfedb",
   "metadata": {},
   "source": [
    "## matplotlib graph embedded in tk widgets.\n",
    "An example from the matplot home page."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "3d0e1f69-62b7-4b10-8207-9d252f799dd9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'module://matplotlib_inline.backend_inline'"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%matplotlib inline\n",
    "import matplotlib\n",
    "matplotlib.get_backend()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "567f2b8b-c45f-4550-8aa5-45f717182a62",
   "metadata": {},
   "outputs": [],
   "source": [
    "import tkinter\n",
    "\n",
    "from matplotlib.backends.backend_tkagg import (\n",
    "    FigureCanvasTkAgg, NavigationToolbar2Tk)\n",
    "# Implement the default Matplotlib key bindings.\n",
    "from matplotlib.backend_bases import key_press_handler\n",
    "from matplotlib.figure import Figure\n",
    "\n",
    "import numpy as np\n",
    "\n",
    "# setup Tk\n",
    "root = tkinter.Tk()\n",
    "root.wm_title(\"Embedding in Tk\")\n",
    "\n",
    "# setup figure with mpl\n",
    "fig = Figure(figsize=(5, 4), dpi=100)\n",
    "t = np.arange(0, 3, .01)\n",
    "ax = fig.add_subplot()\n",
    "line, = ax.plot(t, 2 * np.sin(2 * np.pi * t))\n",
    "ax.set_xlabel(\"time [s]\")\n",
    "ax.set_ylabel(\"f(t)\")\n",
    "\n",
    "canvas = FigureCanvasTkAgg(fig, master=root)  # A tk.DrawingArea.\n",
    "canvas.draw()\n",
    "\n",
    "# pack_toolbar=False will make it easier to use a layout manager later on.\n",
    "toolbar = NavigationToolbar2Tk(canvas, root, pack_toolbar=False)\n",
    "toolbar.update()\n",
    "\n",
    "# connect mpl event loop with tk event loop\n",
    "canvas.mpl_connect(\n",
    "    \"key_press_event\", lambda event: print(f\"you pressed {event.key}\"))\n",
    "canvas.mpl_connect(\"key_press_event\", key_press_handler)\n",
    "\n",
    "button_quit = tkinter.Button(master=root, text=\"Quit\", command=root.quit)\n",
    "\n",
    "def update_frequency(new_val):\n",
    "    # retrieve frequency\n",
    "    f = float(new_val)\n",
    "\n",
    "    # update data\n",
    "    y = 2 * np.sin(2 * np.pi * f * t)\n",
    "    line.set_data(t, y)\n",
    "\n",
    "    # required to update canvas and attached toolbar!\n",
    "    canvas.draw()\n",
    "\n",
    "\n",
    "slider_update = tkinter.Scale(root, \n",
    "                              from_=1, to=5, orient=tkinter.HORIZONTAL,\n",
    "                              command=update_frequency, label=\"Frequency [Hz]\")\n",
    "\n",
    "# Packing order is important. Widgets are processed sequentially and if there\n",
    "# is no space left, because the window is too small, they are not displayed.\n",
    "# The canvas is rather flexible in its size, so we pack it last which makes\n",
    "# sure the UI controls are displayed as long as possible.\n",
    "#button_quit.pack(side=tkinter.BOTTOM)\n",
    "#slider_update.pack(side=tkinter.BOTTOM)\n",
    "#toolbar.pack(side=tkinter.BOTTOM, fill=tkinter.X)\n",
    "#canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1)\n",
    "button_quit.grid(column=0,row=3,sticky=\"ew\")\n",
    "slider_update.grid(column=0,row=2,sticky=\"ew\")\n",
    "toolbar.grid(column=0,row=1,sticky=\"ew\")\n",
    "canvas.get_tk_widget().grid(column=0,row=0,sticky=\"nesw\")\n",
    "\n",
    "# tkinter.mainloop()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "c91b7a51-1f12-4a98-a307-9b73136d2301",
   "metadata": {},
   "outputs": [],
   "source": [
    "%gui tk\n",
    "import tkinter\n",
    "\n",
    "from matplotlib.backends.backend_tkagg import (\n",
    "    FigureCanvasTkAgg, NavigationToolbar2Tk)\n",
    "# Implement the default Matplotlib key bindings.\n",
    "from matplotlib.backend_bases import key_press_handler\n",
    "from matplotlib.figure import Figure\n",
    "\n",
    "import numpy as np\n",
    "\n",
    "# setup Tk\n",
    "root = tkinter.Tk()\n",
    "root.wm_title(\"Embedding in Tk\")\n",
    "\n",
    "\n",
    "# setup figure with mpl\n",
    "fig = Figure(figsize=(5, 4), dpi=100)\n",
    "t = np.arange(0, 3, .01)\n",
    "ax = fig.add_subplot()\n",
    "line, = ax.plot(t, 2 * np.sin(2 * np.pi * t))\n",
    "ax.set_xlabel(\"time [s]\")\n",
    "ax.set_ylabel(\"f(t)\")\n",
    "\n",
    "canvas = FigureCanvasTkAgg(fig, master=root)  # A tk.DrawingArea.\n",
    "canvas.draw()\n",
    "\n",
    "# pack_toolbar=False will make it easier to use a layout manager later on.\n",
    "toolbar = NavigationToolbar2Tk(canvas, root, pack_toolbar=False)\n",
    "toolbar.update()\n",
    "\n",
    "# connect mpl event loop with tk event loop\n",
    "canvas.mpl_connect(\n",
    "    \"key_press_event\", lambda event: print(f\"you pressed {event.key}\"))\n",
    "canvas.mpl_connect(\"key_press_event\", key_press_handler)\n",
    "\n",
    "button_quit = tkinter.Button(master=root, text=\"Quit\", command=root.quit)\n",
    "\n",
    "def update_frequency(new_val):\n",
    "    # retrieve frequency\n",
    "    f = float(new_val)\n",
    "\n",
    "    # update data\n",
    "    y = 2 * np.sin(2 * np.pi * f * t)\n",
    "    line.set_data(t, y)\n",
    "\n",
    "    # required to update canvas and attached toolbar!\n",
    "    canvas.draw()\n",
    "\n",
    "\n",
    "slider_update = tkinter.Scale(root, \n",
    "                              from_=1, to=5, orient=tkinter.HORIZONTAL,\n",
    "                              command=update_frequency, label=\"Frequency [Hz]\")\n",
    "\n",
    "# Packing order is important. Widgets are processed sequentially and if there\n",
    "# is no space left, because the window is too small, they are not displayed.\n",
    "# The canvas is rather flexible in its size, so we pack it last which makes\n",
    "# sure the UI controls are displayed as long as possible.\n",
    "button_quit.grid(column=1,row=2)\n",
    "slider_update.grid(column=0, row=2)\n",
    "toolbar.grid(column=0,row=1, columnspan=2)\n",
    "canvas.get_tk_widget().grid(\n",
    "    column=0,row=0,columnspan=2\n",
    ")\n",
    "\n",
    "# tkinter.mainloop()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "d08d428b-74e0-438d-b101-639eb9baf257",
   "metadata": {},
   "outputs": [],
   "source": [
    "%gui tk\n",
    "from tkinter import *\n",
    "from tkinter import ttk\n",
    "\n",
    "root = Tk()\n",
    "\n",
    "content = ttk.Frame(root)\n",
    "\n",
    "frame = ttk.Frame(content, borderwidth=5, relief=\"ridge\", width=200, height=100)\n",
    "namelbl = ttk.Label(content, text=\"Name\")\n",
    "name = ttk.Entry(content)\n",
    "\n",
    "onevar = BooleanVar(value=True)\n",
    "twovar = BooleanVar(value=False)\n",
    "threevar = BooleanVar(value=True)\n",
    "\n",
    "one = ttk.Checkbutton(content, text=\"One\", variable=onevar, onvalue=True)\n",
    "two = ttk.Checkbutton(content, text=\"Two\", variable=twovar, onvalue=True)\n",
    "three = ttk.Checkbutton(content, text=\"Three\", variable=threevar, onvalue=True)\n",
    "ok = ttk.Button(content, text=\"Okay\")\n",
    "cancel = ttk.Button(content, text=\"Cancel\")\n",
    "\n",
    "content.grid(column=0, row=0)\n",
    "frame.grid(column=0, row=0, columnspan=3, rowspan=2)\n",
    "namelbl.grid(column=3, row=0, columnspan=2)\n",
    "name.grid(column=3, row=1, columnspan=2)\n",
    "one.grid(column=0, row=3)\n",
    "two.grid(column=1, row=3)\n",
    "three.grid(column=2, row=3)\n",
    "ok.grid(column=3, row=3)\n",
    "cancel.grid(column=4, row=3)\n",
    "\n",
    "#root.mainloop()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4a80d421-8f9a-4711-86a0-70b6678b8ee7",
   "metadata": {},
   "source": [
    "### tkinterのcursor の名前\n",
    "\n",
    "https://www.tcl.tk/man/tcl/TkCmd/cursors.html\n",
    "\n",
    "`man n cursors`でも確認できます。\n",
    "\n",
    "### tkinter(Tcl/Tk)で使える色名\n",
    "\n",
    "https://www.tcl.tk/man/tcl/TkCmd/colors.html\n",
    "\n",
    "`man n colors`でも確認できます。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
