Is it a tkinter problem? The progress bar is not shown when other windows are opened

Below is the general structure of my code. I have a main GUI window (w0), and some top level windows e.g. w3 which are used for image processing. Besides, I have also created a top level window as “progress window” to show the process information during processing. If I keep the w0 window opened, the green process bar is not shown, but if the w0 is destroyed somewhere in the beginning of the “auto_classic_i_method”
function, the green process bar appears. What is the reason? How can I keep both the w0 and the process bar at the same time? Also, I have tested some other options such as withdrawing and iconifying w0 at the beginning and end of the auto_classic_i_method, but it was not helpful.

1
2

Please place several images in the input path and run the code:

from tkinter import *
from tkinter import ttk
import tkinter.filedialog
import cv2
import skimage.color
import skimage.io
import skimage.measure
import skimage.morphology
import os.path
import tkinter
import os
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
from tqdm import tqdm
import time


class EasyQuadrat:
    def __init__(self, w0):
        self.w0 = w0
        w0.geometry("350x300")
        w0.title("Easy Quadrat")
        w0.configure(bg='light gray')
        w0.resizable(width=False, height=False)  # Disables window resizing

        # Create a new frame for the rest of the widgets
        self.main_frame = Frame(w0, bg='light gray', padx=0, pady=70)

        class FolderSelect(Frame):
            def __init__(self, parent=None, folderDescription="", **kw):
                Frame.__init__(self, master=parent, **kw)
                self.folderPath = StringVar()
                self.lblName = Label(self, text=folderDescription, fg='dark blue', bg='light gray',
                                     font=('label_font', 10, 'bold'))
                self.lblName.grid(row=1, column=0, padx=0, pady=0, rowspan=2, sticky=NSEW)
                self.entPath = Entry(self, borderwidth=3, width=18, textvariable=self.folderPath)
                self.entPath.grid(row=1, column=1, padx=0, pady=0, ipadx=0, ipady=0, sticky=NSEW)
                self.btnFind = ttk.Button(self, text="Browse", command=self.setFolderPath)
                self.btnFind.grid(row=1, column=2, padx=0, pady=0, ipadx=0, ipady=0, sticky=NSEW)

            def setFolderPath(self):
                folder_selected = filedialog.askdirectory()
                self.folderPath.set(folder_selected)

            @property
            def folder_path(self):
                return self.folderPath.get()

        self.directory1Select = FolderSelect(self.main_frame, "Input path")
        self.directory1Select.grid(row=0, column=0, columnspan=1, padx=10, pady=3, sticky=E)

        self.directory2Select = FolderSelect(self.main_frame, "Output path")
        self.directory2Select.grid(row=1, column=0, columnspan=1, rowspan=1, padx=10, pady=3, sticky=E)

        # Create a new frame for cropping method and radio buttons
        self.cropping_frame = Frame(self.main_frame, bg='light gray')
        self.cropping_frame.grid(row=2, column=0, columnspan=2, rowspan=2, padx=25, pady=50, sticky=W)

        self.Cropping_method = Label(self.cropping_frame, text='Cropping Method', font='label_font 11 bold',
                                     fg="dark green",
                                     bg='light gray')
        self.Cropping_method.grid(row=0, column=0, columnspan=2, rowspan=2, padx=0, pady=5, sticky=W)

        self.Button3 = Button(self.cropping_frame, text='Auto (Classic I)', command=self.auto_classic_i_method,
                              bg='light gray', width=16, height=2)
        self.Button3.grid(row=6, column=0, columnspan=2, rowspan=2, padx=15, pady=5, sticky=tk.W)
        self.buttons_frame = Frame(self.main_frame, bg='light gray')
        self.buttons_frame.grid(row=10, column=0, columnspan=2, padx=65, pady=5, sticky=W)

        # Pack the frames
        self.main_frame.grid(row=0, column=0, columnspan=2, rowspan=11, padx=10, pady=5, sticky=NSEW)
        self.folderPath = StringVar()

    def validate_entries(self):
        global w0, in_path, out_path
        in_path = self.directory1Select.folder_path
        out_path = self.directory2Select.folder_path

        if in_path == '':
            tkinter.messagebox.showwarning(title='Error!', message='Please select the input path')
            return False
        else:
            if out_path == '':
                tkinter.messagebox.showwarning(title='Error!', message='Please select the output path')
                return False
            else:
                return True



    # ----------------------

    # Method: Auto (Classic I)

    # ----------------------

    def auto_classic_i_method(self):

        if self.validate_entries():
            class ProgressWindow(tk.Toplevel):
                def __init__(self, parent, total, title="Progress"):
                    super().__init__(parent)
                    self.title(title)
                    self.geometry("400x150")

                    # Center the window on the screen
                    window_width = 400
                    window_height = 120
                    screen_width = self.winfo_screenwidth()
                    screen_height = self.winfo_screenheight()
                    x_coordinate = int((screen_width - window_width) / 2)
                    y_coordinate = int((screen_height - window_height) / 2)
                    self.geometry(f"+{x_coordinate}+{y_coordinate}")

                    # Show an "Initializing..." message
                    self.init_label = Label(self, text="Initializing... Please wait!", font=('default', 10, 'bold'))
                    self.init_label.pack(pady=25)

                    self.status_label = Label(self, text="", font=('default', 10, 'bold'))
                    self.status_label.pack(pady=25)

                    self.progress_var = IntVar()
                    # Adjusted length of the progress bar
                    self.progress_bar = ttk.Progressbar(self, variable=self.progress_var, maximum=total, length=300)
                    self.progress_bar.pack(pady=10)  # Adjusted padding
                    self.resizable(width=False, height=False)  # Disables window resizing

                    self.update()

                def update_progress(self, value, current_file):
                    if hasattr(self, 'init_label'):
                        self.init_label.destroy()
                        del self.init_label

                    percentage = int((value / self.progress_bar["maximum"]) * 100)
                    status_text = f"Processing image {value} / {self.progress_bar['maximum']}: {current_file} ({percentage}%)"
                    self.status_label.config(text=status_text)
                    self.progress_var.set(value)
                    self.update()

            class ClassicI:
                def __init__(self, w3):
                    self.w3 = w3
                    self.w3.geometry("340x260")
                    self.w3.title(" Easy Quadrat _ Classic I")
                    self.w3.resizable(width=False, height=False)  # Disables window resizing
                    self.lblw3 = Label(self.w3, text="This is a test...:", font=('default', 10, 'bold'))
                    self.lblw3.grid(row=1, column=1, padx=40, pady=15, sticky="w")


                    self.w3OK = ttk.Button(self.w3, text="OK", command=self.entw3)
                    self.w3OK.grid(row=9, column=1, padx=15, pady=20, sticky="s")

                    self.w3Cancel = ttk.Button(self.w3, text="Cancel", command=self.w3.destroy)
                    self.w3Cancel.grid(row=9, column=2, padx=0, pady=20, sticky="w")



                def entw3(self):


                    self.w3.withdraw()  # Hide the main window

                    progress_window = ProgressWindow(self.w3, total=len(os.listdir(in_path)),
                                                     title="Processing Images")


                    try:
                        self.Cropped_images = os.path.join(out_path, 'Cropped images')

                        os.makedirs(self.Cropped_images)
                    except FileExistsError:
                        pass

                    # Process images
                    file_list = os.listdir(in_path)
                    total_images = len(file_list)

                    for i, file in enumerate(
                            tqdm(file_list, desc="Processing Images", unit="image", leave=False, disable=True)):
                        try:
                            f_img = os.path.join(in_path, file)
                            image_rgb = skimage.io.imread(f_img)

                            time.sleep(2)

                            _, extension = os.path.splitext(file)
                            f_out2 = os.path.join(self.Cropped_images, "C_" + file)
                            cv2.imwrite(f_out2, cv2.cvtColor(image_rgb, cv2.COLOR_RGB2BGR))

                        except Exception as e:
                            pass

                        # Update the progress window
                        progress_window.update_progress(i + 1, file)

                    # Destroy the progress window after processing is complete
                    progress_window.destroy()
                    self.w3.destroy()
                    messagebox.showinfo("Easy Quadrat", "Processing completed!")

            w3 = tk.Tk()
            w3.resizable(width=False, height=False)  # Disables window resizing
            app = ClassicI(w3)
            w3.update_idletasks()  # Ensure the window is fully created before centering
            w3.eval('tk::PlaceWindow . center')
            w3.mainloop()


if __name__ == "__main__":
    w0 = tk.Tk()
    app = EasyQuadrat(w0)

    # Configure grid row and column weights
    w0.grid_rowconfigure(0, weight=1)
    w0.grid_columnconfigure(0, weight=1)

    # Center the window
    w0.eval('tk::PlaceWindow . center')

    # Start the main event loop
    w0.mainloop()

Hi !

If you want anyone to be able to run your code, you’ll have to add the imports in the code snippet. Also, don’t copy-paste all your code. Keep only the relevant parts and narrow it down to a minimal version allowing to reproduce your bug.

1 Like

Thanks. I added the reproducible version.

Thank you very much. This is a simple and reproducible version of the code (please add several images in the input path and run the code):

from tkinter import *
from tkinter import ttk
import tkinter.filedialog
import cv2
import skimage.color
import skimage.io
import skimage.measure
import skimage.morphology
import os.path
import tkinter
import os
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
from tqdm import tqdm
import time


class EasyQuadrat:
    def __init__(self, w0):
        self.w0 = w0
        w0.geometry("350x300")
        w0.title("Easy Quadrat")
        w0.configure(bg='light gray')
        w0.resizable(width=False, height=False)  # Disables window resizing

        # Create a new frame for the rest of the widgets
        self.main_frame = Frame(w0, bg='light gray', padx=0, pady=70)

        class FolderSelect(Frame):
            def __init__(self, parent=None, folderDescription="", **kw):
                Frame.__init__(self, master=parent, **kw)
                self.folderPath = StringVar()
                self.lblName = Label(self, text=folderDescription, fg='dark blue', bg='light gray',
                                     font=('label_font', 10, 'bold'))
                self.lblName.grid(row=1, column=0, padx=0, pady=0, rowspan=2, sticky=NSEW)
                self.entPath = Entry(self, borderwidth=3, width=18, textvariable=self.folderPath)
                self.entPath.grid(row=1, column=1, padx=0, pady=0, ipadx=0, ipady=0, sticky=NSEW)
                self.btnFind = ttk.Button(self, text="Browse", command=self.setFolderPath)
                self.btnFind.grid(row=1, column=2, padx=0, pady=0, ipadx=0, ipady=0, sticky=NSEW)

            def setFolderPath(self):
                folder_selected = filedialog.askdirectory()
                self.folderPath.set(folder_selected)

            @property
            def folder_path(self):
                return self.folderPath.get()

        self.directory1Select = FolderSelect(self.main_frame, "Input path")
        self.directory1Select.grid(row=0, column=0, columnspan=1, padx=10, pady=3, sticky=E)

        self.directory2Select = FolderSelect(self.main_frame, "Output path")
        self.directory2Select.grid(row=1, column=0, columnspan=1, rowspan=1, padx=10, pady=3, sticky=E)

        # Create a new frame for cropping method and radio buttons
        self.cropping_frame = Frame(self.main_frame, bg='light gray')
        self.cropping_frame.grid(row=2, column=0, columnspan=2, rowspan=2, padx=25, pady=50, sticky=W)

        self.Cropping_method = Label(self.cropping_frame, text='Cropping Method', font='label_font 11 bold',
                                     fg="dark green",
                                     bg='light gray')
        self.Cropping_method.grid(row=0, column=0, columnspan=2, rowspan=2, padx=0, pady=5, sticky=W)

        self.Button3 = Button(self.cropping_frame, text='Auto (Classic I)', command=self.auto_classic_i_method,
                              bg='light gray', width=16, height=2)
        self.Button3.grid(row=6, column=0, columnspan=2, rowspan=2, padx=15, pady=5, sticky=tk.W)
        self.buttons_frame = Frame(self.main_frame, bg='light gray')
        self.buttons_frame.grid(row=10, column=0, columnspan=2, padx=65, pady=5, sticky=W)

        # Pack the frames
        self.main_frame.grid(row=0, column=0, columnspan=2, rowspan=11, padx=10, pady=5, sticky=NSEW)
        self.folderPath = StringVar()

    def validate_entries(self):
        global w0, in_path, out_path
        in_path = self.directory1Select.folder_path
        out_path = self.directory2Select.folder_path

        if in_path == '':
            tkinter.messagebox.showwarning(title='Error!', message='Please select the input path')
            return False
        else:
            if out_path == '':
                tkinter.messagebox.showwarning(title='Error!', message='Please select the output path')
                return False
            else:
                return True



    # ----------------------

    # Method: Auto (Classic I)

    # ----------------------

    def auto_classic_i_method(self):

        if self.validate_entries():
            class ProgressWindow(tk.Toplevel):
                def __init__(self, parent, total, title="Progress"):
                    super().__init__(parent)
                    self.title(title)
                    self.geometry("400x150")

                    # Center the window on the screen
                    window_width = 400
                    window_height = 120
                    screen_width = self.winfo_screenwidth()
                    screen_height = self.winfo_screenheight()
                    x_coordinate = int((screen_width - window_width) / 2)
                    y_coordinate = int((screen_height - window_height) / 2)
                    self.geometry(f"+{x_coordinate}+{y_coordinate}")

                    # Show an "Initializing..." message
                    self.init_label = Label(self, text="Initializing... Please wait!", font=('default', 10, 'bold'))
                    self.init_label.pack(pady=25)

                    self.status_label = Label(self, text="", font=('default', 10, 'bold'))
                    self.status_label.pack(pady=25)

                    self.progress_var = IntVar()
                    # Adjusted length of the progress bar
                    self.progress_bar = ttk.Progressbar(self, variable=self.progress_var, maximum=total, length=300)
                    self.progress_bar.pack(pady=10)  # Adjusted padding
                    self.resizable(width=False, height=False)  # Disables window resizing

                    self.update()

                def update_progress(self, value, current_file):
                    if hasattr(self, 'init_label'):
                        self.init_label.destroy()
                        del self.init_label

                    percentage = int((value / self.progress_bar["maximum"]) * 100)
                    status_text = f"Processing image {value} / {self.progress_bar['maximum']}: {current_file} ({percentage}%)"
                    self.status_label.config(text=status_text)
                    self.progress_var.set(value)
                    self.update()

            class ClassicI:
                def __init__(self, w3):
                    self.w3 = w3
                    self.w3.geometry("340x260")
                    self.w3.title(" Easy Quadrat _ Classic I")
                    self.w3.resizable(width=False, height=False)  # Disables window resizing
                    self.lblw3 = Label(self.w3, text="This is a test...:", font=('default', 10, 'bold'))
                    self.lblw3.grid(row=1, column=1, padx=40, pady=15, sticky="w")


                    self.w3OK = ttk.Button(self.w3, text="OK", command=self.entw3)
                    self.w3OK.grid(row=9, column=1, padx=15, pady=20, sticky="s")

                    self.w3Cancel = ttk.Button(self.w3, text="Cancel", command=self.w3.destroy)
                    self.w3Cancel.grid(row=9, column=2, padx=0, pady=20, sticky="w")



                def entw3(self):


                    self.w3.withdraw()  # Hide the main window

                    progress_window = ProgressWindow(self.w3, total=len(os.listdir(in_path)),
                                                     title="Processing Images")


                    try:
                        self.Cropped_images = os.path.join(out_path, 'Cropped images')

                        os.makedirs(self.Cropped_images)
                    except FileExistsError:
                        pass

                    # Process images
                    file_list = os.listdir(in_path)
                    total_images = len(file_list)

                    for i, file in enumerate(
                            tqdm(file_list, desc="Processing Images", unit="image", leave=False, disable=True)):
                        try:
                            f_img = os.path.join(in_path, file)
                            image_rgb = skimage.io.imread(f_img)

                            time.sleep(2)

                            _, extension = os.path.splitext(file)
                            f_out2 = os.path.join(self.Cropped_images, "C_" + file)
                            cv2.imwrite(f_out2, cv2.cvtColor(image_rgb, cv2.COLOR_RGB2BGR))

                        except Exception as e:
                            pass

                        # Update the progress window
                        progress_window.update_progress(i + 1, file)

                    # Destroy the progress window after processing is complete
                    progress_window.destroy()
                    self.w3.destroy()
                    messagebox.showinfo("Easy Quadrat", "Processing completed!")

            w3 = tk.Tk()
            w3.resizable(width=False, height=False)  # Disables window resizing
            app = ClassicI(w3)
            w3.update_idletasks()  # Ensure the window is fully created before centering
            w3.eval('tk::PlaceWindow . center')
            w3.mainloop()


if __name__ == "__main__":
    w0 = tk.Tk()
    app = EasyQuadrat(w0)

    # Configure grid row and column weights
    w0.grid_rowconfigure(0, weight=1)
    w0.grid_columnconfigure(0, weight=1)

    # Center the window
    w0.eval('tk::PlaceWindow . center')

    # Start the main event loop
    w0.mainloop()

I’m sorry but I cannot take the time to review your code, it’s still quite long and difficult to read. I think you could simplify it much much more than it is right now:

  • Remove the formatting-related lines (self.title(), self.geometry(), etc.), they’re most likely irrelevant to your problem.
  • Remove all the parts handling images, your interface would likely work the same way if you call sleep instead of performing image processing.
  • The folder selection worked fine for me, and seems independent from your problem. You can probably remove it and set a fixed list of dummy image paths to iterate over.

Also, having classes defined inside of another class method is often a bad design. Do you really need it ? I didn’t check in detail, but having all your classes clearly separated would certainly improve the readability. This design might also be the cause of your problem.

1 Like

Hi,

I just reviewed your code. Was there a copy and paste mistake? There are classes inside functions and classes inside other classes? That is very interesting. I have never seen this before.

Please make sure your code is properly written.

By the way, I also could not download the cv2 library package.

Error: No matching distribution found for cv2
1 Like

Try the following: python -m pip install opencv-python :wink:

1 Like

Thanks. No, there is no mistake in the copying and pasting the code. Sorry; I don’t know about the problem of cv2.

Thanks a lot.

Ok, got it. This way worked like a charm.
Before I was trying:

pip install cv2 

Thank you.

1 Like

… then I would suggest re-writing the code.

Take classes out of classes and classes out of functions.
If you would like to extend the functionality of the classes, use inheritance or
composition.

1 Like

You have duplicate imports of tkinter. This might be a problem. Anyway, you should be able to demonstrate a problem without installing 3rd party modules. If not, the 3rd party module might be the source of the problem.

1 Like