1

I have a class for a GUI with two radio buttons one to set save_to_excel to True and the other to False. However I wanted to add a loading screen using threading, but when I add that second class the save_to_excel variable no longer updates when I press the radio buttons... Does it have to do with the way its initialized or is threading breaking it?

class LoadingScreen:
    def __init__(self, root):
        self.root = root
        self.root.title("Loading...")
        self.root.geometry("300x100")
        self.root.configure(bg="white")
        self.label = tk.Label(self.root, text="Loading, please wait...", bg="white", fg="black")
        self.label.pack(expand=True)

        
class DICOMExtractorApp:
    def __init__(self, root):
        try:
            self.root = root
            self.root.title("DICOM Info Extractor")
            self.root.configure(bg="white")

            # Initializing variables
            self.save_to_excel = tk.BooleanVar(value=True)  # Default to saving to Excel
            self.folder_path = ""
            self.output_path = ""
            self.dicom_tags = {}
            self.default_tags = {
                "Filename": "filename",
                "Acquisition Date": (0x0008, 0x0022),
                "Manufacturer": (0x0008, 0x0070),
                "Study Description": (0x0008, 0x1030),
                "Instance Number": (0x0020, 0x0013),
                "Series Description": (0x0008, 0x103E),
                "Acquisition Time": (0x0008, 0x0032),
                "Modality": (0x0008, 0x0060),
                "Station Name": (0x0008, 0x1010)
            }
            self.selected_tags = {tag: tk.BooleanVar(value=True) for tag in self.default_tags}
            self.tags_per_page = 10
            self.current_page = 0

            self.create_widgets()
        except Exception as e:
            self.log_error(e)
   def create_widgets(self):
        try:
            self.root.geometry("800x600")
            self.root.resizable(False, False)

            # Load Folder button
            tk.Button(self.root, text="Load Folder", command=self.load_folder, bg="#B7DFF5").place(x=50, y=20, width=100, height=30)
            
            # Output Location button
            tk.Button(self.root, text="Output Location", command=self.set_output_location, bg="#B7DFF5").place(x=200, y=20, width=120, height=30)
            
            # Radio buttons for saving options
            tk.Radiobutton(self.root, text="Save to Excel", variable=self.save_to_excel, value=True, bg="white", command=self.print_save_option).place(x=350, y=20)
            tk.Radiobutton(self.root, text="Save to Text", variable=self.save_to_excel, value=False, bg="white", command=self.print_save_option).place(x=460, y=20)

def main_app():
    def initialize_app():
        root = tk.Tk()
        app = DICOMExtractorApp(root)
        loading_root.destroy()
        root.deiconify()
        root.mainloop()

    loading_root = tk.Tk()
    loading_screen = LoadingScreen(loading_root)

    loading_root.after(1000, initialize_app)

    loading_root.mainloop()

if __name__ == "__main__":
    main_app()

When the "save to text" radio button is selected it should set the save_to_excel to False, and when the "save to excel" button is selected it set it to True.. Removing the LoadingScreen class fixes the code and it now correctly updates the variable as expected.

Replaced with, to remove loading screen:

if __name__ == "__main__":
    root = tk.Tk()
    app = DICOMExtractorApp(root)
    root.mainloop()
4
  • Which method are the two tk.Radiobutton lines in? It looks like they're in the except block of DICOMExtractorApp.__init__(), but that doesn't make much sense. Commented Aug 9, 2024 at 14:34
  • @Barmar correct that would not make sense, they are part of a create_widgets method, which creates the window and buttons.. Commented Aug 9, 2024 at 14:36
  • Why do you have multiple roots? I think that may be your problem. Why not use the same root for both LoadingScreen and DICOMExtractor? Commented Aug 9, 2024 at 14:36
  • The intention was to have the LoadingScreen run while DICOMExtractor was being initialized. I can try using the LoadingScreen as a TopLevel window Commented Aug 9, 2024 at 14:41

1 Answer 1

1

Switching from two roots to one root and using TopLevel seems to be a reasonable solution.

import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import os
import pydicom
import pandas as pd
from datetime import datetime
import traceback
import threading

class LoadingScreen:
    def __init__(self, parent):
        self.loading_window = tk.Toplevel(parent)
        self.loading_window.title("Loading...")
        self.loading_window.geometry("300x100")
        self.loading_window.configure(bg="white")
        self.label = tk.Label(self.loading_window, text="Loading, please wait...", bg="white", fg="black")
        self.label.pack(expand=True)
        # Prevent interactions with the main window during loading
        self.loading_window.grab_set()

    def close(self):
        self.loading_window.destroy()

class DICOMExtractorApp:
    def __init__(self, root):
        try:
            self.root = root
            self.root.title("DICOM Info Extractor")
            self.root.configure(bg="white")

            # Initializing variables
            self.save_to_excel = tk.BooleanVar(value=True)  # Default to saving to Excel
            self.folder_path = ""
            self.output_path = ""
            self.dicom_tags = {}
            self.default_tags = {
                "Filename": "filename",
                "Acquisition Date": (0x0008, 0x0022),
                "Manufacturer": (0x0008, 0x0070),
                "Study Description": (0x0008, 0x1030),
                "Instance Number": (0x0020, 0x0013),
                "Series Description": (0x0008, 0x103E),
                "Acquisition Time": (0x0008, 0x0032),
                "Modality": (0x0008, 0x0060),
                "Station Name": (0x0008, 0x1010)
            }
            self.selected_tags = {tag: tk.BooleanVar(value=True) for tag in self.default_tags}
            self.tags_per_page = 10
            self.current_page = 0

            self.create_widgets()
        except Exception as e:
            self.log_error(e)

    # Rest of your methods (load_dicom_tags, create_widgets, etc.) remain the same

def main_app():
    root = tk.Tk()

    # Create and show the loading screen
    loading_screen = LoadingScreen(root)

    def initialize_app():
        app = DICOMExtractorApp(root)
        loading_screen.close()  # Close the loading screen
        root.deiconify()  # Show the main application window

    # Start initialization after a delay (simulate loading time)
    root.after(1000, initialize_app)

    # Hide the main window until the loading screen is closed
    root.withdraw()

    root.mainloop()

if __name__ == "__main__":
    main_app()
Sign up to request clarification or add additional context in comments.

1 Comment

Not just reasonable, but the way the underlying tk library was designed to be used - an app should have a single root.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.