{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "5e39eb83",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Graphical User Interfaces\n",
    "#### CS 65: Introduction to Computer Science I"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d815a429",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Graphical User Interfaces in Python\n",
    "\n",
    "A __Graphical User Interface (GUI)__ is a means for a user to interact with a program which uses graphical components like windows, buttons, and other components that are manipulated with a mouse or touch-input device.\n",
    "\n",
    "There are several different Python packages that provide the ability to build GUIs (some are designed for desktop/laptop apps, mobile, web, etc.)\n",
    "\n",
    "`tkinter` is a one of the most popular GUI toolkits, and it works on standard Windows, Mac, and Linux computers."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4580ec31",
   "metadata": {},
   "outputs": [],
   "source": [
    "import tkinter\n",
    "\n",
    "class MinimalApp:\n",
    "    \n",
    "    def __init__(self):\n",
    "        \n",
    "        #create the main window\n",
    "        self.main_window = tkinter.Tk()\n",
    "        \n",
    "        #enter the \"main loop\" to launch the window and interact with the user\n",
    "        tkinter.mainloop()\n",
    "        \n",
    "my_gui = MinimalApp()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5726ad53",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## `tkinter` widgets\n",
    "We’ll look at code for using the following tkinter _widgets_ - GUI objects\n",
    "\n",
    "* __Label:__ for displaying text to the user\n",
    "* __Button:__ for triggering code when the user clicks it \n",
    "* __Entry:__ a box that lets the user type short input\n",
    "\n",
    "There are two things to do with every widget\n",
    "* create a new attribute for it in your class\n",
    "* call the widget's `pack()` method - which figures out where to put it and makes it visible"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5bec13ed",
   "metadata": {},
   "outputs": [],
   "source": [
    "import tkinter\n",
    "\n",
    "class HelloApp:\n",
    "    \n",
    "    def __init__(self):\n",
    "        \n",
    "        #create the main window\n",
    "        self.main_window = tkinter.Tk()\n",
    "        \n",
    "        #create an attribute for our label\n",
    "        self.hello_label = tkinter.Label(self.main_window, text=\"Hello world!\")\n",
    "        \n",
    "        #pack the label\n",
    "        self.hello_label.pack()\n",
    "        \n",
    "        #enter the \"main loop\" to launch the window and interact with the user\n",
    "        tkinter.mainloop()\n",
    "        \n",
    "my_gui = HelloApp()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c9d79b5c",
   "metadata": {},
   "source": [
    "We pass two arguments when creating the `tkinter.Label` object\n",
    "* the main window - the parent widget this widget will appear in\n",
    "* an optional argument, `text=\"Hello world!\"` - the text that will appear on the label"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "292ecf1d",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## A GUI with multiple labels\n",
    "\n",
    "You can include as many labels as you want (and any number of other widgets)\n",
    "\n",
    "Make a new attribute for each label\n",
    "\n",
    "The labels will appear in the order they're packed\n",
    "\n",
    "Here's an example with two labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fade515d",
   "metadata": {},
   "outputs": [],
   "source": [
    "import tkinter\n",
    "\n",
    "class MultiLabelApp:\n",
    "    \n",
    "    def __init__(self):\n",
    "        \n",
    "        #create the main window\n",
    "        self.main_window = tkinter.Tk()\n",
    "        \n",
    "        #create attributes for our label\n",
    "        self.hello_label = tkinter.Label(self.main_window, text=\"Hello world!\")\n",
    "        self.description_label = tkinter.Label(self.main_window, text=\"This is my GUI program.\")\n",
    "        \n",
    "        #pack the labels\n",
    "        self.hello_label.pack()\n",
    "        self.description_label.pack()\n",
    "        \n",
    "        \n",
    "        #enter the \"main loop\" to launch the window and interact with the user\n",
    "        tkinter.mainloop()\n",
    "        \n",
    "my_gui = MultiLabelApp()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1151a6a",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "## Group Exercise\n",
    "\n",
    "Searching for help on how to do things with `tkinter` can sometimes be difficult because there are different coding styles people use with it. However, it can be done.\n",
    "\n",
    "We want to do the following things\n",
    "1. set the title of the window so it is something other than the default `tk` value\n",
    "2. change the initial size of the window to be something a little bit bigger\n",
    "\n",
    "Do some Internet searching and see if you can figure out how to do this with the `MultiLabelApp` given above."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "82dc2e00",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## `tkinter` buttons\n",
    "\n",
    "Creating a button widget in `tkinter` is similar to creating a label, except you have to set it up with an extra argument: the name of a function that should run when the button is clicked. This function is the __callback function__."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7fd5ddc0",
   "metadata": {},
   "outputs": [],
   "source": [
    "self.my_button = tkinter.Button(self.main_window,\n",
    "            text=\"This appears on the button\",command=name_of_a_function)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1bdf542",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## A button example in context"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "07cbae5b",
   "metadata": {},
   "outputs": [],
   "source": [
    "import tkinter\n",
    "\n",
    "\n",
    "class SimpleButtonApp:\n",
    "    def __init__(self):\n",
    "        # Create the main window widget.\n",
    "        self.main_window = tkinter.Tk()\n",
    "\n",
    "        # Create a Button widget. The text 'Click Me!'\n",
    "        # should appear on the face of the Button. The\n",
    "        # do_something method should be executed when\n",
    "        # the user clicks the Button.\n",
    "        self.my_button = tkinter.Button(self.main_window,\n",
    "            text='Click Me!',command=self.do_something)\n",
    "        self.my_label = tkinter.Label(self.main_window,text=\"No click yet.\")\n",
    "\n",
    "        # Pack the Button.\n",
    "        self.my_button.pack()\n",
    "        self.my_label.pack()\n",
    "\n",
    "        # Enter the tkinter main loop.\n",
    "        tkinter.mainloop()\n",
    "\n",
    "    # The do_something method is a callback function\n",
    "    # for the Button widget.\n",
    "\n",
    "    def do_something(self):\n",
    "        #the label's config method lets you change the text on the label\n",
    "        self.my_label.config(text=\"Good job! You clicked the button.\")\n",
    "\n",
    "\n",
    "# Create an instance of the MyGUI class.\n",
    "my_gui = SimpleButtonApp()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e0a72627",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "#### Things to notice about this\n",
    "* the `command` is set to `self.do_something`, which is a method we define in the same class\n",
    "* when we pass the name of the function, we don't include parentheses - it's _not_ `command=self.do_something()`\n",
    "* the `self.do_something()` function makes a change to the `my_label` attribute using the label'ss `config` method"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aaf9016f",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "## Example: Turning the Magic 8 ball code into a GUI\n",
    "\n",
    "We previously wrote a function like this which simulated a Magic 8 Ball. Let's look at how we would code this up as a GUI."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7c76173b",
   "metadata": {},
   "outputs": [],
   "source": [
    "import random\n",
    "\n",
    "def magic_8_ball():\n",
    "    rand_val = random.randint(1,3)\n",
    "    \n",
    "    if rand_val == 1:\n",
    "        print(\"It is Certain.\")\n",
    "    elif rand_val == 2:\n",
    "        print(\"Don't count on it.\")\n",
    "    else:\n",
    "        print(\"Reply hazy, try again.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2b23ba22",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "551ad77c",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "outputs": [],
   "source": [
    "import tkinter\n",
    "import random\n",
    "\n",
    "class Magic8BallApp:\n",
    "    \n",
    "    def __init__(self):\n",
    "        \n",
    "        self.main_window = tkinter.Tk()\n",
    "        \n",
    "        self.question_button = tkinter.Button(self.main_window,text=\"Press Me\",command=self.generate_response)\n",
    "        self.answer_label = tkinter.Label(self.main_window,text=\"Ask the 8 Ball a Question!\")\n",
    "        \n",
    "        self.question_button.pack()\n",
    "        self.answer_label.pack()\n",
    "        tkinter.mainloop()\n",
    "        \n",
    "    def generate_response(self):\n",
    "        rand_val = random.randint(1,3)\n",
    "        if rand_val == 1:\n",
    "            self.answer_label.config(text=\"It is certain.\")\n",
    "        elif rand_val ==2:\n",
    "            self.answer_label.config(text=\"The answer is No.\")\n",
    "        else:\n",
    "            self.answer_label.config(text=\"Reply hazy, try again.\")\n",
    "        \n",
    "        \n",
    "running_instance = Magic8BallApp()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4dabdc6",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Supporting multiple buttons\n",
    "\n",
    "If you have multiple buttons, make sure to define multiple functions to handle their actions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "69e72e58",
   "metadata": {},
   "outputs": [],
   "source": [
    "import tkinter\n",
    "\n",
    "\n",
    "class MultiButtonApp:\n",
    "    def __init__(self):\n",
    "        # Create the main window widget.\n",
    "        self.main_window = tkinter.Tk()\n",
    "        \n",
    "        #Initialize the widgets\n",
    "        self.question_label = tkinter.Label(self.main_window,text=\"Did you have a good day?\")\n",
    "\n",
    "        self.yes_button = tkinter.Button(self.main_window,\n",
    "            text='Yes',command=self.yes_response)\n",
    "        \n",
    "        self.no_button = tkinter.Button(self.main_window,\n",
    "            text='No',command=self.no_response)\n",
    "        \n",
    "        #leaving this empty initially, but we'll provide output to it in the callback functions\n",
    "        self.reaction_label = tkinter.Label(self.main_window,text=\"\")\n",
    "        \n",
    "\n",
    "        # Pack the widgets\n",
    "        self.question_label.pack()\n",
    "        self.yes_button.pack()\n",
    "        self.no_button.pack()\n",
    "        self.reaction_label.pack()\n",
    "\n",
    "        # Enter the tkinter main loop.\n",
    "        tkinter.mainloop()\n",
    "\n",
    "    #callback function for responding to a \"Yes\" button click\n",
    "    def yes_response(self):\n",
    "        self.reaction_label.config(text=\"Hooray! That's great.\")\n",
    "        \n",
    "    #callback function for responding to a \"No\" button click\n",
    "    def no_response(self):\n",
    "        self.reaction_label.config(text=\"Too bad, maybe tomorrow will be better.\")\n",
    "\n",
    "\n",
    "# Create an instance of the MyGUI class.\n",
    "my_gui = MultiButtonApp()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a4db923f",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Group Exercise\n",
    "\n",
    "Create an application that has multiple buttons and multiple labels."
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Slideshow",
  "kernelspec": {
   "display_name": "Python 3",
   "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.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
