i am currently working on a python GUI and i use customtkinter (which is based on tkinter) for that. To enhance maintainability, scalability, and testability of the GUI i use the Model-View-Controller (MVC) architecture pattern, which decouples the business logic from the UI. This is giving me quite a problem because some of the functionalities of one model (reading a file) takes a few seconds. To indicate to the user that the GUI is not frozen i want to show the progress in a progressbar while executing the fie reading function in a thread.
Now my question is: How do i start the progressbar (which is in class B view) before the reading file function starts (which is in class A controller) and when its finished continue other functionalities?
This is my project simplified:
Structure overview
Project/
├── controllers/ # Contains classes that act as intermediaries
# between models and views. They handle user
# input, update models, and reflect changes
# in views.
│ ├── main_controller.py # Handles interactions in the main window.
│ ├── class_a_controller.py # Handles interactions in the class A view.
│ └── class_b_controller.py # Handles interactions in the class B view.
├── models/ # Contains classes that represent the data
# and business logic of the application.
│ └── model.py
├── views/ # Contains classes that define the visual
# representation of the application.
│ ├── main_view.py # The visual representation of main window.
│ ├── class_a_view.py # The visual representation of the class A view.
│ └── class_b_view.py # The visual representation of the class B view.
└── app.py # Entry point of the application.
Code snippets
views/main_view.py
class MainView(ctk.CTkFrame):
self.initialize_widgets()
def initialize_widgets(self) -> None:
"""
Initialize widgets.
"""
self.classA_view: ctk.CTkFrame = ClassAView(self)
self.classB_view: ctk.CTkFrame = ClassBView(self)
views/class_a_view.py
class ClassAView(ctk.CTkFrame):
"""
Layout of the class A view.
"""
def __init__(self, master: ctk.CTkFrame) -> None:
super().__init__(master)
self.initialize_widgets()
def initialize_widgets(self) -> None:
"""
Initialize widgets.
"""
self.custom_frame_view: ctk.CTkFrame = CustomFrameView(self)
class CustomFrameView(ctk.CTkFrame):
"""
Layout of the custom frame.
"""
def __init__(self, master: ctk.CTkFrame) -> None:
super().__init__(master)
self.initialize_widgets()
self.create_layout()
def initialize_widgets(self) -> None:
"""
Initialize widgets.
"""
self.button= ctk.CTkButton(self, text="")
def create_layout(self) -> None:
"""
Create layout.
"""
self.open_file_button.grid(row=0, column=1)
views/class_b_view.py
class ClassBView(ctk.CTkFrame):
"""
Layout of the class B view.
"""
def __init__(self, master: ctk.CTkFrame) -> None:
super().__init__(master)
self.initialize_widgets()
def initialize_widgets(self) -> None:
"""
Initialize widgets.
"""
self.progressbar = ctk.CTkProgressBar(self)
controllers/main_controller.py
class MainController:
"""
Functionality of the main application.
"""
def __init__(self, master: ctk.CTk) -> None:
self.__master: ctk.CTk = master
self.__view: ctk.CTkFrame = MainView(master)
self.__initialize_controller()
def __initialize_controller(self) -> None:
"""
Initialize controller.
"""
self.classA_controller: ClassAController = ClassAController(self.__view.classA_view)
self.classB_controller: ClassBController = ClassBController(self.__view.classB_view)
controllers/class_a_controller.py
class ClassAController:
"""
Functionality of the class A view.
"""
def __init__(self, view: ctk.CTkFrame) -> None:
self.view: ctk.CTkFrame = view
self.initialize_controller()
def initialize_controller(self) -> None:
"""
Initialize controller.
"""
self.custom_frame_controller = CustomFrameController(self.view.custom_frame_view)
class CustomFrameController:
"""
Functionality of the custom frame view.
"""
def __init__(self, view: ctk.CTkFrame) -> None:
self.view: ctk.CTkFrame = view
self.view.button.configure(command=self.func1)
self.setup_tracings()
def setup_tracings(self) -> None:
"""
Tracing variables from the widgets.
"""
self.view.var.trace("w", self.func2)
def func1(self) -> None:
"""
HERE I DISABLE OTHER WIDGETS I USE IN THE CustomFrameView CLASS.
"""
####################################################################
HERE I DISABLE ALL OTHER WIDGETS IN THE CLASS A VIEW.
####################################################################
self.view.var.set("")
def func2(self, *_: Any) -> None:
var= self.view.var.get()
####################################################################
HERE I WANT TO READ A FILE IN A THREAD WHICH TAKES A FEW SECONDS. I
WANT TO START A PROGRESSBAR FROM CLASS B AND LET IT RUN UNTIL THE
FILE IS COMPLETALLY READ.
####################################################################
####################################################################
HERE I WANT TO ENABLE ALL OTHER WIDGETS IN THE CLASS A VIEW AGAIN.
####################################################################
controllers/class_b_controller.py
class ClassBController:
"""
Functionality of the class B view.
"""
def __init__(self, view: ctk.CTkFrame) -> None:
self.view: ctk.CTkFrame = view
####################################################################
SOMEWHERE HERE I THINK IS THE BEST PLACE FOR THE FUNCTIONALITY OF
THE PROGRESSBAR.
####################################################################
app.py
class AppView(ctk.CTk):
"""
Layout of the main application.
"""
def __init__(self) -> None:
super().__init__()
class AppController:
"""
Functionality of the main application.
"""
def __init__(self, view: ctk.CTk) -> None:
self.view: ctk.CTk = view
def main() -> None:
"""
Sets up the main window and event loop.
"""
app_view = AppView()
app = AppController(app_view)
MainController(app.view)
app.view.mainloop()
if __name__ == "__main__":
main()
I tried using decoraters or and an observer pattern but failed completally.
PS: This is my first time asking something on StackOverflow. I hope i made it simple and clear and didnt forget anything. Thank you in advance!