Bỏ qua

Image

GIAO DIỆN ĐỒ HỌA VỚI TKINTER (PHẦN 1)

1. Giới thiệu về Tkinter

1.1. Tkinter là gì?

  • Tkinter (Tk interface) là thư viện GUI (Graphical User Interface) có sẵn trong Python
  • Cho phép tạo các ứng dụng giao diện đồ họa với cửa sổ, nút bấm, hộp văn bản...
  • Đơn giản, dễ học và không cần cài đặt thêm

1.2. Tại sao học Tkinter?

  • Được tích hợp sẵn trong Python
  • Dễ học cho người mới bắt đầu
  • Tạo được các ứng dụng desktop đơn giản
  • Nền tảng để hiểu các thư viện GUI khác

1.3. Cấu trúc cơ bản của ứng dụng Tkinter

import tkinter as tk

# Tạo cửa sổ chính
window = tk.Tk()
window.title("Tiêu đề cửa sổ")
window.geometry("400x300")

# Thêm các widget vào đây

# Chạy vòng lặp chính
window.mainloop()

2. Tạo cửa sổ chính (Window)

2.1. Tạo cửa sổ cơ bản

import tkinter as tk

# Tạo cửa sổ
window = tk.Tk()
window.title("Cửa sổ đầu tiên của tôi")
window.geometry("500x400")  # rộng x cao
window.resizable(True, True)  # cho phép thay đổi kích thước

# Hiển thị cửa sổ
window.mainloop()

2.2. Các thuộc tính của cửa sổ

import tkinter as tk

window = tk.Tk()
window.title("Ứng dụng quản lý học sinh")
window.geometry("600x500")
window.configure(bg="lightblue")  # màu nền
window.resizable(False, False)    # không cho thay đổi kích thước

# Đặt vị trí cửa sổ ở giữa màn hình
window.eval('tk::PlaceWindow . center')

window.mainloop()

3. Widget Label - Hiển thị văn bản

3.1. Label cơ bản

import tkinter as tk

window = tk.Tk()
window.title("Label Demo")
window.geometry("400x200")

# Tạo label
label1 = tk.Label(window, text="Xin chào các bạn!")
label1.pack()  # Đặt label vào cửa sổ

label2 = tk.Label(window, text="Đây là bài học về Tkinter", 
                  font=("Arial", 14))
label2.pack()

window.mainloop()

3.2. Label với nhiều thuộc tính

import tkinter as tk

window = tk.Tk()
window.title("Label nâng cao")
window.geometry("500x300")

# Label với màu sắc và font
label_title = tk.Label(window, 
                       text="CHƯƠNG TRÌNH QUẢN LÝ ĐIỂM",
                       font=("Times New Roman", 18, "bold"),
                       fg="blue",           # màu chữ
                       bg="yellow",         # màu nền
                       padx=20, pady=10)    # khoảng cách padding
label_title.pack()

# Label thông tin
label_info = tk.Label(window,
                      text="Nhập điểm các môn học của bạn:",
                      font=("Arial", 12),
                      fg="darkgreen")
label_info.pack(pady=20)

window.mainloop()

4. Widget Button - Nút bấm

4.1. Button cơ bản

import tkinter as tk

def say_hello():
    print("Xin chào!")

window = tk.Tk()
window.title("Button Demo")
window.geometry("300x200")

# Tạo button
button1 = tk.Button(window, text="Click me!", command=say_hello)
button1.pack(pady=20)

window.mainloop()

4.2. Button với nhiều chức năng

import tkinter as tk

def chao_buoi_sang():
    print("Chào buổi sáng!")

def chao_buoi_chieu():
    print("Chào buổi chiều!")

def thoat_chuong_trinh():
    print("Tạm biệt!")
    window.quit()

window = tk.Tk()
window.title("Các nút chức năng")
window.geometry("300x250")

# Các button khác nhau
btn_sang = tk.Button(window, text="Chào buổi sáng", 
                     command=chao_buoi_sang,
                     bg="lightblue", font=("Arial", 10))
btn_sang.pack(pady=10)

btn_chieu = tk.Button(window, text="Chào buổi chiều",
                      command=chao_buoi_chieu,
                      bg="lightgreen", font=("Arial", 10))
btn_chieu.pack(pady=10)

btn_thoat = tk.Button(window, text="Thoát",
                      command=thoat_chuong_trinh,
                      bg="red", fg="white", font=("Arial", 10, "bold"))
btn_thoat.pack(pady=10)

window.mainloop()

5. Widget Entry - Hộp nhập liệu

5.1. Entry cơ bản

import tkinter as tk

window = tk.Tk()
window.title("Entry Demo")
window.geometry("400x200")

# Label hướng dẫn
label = tk.Label(window, text="Nhập tên của bạn:")
label.pack(pady=10)

# Entry box
entry = tk.Entry(window, font=("Arial", 12))
entry.pack(pady=10)

def lay_ten():
    ten = entry.get()  # Lấy giá trị từ entry
    print(f"Tên bạn là: {ten}")

# Button để lấy dữ liệu
button = tk.Button(window, text="Xác nhận", command=lay_ten)
button.pack(pady=10)

window.mainloop()

5.2. Entry cho form nhập điểm

import tkinter as tk

window = tk.Tk()
window.title("Nhập điểm học sinh")
window.geometry("400x300")

# Tiêu đề
title = tk.Label(window, text="NHẬP ĐIỂM HỌC SINH", 
                 font=("Arial", 16, "bold"), fg="blue")
title.pack(pady=10)

# Nhập tên
label_ten = tk.Label(window, text="Tên học sinh:")
label_ten.pack()
entry_ten = tk.Entry(window, font=("Arial", 11))
entry_ten.pack(pady=5)

# Nhập điểm Toán
label_toan = tk.Label(window, text="Điểm Toán:")
label_toan.pack()
entry_toan = tk.Entry(window, font=("Arial", 11))
entry_toan.pack(pady=5)

# Nhập điểm Văn
label_van = tk.Label(window, text="Điểm Văn:")
label_van.pack()
entry_van = tk.Entry(window, font=("Arial", 11))
entry_van.pack(pady=5)

def tinh_diem_tb():
    try:
        ten = entry_ten.get()
        diem_toan = float(entry_toan.get())
        diem_van = float(entry_van.get())

        diem_tb = (diem_toan + diem_van) / 2
        print(f"Học sinh: {ten}")
        print(f"Điểm TB: {diem_tb:.1f}")

        # Xóa các ô nhập sau khi tính
        entry_ten.delete(0, tk.END)
        entry_toan.delete(0, tk.END)
        entry_van.delete(0, tk.END)

    except ValueError:
        print("Vui lòng nhập số hợp lệ!")

# Button tính điểm
btn_tinh = tk.Button(window, text="Tính điểm TB", 
                     command=tinh_diem_tb,
                     bg="green", fg="white", font=("Arial", 12))
btn_tinh.pack(pady=20)

window.mainloop()

6. Layout Managers - Quản lý bố cục

6.1. Pack Layout

import tkinter as tk

window = tk.Tk()
window.title("Pack Layout")
window.geometry("300x400")

# Các widget xếp theo chiều dọc
label1 = tk.Label(window, text="Label 1", bg="red")
label1.pack(fill=tk.X, padx=10, pady=5)

label2 = tk.Label(window, text="Label 2", bg="green")
label2.pack(fill=tk.X, padx=10, pady=5)

label3 = tk.Label(window, text="Label 3", bg="blue")
label3.pack(fill=tk.X, padx=10, pady=5)

# Các button xếp theo chiều ngang
frame = tk.Frame(window)
frame.pack(pady=20)

btn1 = tk.Button(frame, text="Button 1")
btn1.pack(side=tk.LEFT, padx=5)

btn2 = tk.Button(frame, text="Button 2")
btn2.pack(side=tk.LEFT, padx=5)

btn3 = tk.Button(frame, text="Button 3")
btn3.pack(side=tk.LEFT, padx=5)

window.mainloop()

6.2. Grid Layout

import tkinter as tk

window = tk.Tk()
window.title("Grid Layout")
window.geometry("400x300")

# Tạo form bằng grid
tk.Label(window, text="Họ tên:").grid(row=0, column=0, sticky="w", padx=10, pady=5)
tk.Entry(window).grid(row=0, column=1, padx=10, pady=5)

tk.Label(window, text="Tuổi:").grid(row=1, column=0, sticky="w", padx=10, pady=5)
tk.Entry(window).grid(row=1, column=1, padx=10, pady=5)

tk.Label(window, text="Lớp:").grid(row=2, column=0, sticky="w", padx=10, pady=5)
tk.Entry(window).grid(row=2, column=1, padx=10, pady=5)

# Button span nhiều cột
tk.Button(window, text="Xác nhận").grid(row=3, column=0, columnspan=2, pady=20)

window.mainloop()

7. Ví dụ tổng hợp - Ứng dụng đơn giản

7.1. Máy tính cơ bản

import tkinter as tk

class MayTinhDonGian:
    def __init__(self):
        self.window = tk.Tk()
        self.window.title("Máy tính đơn giản")
        self.window.geometry("300x400")

        # Tạo giao diện
        self.tao_giao_dien()

    def tao_giao_dien(self):
        # Tiêu đề
        title = tk.Label(self.window, text="MÁY TÍNH ĐƠN GIẢN",
                         font=("Arial", 16, "bold"), fg="blue")
        title.pack(pady=10)

        # Số thứ nhất
        tk.Label(self.window, text="Số thứ nhất:").pack()
        self.entry1 = tk.Entry(self.window, font=("Arial", 12))
        self.entry1.pack(pady=5)

        # Số thứ hai
        tk.Label(self.window, text="Số thứ hai:").pack()
        self.entry2 = tk.Entry(self.window, font=("Arial", 12))
        self.entry2.pack(pady=5)

        # Các nút phép tính
        frame_buttons = tk.Frame(self.window)
        frame_buttons.pack(pady=20)

        tk.Button(frame_buttons, text="+", command=self.cong,
                  width=5, font=("Arial", 12)).pack(side=tk.LEFT, padx=5)
        tk.Button(frame_buttons, text="-", command=self.tru,
                  width=5, font=("Arial", 12)).pack(side=tk.LEFT, padx=5)
        tk.Button(frame_buttons, text="×", command=self.nhan,
                  width=5, font=("Arial", 12)).pack(side=tk.LEFT, padx=5)
        tk.Button(frame_buttons, text="÷", command=self.chia,
                  width=5, font=("Arial", 12)).pack(side=tk.LEFT, padx=5)

        # Kết quả
        self.label_ketqua = tk.Label(self.window, text="Kết quả: ",
                                     font=("Arial", 14, "bold"), fg="red")
        self.label_ketqua.pack(pady=20)

        # Nút xóa
        tk.Button(self.window, text="Xóa", command=self.xoa,
                  bg="orange", font=("Arial", 12)).pack(pady=10)

    def lay_so(self):
        try:
            so1 = float(self.entry1.get())
            so2 = float(self.entry2.get())
            return so1, so2
        except ValueError:
            self.label_ketqua.config(text="Lỗi: Vui lòng nhập số hợp lệ!")
            return None, None

    def cong(self):
        so1, so2 = self.lay_so()
        if so1 is not None:
            ketqua = so1 + so2
            self.label_ketqua.config(text=f"Kết quả: {ketqua}")

    def tru(self):
        so1, so2 = self.lay_so()
        if so1 is not None:
            ketqua = so1 - so2
            self.label_ketqua.config(text=f"Kết quả: {ketqua}")

    def nhan(self):
        so1, so2 = self.lay_so()
        if so1 is not None:
            ketqua = so1 * so2
            self.label_ketqua.config(text=f"Kết quả: {ketqua}")

    def chia(self):
        so1, so2 = self.lay_so()
        if so1 is not None:
            if so2 != 0:
                ketqua = so1 / so2
                self.label_ketqua.config(text=f"Kết quả: {ketqua}")
            else:
                self.label_ketqua.config(text="Lỗi: Không thể chia cho 0!")

    def xoa(self):
        self.entry1.delete(0, tk.END)
        self.entry2.delete(0, tk.END)
        self.label_ketqua.config(text="Kết quả: ")

    def chay(self):
        self.window.mainloop()

# Chạy ứng dụng
if __name__ == "__main__":
    may_tinh = MayTinhDonGian()
    may_tinh.chay()

8. Bài tập thực hành tại lớp

Bài 1: Cửa sổ chào mừng

Tạo một cửa sổ với tiêu đề "Chào mừng", có một label hiển thị "Xin chào [Tên bạn]!" và một button "Thoát" để đóng ứng dụng.

Bài 2: Form nhập thông tin cá nhân

Tạo form có các trường: Họ tên, Tuổi, Lớp và một button "Hiển thị" để in thông tin ra console.

Bài 3: Máy tính đơn giản 2 số

Tạo giao diện để nhập 2 số và thực hiện phép cộng, in kết quả lên label.

Bài 4: Đổi màu nền

Tạo cửa sổ với 3 button: "Đỏ", "Xanh", "Vàng". Khi nhấn button nào thì màu nền cửa sổ đổi thành màu đó.

9. Bài tập về nhà

Bài 1: Ứng dụng chuyển đổi nhiệt độ

Đề bài: Tạo giao diện để chuyển đổi giữa Celsius và Fahrenheit.

💡 Phân tích đề

Các bước thực hiện:

  1. Tạo cửa sổ chính với title phù hợp
  2. Tạo 2 Entry để nhập/hiển thị nhiệt độ
  3. Tạo 2 Button cho chuyển đổi C->F và F->C
  4. Viết hàm chuyển đổi: F = C * 9/5 + 32
  5. Viết hàm chuyển đổi ngược: C = (F - 32) * 5/9
  6. Xử lý lỗi khi nhập dữ liệu không hợp lệ
  7. Sử dụng grid() để bố trí giao diện

Input/Output mẫu:

Input C Output F Input F Output C
0 32 100 37.78
100 212 32 0

Bài 2: Máy tính BMI

Đề bài: Tạo form nhập cân nặng và chiều cao, tính chỉ số BMI và hiển thị đánh giá.

💡 Phân tích đề

Các bước thực hiện:

  1. Tạo form với Entry cho cân nặng (kg) và chiều cao (m)
  2. Tạo Button "Tính BMI" để thực hiện tính toán
  3. Tạo Label để hiển thị kết quả BMI
  4. Tính BMI theo công thức: BMI = cân nặng / (chiều cao)²
  5. Đánh giá BMI: <18.5 gầy, 18.5-24.9 bình thường, ≥25 thừa cân
  6. Hiển thị cả số BMI và đánh giá
  7. Xử lý lỗi nhập liệu và validation

Input/Output mẫu:

Cân nặng (kg) Chiều cao (m) BMI Đánh giá
70 1.75 22.86 Bình thường
50 1.60 19.53 Bình thường

Bài 3: Trò chơi đoán số

Đề bài: Tạo game đoán số từ 1-100 với giao diện Tkinter.

💡 Phân tích đề

Các bước thực hiện:

  1. Import random để tạo số ngẫu nhiên từ 1-100
  2. Tạo giao diện với Entry nhập số đoán
  3. Tạo Button "Đoán" và "Chơi lại"
  4. Tạo Label hiển thị gợi ý (lớn hơn/nhỏ hơn/đúng)
  5. Đếm số lần đoán và hiển thị
  6. Khi đoán đúng, hiển thị chúc mừng + số lần đoán
  7. Button "Chơi lại" để bắt đầu game mới

Input/Output mẫu:

Số bí mật Số đoán Kết quả Lần đoán
67 50 Lớn hơn! 1
67 80 Nhỏ hơn! 2
67 67 Chính xác! 3

Bài 4: Quản lý danh sách học sinh đơn giản

Đề bài: Tạo form để thêm tên học sinh vào danh sách và hiển thị.

💡 Phân tích đề

Các bước thực hiện:

  1. Tạo Entry để nhập tên học sinh
  2. Tạo Button "Thêm" để thêm vào danh sách
  3. Tạo Listbox hoặc Text để hiển thị danh sách
  4. Tạo list Python để lưu trữ tên học sinh
  5. Hàm thêm: lấy tên từ Entry, thêm vào list, cập nhật hiển thị
  6. Tạo Button "Xóa" để xóa học sinh được chọn
  7. Validation: không thêm tên trống hoặc trùng lặp

Input/Output mẫu:

Hành động Input Danh sách hiện tại
Thêm Nguyễn Văn A 1. Nguyễn Văn A
Thêm Trần Thị B 1. Nguyễn Văn A, 2. Trần Thị B
Xóa Chọn item 1 1. Trần Thị B

10. Ghi chú quan trọng

10.1. Lưu ý khi sử dụng Tkinter

  • Luôn gọi mainloop() ở cuối để hiển thị cửa sổ
  • Sử dụng try-except khi xử lý dữ liệu từ Entry
  • pack(), grid()place() không nên trộn lẫn trong cùng container
  • Sử dụng StringVar(), IntVar() để liên kết dữ liệu với widget

10.2. Các widget cơ bản cần nhớ

  • Label: Hiển thị văn bản hoặc hình ảnh
  • Button: Nút bấm để thực hiện hành động
  • Entry: Hộp nhập liệu một dòng
  • Frame: Container để nhóm các widget

10.3. Layout managers

  • pack(): Xếp widget theo chiều dọc hoặc ngang
  • grid(): Xếp widget theo dạng bảng
  • place(): Đặt widget ở vị trí cụ thể (ít dùng)

10.4. Event và Command

  • command: Gán hàm cho button
  • bind(): Gán sự kiện cho widget
  • Hàm xử lý sự kiện không có tham số hoặc có tham số event

10.5. Tips học tập

  • Bắt đầu với các ví dụ đơn giản
  • Thực hành tạo nhiều giao diện khác nhau
  • Kết hợp với kiến thức đã học (vòng lặp, điều kiện, hàm)
  • Tham khảo tài liệu chính thức của Tkinter