Tkinter • Beginner to Practical Project

Complete Lesson on Tkinter

This lesson starts from the absolute beginning. It explains what Tkinter is, how windows, labels, buttons, entries, text boxes, frames, tabs, and message boxes work, and then shows how to build a real GUI that calls your stock-analysis and demo-trading functions from function.py.

Starts at zero Step-by-step Real project included Uses your existing functions

1. What is Tkinter?

Tkinter is Python’s built-in library for making desktop GUI applications. GUI means Graphical User Interface. A GUI program has visible elements such as:

  • windows
  • buttons
  • labels
  • text boxes
  • menus
  • dialog boxes
  • tabs

Without Tkinter, many beginner Python programs run only in the terminal. With Tkinter, the user can click buttons, type in text boxes, and interact with a window.

Tkinter is excellent when you want to turn Python logic into a small desktop tool.

2. Why Tkinter is good for beginners

Simple to start

In most Python installations, Tkinter is already available. That means you can begin building windows immediately.

Teaches core GUI ideas

You learn windows, widgets, layout, events, and callbacks. These are fundamental ideas used in many GUI frameworks.

Good for tools

Tkinter is perfect for calculators, data entry forms, small dashboards, file tools, and desktop demos.

Works well with Python code

You can connect Tkinter to your existing functions, classes, and business logic.

3. Your first Tkinter program

This is the smallest useful Tkinter program:

import tkinter as tk

root = tk.Tk()
root.title("My First Tkinter App")
root.geometry("400x200")

label = tk.Label(root, text="Hello, Tkinter!")
label.pack()

root.mainloop()

Line-by-line explanation

Code Meaning
import tkinter as tk Imports the Tkinter library and gives it the short name tk.
root = tk.Tk() Creates the main application window.
root.title(...) Sets the text shown in the window title bar.
root.geometry("400x200") Sets the width and height of the window.
tk.Label(...) Creates a label widget that shows text.
pack() Places the label inside the window.
root.mainloop() Starts the app and keeps the window open until the user closes it.
Important: without mainloop(), the window appears and closes immediately.

4. Important widgets in Tkinter

A widget is a visible GUI element. Here are the most common ones:

Widget Purpose
Label Shows text
Button Runs a function when clicked
Entry Single-line text input
Text Multi-line text area
Frame Container for grouping widgets
LabelFrame Frame with a visible title
Notebook Tabbed interface
ScrolledText Text area with scrollbar

5. Buttons and functions

Buttons usually call a Python function.

import tkinter as tk

def say_hello():
    print("Hello!")

root = tk.Tk()
root.title("Button Example")

button = tk.Button(root, text="Click Me", command=say_hello)
button.pack(pady=20)

root.mainloop()

Key idea

In command=say_hello, we pass the function name without parentheses.

# Correct
command=say_hello

# Wrong
command=say_hello()

If you write parentheses, the function runs immediately when the button is created.

6. Getting user input with Entry

The Entry widget takes single-line text input.

import tkinter as tk

def show_name():
    name = entry.get()
    print("Name:", name)

root = tk.Tk()

entry = tk.Entry(root)
entry.pack()

button = tk.Button(root, text="Show", command=show_name)
button.pack()

root.mainloop()

Important method

entry.get() reads whatever the user typed in the box.

7. Multi-line output with Text and ScrolledText

For larger output such as reports, logs, transaction history, and file paths, use a text area.

import tkinter as tk

root = tk.Tk()

text_box = tk.Text(root, height=10, width=40)
text_box.pack()

text_box.insert("end", "Hello from Tkinter!\n")
text_box.insert("end", "This is a text area.")

root.mainloop()

Useful methods

  • insert("end", text) adds text
  • delete("1.0", "end") clears all text
  • get("1.0", "end") reads all text

For larger apps, ScrolledText is even better because it adds scrolling automatically.

8. Layout managers: pack, grid, and place

Tkinter has three main ways to position widgets:

pack()

Simple and quick. Good for basic vertical or horizontal layouts.

grid()

Best for forms. Widgets are arranged in rows and columns.

import tkinter as tk

root = tk.Tk()

tk.Label(root, text="Name").grid(row=0, column=0)
tk.Entry(root).grid(row=0, column=1)

tk.Label(root, text="Age").grid(row=1, column=0)
tk.Entry(root).grid(row=1, column=1)

root.mainloop()

place()

Places widgets at exact x-y coordinates. Usually less flexible for responsive layouts.

In forms and dashboards, grid() is often the best choice.

9. Frames and grouping widgets

A Frame is a container. It helps organize large windows into sections.

import tkinter as tk

root = tk.Tk()

top_frame = tk.Frame(root)
top_frame.pack()

bottom_frame = tk.Frame(root)
bottom_frame.pack()

tk.Label(top_frame, text="Top section").pack()
tk.Button(bottom_frame, text="Bottom button").pack()

root.mainloop()

In larger apps, frames make it easier to separate different areas such as:

  • input form
  • buttons section
  • report section
  • charts section

10. Message boxes

Message boxes show popup messages to the user.

import tkinter as tk
from tkinter import messagebox

def show_popup():
    messagebox.showinfo("Info", "This is a popup")

root = tk.Tk()
tk.Button(root, text="Show Popup", command=show_popup).pack()
root.mainloop()

Common popup types

  • messagebox.showinfo()
  • messagebox.showwarning()
  • messagebox.showerror()

In real apps, they are useful for showing success messages, input mistakes, and error details.

11. Event-driven programming

Tkinter programs are event-driven. That means the program waits for events such as:

  • button click
  • mouse action
  • key press
  • window close

When the event happens, Tkinter calls the related function. This is different from simple terminal programs that run top to bottom and end.

In a GUI app, the user controls the flow by interacting with the interface.

12. Using Tkinter with your stock-analysis functions

You already built logic in function.py for:

  • analyze_share(...)
  • analyze_two_shares(...)
  • DemoTradingApp

Tkinter becomes the front-end for those functions.

What the GUI will do

  • user enters share ticker and time period into input boxes
  • user clicks a button
  • your Python function runs
  • output paths and reports are shown in a text area
  • popups show success or error messages

Why this is powerful

Your business logic stays in one file, and your interface stays in another. This is a clean and professional way to design software.

13. Complete Tkinter GUI code

The following file can be saved as tkinter_app.py. It uses the functions from your existing function.py.

import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext

from function import analyze_share, analyze_two_shares, DemoTradingApp


class StockAppGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("Demo Share Analysis and Trading App")
        self.root.geometry("1100x750")

        self.trading_app = DemoTradingApp()

        self.create_widgets()

    def create_widgets(self):
        title = tk.Label(
            self.root,
            text="Demo Share Analysis and Trading App",
            font=("Arial", 18, "bold")
        )
        title.pack(pady=10)

        notebook = ttk.Notebook(self.root)
        notebook.pack(fill="both", expand=True, padx=10, pady=10)

        self.analysis_tab = tk.Frame(notebook)
        self.compare_tab = tk.Frame(notebook)
        self.trading_tab = tk.Frame(notebook)
        self.output_tab = tk.Frame(notebook)

        notebook.add(self.analysis_tab, text="Analyze One Share")
        notebook.add(self.compare_tab, text="Compare Two Shares")
        notebook.add(self.trading_tab, text="Demo Trading")
        notebook.add(self.output_tab, text="Output")

        self.build_analysis_tab()
        self.build_compare_tab()
        self.build_trading_tab()
        self.build_output_tab()

    def build_analysis_tab(self):
        frame = self.analysis_tab

        tk.Label(frame, text="Share Ticker").grid(row=0, column=0, padx=10, pady=10, sticky="w")
        self.share_entry = tk.Entry(frame, width=30)
        self.share_entry.insert(0, "RELIANCE.NS")
        self.share_entry.grid(row=0, column=1, padx=10, pady=10)

        tk.Label(frame, text="Period").grid(row=1, column=0, padx=10, pady=10, sticky="w")
        self.period_entry = tk.Entry(frame, width=30)
        self.period_entry.insert(0, "1mo")
        self.period_entry.grid(row=1, column=1, padx=10, pady=10)

        tk.Label(frame, text="Interval").grid(row=2, column=0, padx=10, pady=10, sticky="w")
        self.interval_entry = tk.Entry(frame, width=30)
        self.interval_entry.insert(0, "1d")
        self.interval_entry.grid(row=2, column=1, padx=10, pady=10)

        tk.Label(frame, text="Output Folder").grid(row=3, column=0, padx=10, pady=10, sticky="w")
        self.output_dir_entry = tk.Entry(frame, width=30)
        self.output_dir_entry.insert(0, "stock_output")
        self.output_dir_entry.grid(row=3, column=1, padx=10, pady=10)

        tk.Button(
            frame,
            text="Analyze Share",
            command=self.run_analyze_share,
            width=20
        ).grid(row=4, column=0, columnspan=2, pady=20)

    def build_compare_tab(self):
        frame = self.compare_tab

        tk.Label(frame, text="First Share").grid(row=0, column=0, padx=10, pady=10, sticky="w")
        self.share1_entry = tk.Entry(frame, width=30)
        self.share1_entry.insert(0, "RELIANCE.NS")
        self.share1_entry.grid(row=0, column=1, padx=10, pady=10)

        tk.Label(frame, text="Second Share").grid(row=1, column=0, padx=10, pady=10, sticky="w")
        self.share2_entry = tk.Entry(frame, width=30)
        self.share2_entry.insert(0, "TCS.NS")
        self.share2_entry.grid(row=1, column=1, padx=10, pady=10)

        tk.Label(frame, text="Period").grid(row=2, column=0, padx=10, pady=10, sticky="w")
        self.compare_period_entry = tk.Entry(frame, width=30)
        self.compare_period_entry.insert(0, "1mo")
        self.compare_period_entry.grid(row=2, column=1, padx=10, pady=10)

        tk.Label(frame, text="Interval").grid(row=3, column=0, padx=10, pady=10, sticky="w")
        self.compare_interval_entry = tk.Entry(frame, width=30)
        self.compare_interval_entry.insert(0, "1d")
        self.compare_interval_entry.grid(row=3, column=1, padx=10, pady=10)

        tk.Label(frame, text="Output Folder").grid(row=4, column=0, padx=10, pady=10, sticky="w")
        self.compare_output_dir_entry = tk.Entry(frame, width=30)
        self.compare_output_dir_entry.insert(0, "stock_compare_output")
        self.compare_output_dir_entry.grid(row=4, column=1, padx=10, pady=10)

        tk.Button(
            frame,
            text="Compare Shares",
            command=self.run_compare_shares,
            width=20
        ).grid(row=5, column=0, columnspan=2, pady=20)

    def build_trading_tab(self):
        frame = self.trading_tab

        left = tk.LabelFrame(frame, text="User Management", padx=10, pady=10)
        left.grid(row=0, column=0, padx=10, pady=10, sticky="n")

        tk.Label(left, text="Username").grid(row=0, column=0, padx=5, pady=5, sticky="w")
        self.username_entry = tk.Entry(left, width=25)
        self.username_entry.grid(row=0, column=1, padx=5, pady=5)

        tk.Label(left, text="Starting Cash / Deposit").grid(row=1, column=0, padx=5, pady=5, sticky="w")
        self.cash_entry = tk.Entry(left, width=25)
        self.cash_entry.insert(0, "100000")
        self.cash_entry.grid(row=1, column=1, padx=5, pady=5)

        tk.Button(left, text="Create User", command=self.create_user, width=18).grid(row=2, column=0, pady=10)
        tk.Button(left, text="Deposit Cash", command=self.deposit_cash, width=18).grid(row=2, column=1, pady=10)

        middle = tk.LabelFrame(frame, text="Trading", padx=10, pady=10)
        middle.grid(row=0, column=1, padx=10, pady=10, sticky="n")

        tk.Label(middle, text="Ticker").grid(row=0, column=0, padx=5, pady=5, sticky="w")
        self.trade_ticker_entry = tk.Entry(middle, width=25)
        self.trade_ticker_entry.insert(0, "RELIANCE.NS")
        self.trade_ticker_entry.grid(row=0, column=1, padx=5, pady=5)

        tk.Label(middle, text="Quantity").grid(row=1, column=0, padx=5, pady=5, sticky="w")
        self.quantity_entry = tk.Entry(middle, width=25)
        self.quantity_entry.insert(0, "1")
        self.quantity_entry.grid(row=1, column=1, padx=5, pady=5)

        tk.Button(middle, text="Buy Share", command=self.buy_share, width=18).grid(row=2, column=0, pady=10)
        tk.Button(middle, text="Sell Share", command=self.sell_share, width=18).grid(row=2, column=1, pady=10)

        right = tk.LabelFrame(frame, text="Reports", padx=10, pady=10)
        right.grid(row=0, column=2, padx=10, pady=10, sticky="n")

        tk.Button(right, text="Portfolio Report", command=self.portfolio_report, width=18).grid(row=0, column=0, pady=10)
        tk.Button(right, text="Transactions", command=self.show_transactions, width=18).grid(row=1, column=0, pady=10)
        tk.Button(right, text="Market Snapshot", command=self.market_snapshot, width=18).grid(row=2, column=0, pady=10)

    def build_output_tab(self):
        self.output_text = scrolledtext.ScrolledText(self.output_tab, wrap=tk.WORD, font=("Courier New", 10))
        self.output_text.pack(fill="both", expand=True, padx=10, pady=10)

    def write_output(self, text):
        self.output_text.insert(tk.END, text + "\n")
        self.output_text.see(tk.END)

    def clear_output(self):
        self.output_text.delete("1.0", tk.END)

    def run_analyze_share(self):
        try:
            self.clear_output()
            share = self.share_entry.get().strip()
            period = self.period_entry.get().strip()
            interval = self.interval_entry.get().strip()
            output_dir = self.output_dir_entry.get().strip()

            result = analyze_share(
                share_name=share,
                period=period,
                interval=interval,
                output_dir=output_dir
            )

            self.write_output(f"Analysis completed for {share}")
            self.write_output(f"Raw CSV: {result['raw_csv']}")
            self.write_output(f"Raw JSON: {result['raw_json']}")
            self.write_output(f"Statistics CSV: {result['stats_csv']}")
            self.write_output(f"Statistics JSON: {result['stats_json']}")
            self.write_output("Charts:")
            for file_path in result["chart_files"]:
                self.write_output(file_path)

            messagebox.showinfo("Success", f"Analysis completed for {share}")

        except Exception as e:
            messagebox.showerror("Error", str(e))
            self.write_output(f"Error: {e}")

    def run_compare_shares(self):
        try:
            self.clear_output()
            share1 = self.share1_entry.get().strip()
            share2 = self.share2_entry.get().strip()
            period = self.compare_period_entry.get().strip()
            interval = self.compare_interval_entry.get().strip()
            output_dir = self.compare_output_dir_entry.get().strip()

            result = analyze_two_shares(
                share1=share1,
                share2=share2,
                period=period,
                interval=interval,
                output_dir=output_dir
            )

            self.write_output(f"Comparison completed: {share1} vs {share2}")
            self.write_output(f"Statistics CSV: {result['statistics_csv']}")
            self.write_output(f"Statistics JSON: {result['statistics_json']}")
            self.write_output(f"Comparison CSV: {result['comparison_csv']}")
            self.write_output(f"Comparison JSON: {result['comparison_json']}")
            self.write_output("Charts:")
            for file_path in result["chart_files"]:
                self.write_output(file_path)

            messagebox.showinfo("Success", f"Comparison completed: {share1} vs {share2}")

        except Exception as e:
            messagebox.showerror("Error", str(e))
            self.write_output(f"Error: {e}")

    def create_user(self):
        try:
            username = self.username_entry.get().strip()
            cash = float(self.cash_entry.get().strip())
            self.trading_app.create_user(username, cash)
            self.write_output(f"User created: {username} with cash {cash}")
            messagebox.showinfo("Success", f"User {username} created")
        except Exception as e:
            messagebox.showerror("Error", str(e))
            self.write_output(f"Error: {e}")

    def deposit_cash(self):
        try:
            username = self.username_entry.get().strip()
            amount = float(self.cash_entry.get().strip())
            self.trading_app.deposit_cash(username, amount)
            self.write_output(f"Deposited {amount} into {username}")
            messagebox.showinfo("Success", "Cash deposited")
        except Exception as e:
            messagebox.showerror("Error", str(e))
            self.write_output(f"Error: {e}")

    def buy_share(self):
        try:
            username = self.username_entry.get().strip()
            ticker = self.trade_ticker_entry.get().strip()
            quantity = int(self.quantity_entry.get().strip())
            self.trading_app.buy_share(username, ticker, quantity)
            self.write_output(f"{username} bought {quantity} of {ticker}")
            messagebox.showinfo("Success", "Buy completed")
        except Exception as e:
            messagebox.showerror("Error", str(e))
            self.write_output(f"Error: {e}")

    def sell_share(self):
        try:
            username = self.username_entry.get().strip()
            ticker = self.trade_ticker_entry.get().strip()
            quantity = int(self.quantity_entry.get().strip())
            self.trading_app.sell_share(username, ticker, quantity)
            self.write_output(f"{username} sold {quantity} of {ticker}")
            messagebox.showinfo("Success", "Sell completed")
        except Exception as e:
            messagebox.showerror("Error", str(e))
            self.write_output(f"Error: {e}")

    def portfolio_report(self):
        try:
            username = self.username_entry.get().strip()
            report = self.trading_app.get_portfolio_report(username)

            self.clear_output()
            self.write_output(f"Portfolio Report for {username}")
            self.write_output(f"Cash: {report['cash']}")
            self.write_output("Holdings:")
            for item in report["holdings"]:
                self.write_output(str(item))
            self.write_output(f"Total Market Value: {report['total_market_value']}")
            self.write_output(f"Total Portfolio Value: {report['total_portfolio_value']}")
            self.write_output(f"Total PnL: {report['total_pnl']}")

            messagebox.showinfo("Success", "Portfolio report generated")
        except Exception as e:
            messagebox.showerror("Error", str(e))
            self.write_output(f"Error: {e}")

    def show_transactions(self):
        try:
            username = self.username_entry.get().strip()
            transactions = self.trading_app.get_user_transactions(username)

            self.clear_output()
            self.write_output(f"Transactions for {username}")
            for txn in transactions:
                self.write_output(str(txn))

            messagebox.showinfo("Success", "Transactions loaded")
        except Exception as e:
            messagebox.showerror("Error", str(e))
            self.write_output(f"Error: {e}")

    def market_snapshot(self):
        try:
            ticker = self.trade_ticker_entry.get().strip()
            result = self.trading_app.save_market_snapshot(ticker, "1mo", "1d")
            self.write_output(f"Market snapshot saved for {ticker}")
            self.write_output(f"Raw JSON: {result['raw_json']}")
            self.write_output(f"Statistics JSON: {result['stats_json']}")
            messagebox.showinfo("Success", "Market snapshot saved")
        except Exception as e:
            messagebox.showerror("Error", str(e))
            self.write_output(f"Error: {e}")


if __name__ == "__main__":
    root = tk.Tk()
    app = StockAppGUI(root)
    root.mainloop()

14. Explanation of the full app

The class

The GUI is wrapped inside a class called StockAppGUI. This keeps the code organized. It stores all widgets and methods together.

The constructor

def __init__(self, root):
    self.root = root
    self.root.title("Demo Share Analysis and Trading App")
    self.root.geometry("1100x750")

    self.trading_app = DemoTradingApp()

    self.create_widgets()

This runs when the GUI object is created. It stores the root window, creates the trading app object, and builds the widgets.

Notebook tabs

The GUI uses ttk.Notebook to create tabs:

  • Analyze One Share
  • Compare Two Shares
  • Demo Trading
  • Output

Entry widgets

Every input field is an Entry widget. Example:

self.share_entry = tk.Entry(frame, width=30)

Button callbacks

Buttons call class methods. Example:

tk.Button(frame, text="Analyze Share", command=self.run_analyze_share)

Writing output

The output tab uses a scrolling text area:

self.output_text = scrolledtext.ScrolledText(...)

Then this helper method writes text into it:

def write_output(self, text):
    self.output_text.insert(tk.END, text + "\n")
    self.output_text.see(tk.END)

Error handling

Every action uses try/except. If an error happens, the app shows:

messagebox.showerror("Error", str(e))

This prevents the program from crashing badly for the user.

15. How to run the project

Step 1: install required packages

pip install yfinance pandas numpy matplotlib

Step 2: keep these files together

  • function.py
  • tkinter_app.py

Step 3: run the GUI

python tkinter_app.py

What happens next?

  • a window opens
  • you can analyze one share
  • you can compare two shares
  • you can create a demo trading user
  • you can buy and sell shares in the demo system
  • you can generate reports and market snapshots

16. Final summary

In this lesson, you learned:

  • what Tkinter is
  • how to create a window
  • how labels, buttons, entries, and text areas work
  • how layout managers position widgets
  • how popups and events work
  • how to build a tabbed desktop interface
  • how to connect Tkinter to your own Python functions
  • how to build a complete GUI for stock analysis and demo share trading
The biggest lesson is this: Tkinter is the interface, and your Python functions are the engine. When you combine both, you can build real desktop applications.

Suggested next upgrades

  • show charts inside the GUI
  • use ttk.Treeview for portfolio tables
  • add a file chooser for output folders
  • add theme switching
  • add dropdowns for common stock symbols
  • add form validation before running actions