TKINTER TUTORIAL
Chương trình "Hello, World!" bằng Tkinter trong Python Tìm hiểu cửa sổ Tkinter trong Python Tìm hiểu về các Widget Tkinter trong Python 3 cách đặt Options cho Widget Tkinter trong Python Ràng buộc lệnh trong Tkinter Tìm hiểu Event Binding của Tkinter trong Python Giới thiệu widget Label trong Tkinter Cách sử dụng widget Entry trong Tkinter Trình quản lý hình học Pack trong Tkinter Trình quản lý hình học Grid trong Tkinter Trình quản lý hình học Place trong Tkinter Kích thước Widget trong Tkinter Tìm hiểu về widget Frame trong Tkinter Cách sử dụng widget Text của Tkinter Tìm hiểu về widget Scrollbar của Tkinter Cách sử dụng widget ScrolledText của Tkinter Cách sử dụng widget Separator của Tkinter Cách sử dụng Widget Checkbox Trong Tkinter Cách sử dụng widget radio button của Tkinter Cách tạo widget combobox trong Tkinter Cách sử dụng widget Listbox trong Tkinter Sử dụng widget PanedWindow trong Tkinter Cách tạo widget Spinbox trong Tkinter Hướng dẫn sử dụng Slider trong Tkinter Cách sử dụng widget Sizegrip trong Tkinter Cách sử dụng widget LabelFrame trong Tkinter Cách sử dụng widget Progressbar trong Tkinter Cách sử dụng widget Notebook trong Tkinter Cách sử dụng về widget Treeview trong Tkinter Hướng dẫn về Canvas trong Tkinter Cách thiết lập con trỏ widget trong Tkinter. Window hướng đối tượng trong Tkinter Các Frame hướng đối tượng trong Tkinter Cách sử dụng Object-Oriented Application trong Tkinter Phương thức tkraise() của Frame trong Tkinter Các kiểu (Styles) trong Tkinter Cách thay đổi theme trong Tkinter Các element của ttk trong Tkinter Tìm hiểu về đối tượng Tkinter StringVar trong Tkinter Cách sử dụng phương thức map() của ttk.Style Cách sử dụng phương thức after() của Tkinter Cách sử dụng threads trong Tkinter Cách hiển thị progress bar khi thread đang chạy trong Tkinter. Cách tạo nhiều cửa sổ trong một Tkinter Cách sử dụng widget PhotoImage của Tkinter Cấu trúc MVC trong Tkinter Cách sử dụng Validate trong Tkinter Cách hiển thị đồ thị từ thư viện Matplotlib trong Tkinter Ứng dụng System Tray với Tkinter
CÁC CHỦ ĐỀ
BÀI MỚI NHẤT
MỚI CẬP NHẬT

Thông báo: Download 4 khóa học Python từ cơ bản đến nâng cao tại đây.

Cách hiển thị progress bar khi thread đang chạy trong Tkinter.

Trong bài viết này, bạn sẽ được hướng dẫn cách tích hợp và hiển thị thanh tiến trình (progress bar) trong ứng dụng Tkinter. Cụ thể, bạn sẽ tìm hiểu cách sử dụng luồng (thread) để thực hiện các tác vụ nền đồng thời cập nhật giao diện người dùng (UI) nhằm hiển thị tiến trình của tác vụ đó. Điều này sẽ giúp cải thiện trải nghiệm người dùng bằng cách cung cấp phản hồi trực quan khi ứng dụng đang xử lý các công việc kéo dài, như tải xuống dữ liệu hoặc thực hiện các tính toán phức tạp.

test php

banquyen png
Bài viết này được đăng tại freetuts.net, không được copy dưới mọi hình thức.

Giới thiệu hiển thị progress bar khi thread đang chạy trong Tkinter.

Hướng dẫn này giả định rằng bạn đã biết cách sử dụng phương thức after() và hiểu cách hoạt động của luồng trong Python. Ngoài ra, bạn cũng nên biết cách chuyển đổi giữa các khung (frames) bằng phương thức tkraise().

Trong hướng dẫn này, chúng ta sẽ xây dựng một ứng dụng xem ảnh (picture viewer) hiển thị một hình ảnh ngẫu nhiên từ unsplash.com sử dụng API của nó.

Nếu bạn thực hiện yêu cầu HTTP tới endpoint API sau:

Bài viết này được đăng tại [free tuts .net]

https://source.unsplash.com/random/640x480

Bạn sẽ nhận được một hình ảnh ngẫu nhiên với kích thước 640×480.

Hình ảnh sau đây hiển thị ứng dụng Image Viewer cuối cùng:

Tkinter Thread Progressbar png

Khi bạn nhấn vào nút "Next Picture", chương trình sẽ gọi API từ unsplash.com để tải xuống một hình ảnh ngẫu nhiên và hiển thị nó trên cửa sổ.

Nó cũng sẽ hiển thị một thanh tiến trình trong khi tải ảnh, chỉ ra rằng quá trình tải xuống đang diễn ra:

Tkinter Thread Progressbar Progressbar png

Để gọi API, bạn sử dụng mô-đun requests.

Cách hiển thị progress bar khi thread đang chạy trong Tkinter.

Trước tiên, cài đặt mô-đun requests nếu nó chưa có trên máy tính của bạn:

pip install requests

Thứ hai, định nghĩa một lớp mới kế thừa từ lớp Thread:

class PictureDownload(Thread):
    def __init__(self, url):
        super().__init__()

        self.picture_file = None
        self.url = url

    def run(self):
        """Tải ảnh và lưu vào một file"""
        # Tải ảnh
        response = requests.get(self.url)
        picture_name = self.url.split('/')[-1]
        picture_file = f'./assets/{picture_name}.jpg'

        # Lưu ảnh vào file
        with open(picture_file, 'wb') as f:
            f.write(response.content)

        self.picture_file = picture_file

Thứ hai, định nghĩa một lớp mới kế thừa từ lớp Thread:

class PictureDownload(Thread):
    def __init__(self, url):
        super().__init__()

        self.picture_file = None
        self.url = url

    def run(self):
        """Tải ảnh và lưu vào một file"""
        # Tải ảnh
        response = requests.get(self.url)
        picture_name = self.url.split('/')[-1]
        picture_file = f'./assets/{picture_name}.jpg'

        # Lưu ảnh vào 
        with open(picture_file, 'wb') as f:
            f.write(response.content)

        self.picture_file = picture_file

Trong lớp PictureDownload, phương thức run() gọi API sử dụng mô-đun requests.

Phương thức run() tải xuống một hình ảnh và lưu nó vào thư mục /assets/. Đồng thời, nó gán đường dẫn của ảnh đã tải xuống cho thuộc tính picture_file của đối tượng.

Thứ ba, định nghĩa một lớp App kế thừa từ lớp Tk. Lớp App đại diện cho cửa sổ gốc.

Cửa sổ gốc có hai khung, một để hiển thị thanh tiến trình (Progressbar) và một để hiển thị Canvas chứa hình ảnh đã tải xuống:

def __init__(self, canvas_width, canvas_height):
    super().__init__()
    self.resizable(0, 0)
    self.title('Image Viewer')

    # Khung tiến trình
    self.progress_frame = ttk.Frame(self)

    # Cấu hình lưới để đặt thanh tiến trình ở trung tâm
    self.progress_frame.columnconfigure(0, weight=1)
    self.progress_frame.rowconfigure(0, weight=1)

    # Thanh tiến trình
    self.pb = ttk.Progressbar(
        self.progress_frame, orient=tk.HORIZONTAL, mode='indeterminate')
    self.pb.grid(row=0, column=0, sticky=tk.EW, padx=10, pady=10)

    # Đặt khung tiến trình
    self.progress_frame.grid(row=0, column=0, sticky=tk.NSEW)

    # Khung hình ảnh
    self.picture_frame = ttk.Frame(self)

    # Chiều rộng và chiều cao của canvas
    self.canvas_width = canvas_width
    self.canvas_height = canvas_height

    # Canvas
    self.canvas = tk.Canvas(
        self.picture_frame,
        width=self.canvas_width,
        height=self.canvas_height)
    self.canvas.grid(row=0, column=0)

    self.picture_frame.grid(row=0, column=0)

Khi bạn nhấn nút "Next Picture", phương thức handle_download() sẽ thực thi:

def handle_download(self):
    """Tải một bức ảnh ngẫu nhiên từ unsplash"""
    self.start_downloading()

    url = 'https://source.unsplash.com/random/640x480'
    download_thread = PictureDownload(url)
    download_thread.start()

    self.monitor(download_thread)

Phương thức handle_download() hiển thị khung tiến trình bằng cách gọi phương thức start_downloading() và bắt đầu thanh tiến trình:

def start_downloading(self):
    self.progress_frame.tkraise()
    self.pb.start(20)

Nó cũng tạo một luồng mới để tải xuống hình ảnh ngẫu nhiên và gọi phương thức monitor() để giám sát trạng thái của luồng.

Dưới đây là phương thức monitor():

def monitor(self, download_thread):
    """Giám sát luồng tải xuống"""
    if download_thread.is_alive():
        self.after(100, lambda: self.monitor(download_thread))
    else:
        self.stop_downloading()
        self.set_picture(download_thread.picture_file)

Phương thức monitor() kiểm tra trạng thái của luồng. Nếu luồng vẫn đang chạy, nó sẽ lên lịch kiểm tra lại sau 100ms.

Nếu không, phương thức monitor() sẽ gọi stop_downloading() để dừng thanh tiến trình, hiển thị khung hình ảnh, và hiển thị hình ảnh.

Dưới đây là phương thức stop_downloading():

def stop_downloading(self):
    self.picture_frame.tkraise()
    self.pb.stop()

Dưới đây là chương trình hoàn chỉnh của Image Viewer:

import requests
import tkinter as tk
from threading import Thread
from PIL import Image, ImageTk
from tkinter import ttk


class PictureDownload(Thread):
    def __init__(self, url):
        super().__init__()

        self.picture_file = None
        self.url = url

    def run(self):
        """Tải ảnh và lưu vào tệp"""
        response = requests.get(self.url)
        picture_name = self.url.split('/')[-1]
        picture_file = f'./assets/{picture_name}.jpg'

        with open(picture_file, 'wb') as f:
            f.write(response.content)

        self.picture_file = picture_file


class App(tk.Tk):
    def __init__(self, canvas_width, canvas_height):
        super().__init__()
        self.resizable(0, 0)
        self.title('Image Viewer')

        self.progress_frame = ttk.Frame(self)

        self.progress_frame.columnconfigure(0, weight=1)
        self.progress_frame.rowconfigure(0, weight=1)

        self.pb = ttk.Progressbar(
            self.progress_frame, orient=tk.HORIZONTAL, mode='indeterminate')
        self.pb.grid(row=0, column=0, sticky=tk.EW, padx=10, pady=10)

        self.progress_frame.grid(row=0, column=0, sticky=tk.NSEW)

        self.picture_frame = ttk.Frame(self)

        self.canvas_width = canvas_width
        self.canvas_height = canvas_height

        self.canvas = tk.Canvas(
            self.picture_frame,
            width=self.canvas_width,
            height=self.canvas_height)
        self.canvas.grid(row=0, column=0)

        self.picture_frame.grid(row=0, column=0)

        btn = ttk.Button(self, text='Next Picture')
        btn['command'] = self.handle_download
        btn.grid(row=1, column=0)

    def start_downloading(self):
        self.progress_frame.tkraise()
        self.pb.start(20)

    def stop_downloading(self):
        self.picture_frame.tkraise()
        self.pb.stop()

    def set_picture(self, file_path):
        """Đặt hình ảnh vào canvas"""
        pil_img = Image.open(file_path)

        resized_img = pil_img.resize(
            (self.canvas_width, self.canvas_height),
            Image.ANTIALIAS)

        self.img = ImageTk.PhotoImage(resized_img)

        self.bg = self.canvas.create_image(
            0,
            0,
            anchor=tk.NW,
            image=self.img)

    def handle_download(self):
        """Tải một bức ảnh ngẫu nhiên từ unsplash"""
        self.start_downloading()

        url = 'https://source.unsplash.com/random/640x480'
        download_thread = PictureDownload(url)
        download_thread.start()

        self.monitor(download_thread)

    def monitor(self, download_thread):
        """Giám sát luồng tải xuống"""
        if download_thread.is_alive():
            self.after(100, lambda: self.monitor(download_thread))
        else:
            self.stop_downloading()
            self.set_picture(download_thread.picture_file)


if __name__ == '__main__':
    app = App(640, 480)
    app.mainloop()

Kết bài

Qua bài viết này, bạn đã học cách hiển thị thanh tiến trình trong ứng dụng Tkinter khi một luồng đang chạy. Bằng cách sử dụng luồng để xử lý tác vụ nền và cập nhật giao diện người dùng một cách đồng bộ, bạn có thể cải thiện hiệu suất ứng dụng và cung cấp trải nghiệm người dùng mượt mà hơn. Việc áp dụng các kỹ thuật này sẽ giúp bạn xây dựng các ứng dụng tương tác và thân thiện hơn, đồng thời tận dụng hiệu quả sức mạnh của Tkinter và lập trình đa luồng trong Python.

Cùng chuyên mục:

Hướng dẫn xây dựng Command-Line Interface (CLI) bằng Quo trong Python

Hướng dẫn xây dựng Command-Line Interface (CLI) bằng Quo trong Python

Hướng dẫn toàn diện về module datetime trong Python

Hướng dẫn toàn diện về module datetime trong Python

Cách truy cập và thiết lập biến môi trường trong Python

Cách truy cập và thiết lập biến môi trường trong Python

Lớp dữ liệu (Data Classes) trong Python với decorator @dataclass

Lớp dữ liệu (Data Classes) trong Python với decorator @dataclass

Từ khóa yield trong Python

Từ khóa yield trong Python

Sự khác biệt giữa sort() và sorted() trong Python

Sự khác biệt giữa sort() và sorted() trong Python

Sử dụng Poetry để quản lý dependencies trong Python

Sử dụng Poetry để quản lý dependencies trong Python

Định dạng chuỗi Strings trong Python

Định dạng chuỗi Strings trong Python

Một tác vụ phổ biến khi làm việc với danh sách trong Python

Một tác vụ phổ biến khi làm việc với danh sách trong Python

Làm việc với các biến môi trường trong Python

Làm việc với các biến môi trường trong Python

Sự khác biệt giữa set() và frozenset() trong Python

Sự khác biệt giữa set() và frozenset() trong Python

Sự khác biệt giữa iterator và iterable trong Python

Sự khác biệt giữa iterator và iterable trong Python

Cách làm việc với file tarball/tar trong Python

Cách làm việc với file tarball/tar trong Python

Chuyển đổi kiểu dữ liệu trong Python

Chuyển đổi kiểu dữ liệu trong Python

Sự khác biệt giữa toán tử == và is trong Python

Sự khác biệt giữa toán tử == và is trong Python

Làm việc với file ZIP trong Python

Làm việc với file ZIP trong Python

Cách sử dụng ThreadPoolExecutor trong Python

Cách sử dụng ThreadPoolExecutor trong Python

Sự khác biệt giữa byte objects và string trong Python

Sự khác biệt giữa byte objects và string trong Python

Xử lý độ chính xác các hàm floor, ceil, round, trunc, format  trong Python

Xử lý độ chính xác các hàm floor, ceil, round, trunc, format trong Python

Cách lặp qua nhiều list với hàm zip() trong Python

Cách lặp qua nhiều list với hàm zip() trong Python

Top