diff --git a/controller/HomeController.py b/controller/HomeController.py index 2f0a090..c9fac8b 100644 --- a/controller/HomeController.py +++ b/controller/HomeController.py @@ -76,13 +76,22 @@ class HomeController: # END View events # START Controller methods - def on_quit(self) -> None: + def on_quit(self) -> bool: """ [event function for controller] => Call this event when a request to exit is thrown. """ - self.__download_task.stop() - print("Quit... homecontroller END") + if self.__download_task and self.__download_task.is_alive(): + if self.__main_controller.show_question_dialog( + "Are you sure?", + "Do you really want to quit while the download is running?\nThis will stop the download." + ): + self.__download_task.stop() + self.__download_task.join() + return False + else: + return True + print("Quit... homecontroller END") # REMOVE # END Controller methods # START Task methods @@ -96,7 +105,7 @@ class HomeController: * :url: -> Url for webpic. * :name: -> Working dir name for webpic. """ - print("start callback called") + print("start callback called") # REMOVE self.__view.clear_logs() if self.__webpic.download(url, name): self.__view.show_success_message("The download has been successfully completed.") @@ -110,6 +119,6 @@ class HomeController: function will keep its controller context. In short it's as if the thread was launched in the controller and the execution never left it. """ - print("stop callback called") + print("stop callback called") # REMOVE self.__webpic.stop() # END Task methods \ No newline at end of file diff --git a/controller/MainController.py b/controller/MainController.py index 7538188..a0f7503 100644 --- a/controller/MainController.py +++ b/controller/MainController.py @@ -1,3 +1,6 @@ +import os + + class MainController: """ Controller - MainController @@ -19,7 +22,7 @@ class MainController: """ Constructor - :config: -> The application configuration (a dictionary). + * :config: -> The application configuration (a dictionary). """ # Setup variables self.__config = config @@ -31,17 +34,17 @@ class MainController: [function for view] => Allow to define the controller view. - :view: -> The view that this controller manage. + * :view: -> The view that this controller manage and setup it. """ self.__view = view + view.set_window_title(self.get_config('app_name')) def on_open_folder(self) -> None: """ [event function for view] => Event launch when you ask to open the current folder. """ - # TODO on_open_folder - print("on_open_folder") # TODO remove + os.startfile(self.get_config('app_folder')) def on_quite(self) -> None: """ @@ -77,7 +80,7 @@ class MainController: [function for controller] => Allows you to request a frame change in the main window. - :frame: -> The frame we want to display on the window instead of the current frame. + * :frame: -> The frame we want to display on the window instead of the current frame. """ self.__view.show_frame(frame) @@ -86,16 +89,34 @@ class MainController: [function for controller] => Allows controllers to access the application's configuration. - :name: -> The name of the configuration parameter for which we want to access the configured value. + * :name: -> The name of the configuration parameter for which we want to access the configured value. """ if self.__config.get(name): return self.__config.get(name) else: raise ValueError("Unable to find a configuration with this name") + + def show_question_dialog(self, title: str='title', message: str='question?', icon: str='question') -> bool: + """ + [function for controller] + => Ask a question to the user and block until he answers with yes or no. + + * :title: -> + * :message: -> + * :icon: -> + """ + return self.__view.show_question_dialog(title, message, icon) # END Controller methods # START Controller events def subscribe_to_quite_event(self, callback) -> None: - # TODO + """ + [function for controller] + => Subscription function allowing to be warned if a request to quit occurs. + In the case where the callback function returns False the process continues + but if the callback returns True the process is aborted. + + * :callback: -> Callback function that will be called when a request to exit occurs. + """ self.__quite_event_subscribers.append(callback) # END Controller events diff --git a/model/WebPicDownloader.py b/model/WebPicDownloader.py index b7efd98..5da7272 100644 --- a/model/WebPicDownloader.py +++ b/model/WebPicDownloader.py @@ -1,4 +1,5 @@ import os +import sys from urllib import request from urllib.error import HTTPError, URLError from bs4 import BeautifulSoup, Tag, ResultSet @@ -138,6 +139,7 @@ class WebPicDownloader(): Stop the downloading and processing of images (method for use in a thread). """ self.thread_run = False + sys.exit(0) def download(self, url: str, folder_name: str) -> bool: """ @@ -158,8 +160,10 @@ class WebPicDownloader(): self.messenger(f"WebPicDownloader found {len(images)} images on the website.") for i, img in enumerate(images): + print("check") + print(id(self)) if not self.thread_run: - exit() + print("return") try: self.messenger(f"Start downloading image {i}.") img_link = self.__find_img_link(img) diff --git a/util/AsyncTask.py b/util/AsyncTask.py index 05f9021..e5bb3c6 100644 --- a/util/AsyncTask.py +++ b/util/AsyncTask.py @@ -45,7 +45,9 @@ class AsyncTask(threading.Thread): [Internal function of (threading.Thread)] [!] : This function must not be used! Start the task with {AsyncTask.start()} ! """ + print("runn") self.__run_callback(*self.__run_args) + print("stopp") def stop(self) -> None: """ diff --git a/view/HomeView.py b/view/HomeView.py index a0df3d1..5bb25ca 100644 --- a/view/HomeView.py +++ b/view/HomeView.py @@ -20,14 +20,20 @@ class HomeView(ttk.Frame): # Constructor def __init__(self, parent, controller: HomeController): - super().__init__(parent) + """ + Constructor - # Init view - self.__init_content() + * :parent: -> The main windows container. + * :controller: -> The view controller + """ + super().__init__(parent) # Save and setup main controller self.__controller = controller controller.set_view(self) + + # Init view + self.__init_content() # START Internal function def __init_content(self) -> None: diff --git a/view/InfoView.py b/view/InfoView.py index f497a44..82ef8cd 100644 --- a/view/InfoView.py +++ b/view/InfoView.py @@ -19,6 +19,12 @@ class InfoView(ttk.Frame): # Constructor def __init__(self, parent, controller: InfoController): + """ + Constructor + + * :parent: -> The main windows container. + * :controller: -> The view controller + """ super().__init__(parent) # create widgets diff --git a/view/MainWindow.py b/view/MainWindow.py index 3ea0a30..42d9dfa 100644 --- a/view/MainWindow.py +++ b/view/MainWindow.py @@ -1,4 +1,5 @@ import tkinter as tk +from tkinter import messagebox from controller.MainController import MainController @@ -6,12 +7,12 @@ class MainWindow(tk.Tk): """ View - MainWindow - dec... + TODO dec... @author Jérémi Nihart / EndMove @link https://git.endmove.eu/EndMove/WebPicDownloader - @version 1.0.0 - @since 2022-08-30 + @version 1.0.1 + @since 2022-09-04 """ # Variables __controller: MainController = None @@ -20,6 +21,11 @@ class MainWindow(tk.Tk): # Constructor def __init__(self, controller: MainController) -> None: + """ + Constructor + + * :controller: -> The main application cpntroller. + """ super().__init__() # Init view repository @@ -40,10 +46,10 @@ class MainWindow(tk.Tk): [internal function] => Initialize window parameters """ - self.title('Tkinter app') + # self.title('My tkinter app') # self.geometry('450x250') self.resizable(False, False) - self.config(bg='#f7ef38') + # self.config(bg='#f7ef38') def __init_top_menu(self) -> None: """ @@ -53,7 +59,7 @@ class MainWindow(tk.Tk): main_menu = tk.Menu(self) col1_menu = tk.Menu(main_menu, tearoff=0) - col1_menu.add_command(label="Open folder", command=self.__controller.on_open_folder) + col1_menu.add_command(label="Open app folder", command=self.__controller.on_open_folder) col1_menu.add_separator() col1_menu.add_command(label="Quit", command=self.__controller.on_quite) main_menu.add_cascade(label="File", menu=col1_menu) @@ -74,23 +80,32 @@ class MainWindow(tk.Tk): # END Internal methods # START App methods - def add_view(self, frame, view): + def add_view(self, frame, view) -> None: """ [function for app] - :frame: -> the frame id of the view to add. + * :frame: -> the frame id of the view to add. """ self.__views[frame] = view # END App methods # START Controller methods - def show_frame(self, frame): + def set_window_title(self, title: str) -> None: + """ + [function for controller] + => Sets the title of the main window. + + * :title: -> Window title. + """ + self.title(title) + + def show_frame(self, frame) -> None: """ [function for app & controller] => Allows to display the selected frame provided that it has been previously added to the frame dictionary. - :frame: -> the frame if of the view to display. + * :frame: -> the frame if of the view to display. """ if self.__views.get(frame): if self.__frame_id: @@ -102,8 +117,28 @@ class MainWindow(tk.Tk): def close_window(self) -> None: """ - [function for app & controller] - TODO + [function for controller] + => Closes the main window and stops the program from the controller. """ self.destroy() + + def show_question_dialog(self, title: str, message: str, icon: str='question') -> bool: + """ + [function for controller] + => TODO DESC + + * :message: -> + * RETURN -> + """ + return messagebox.askquestion(title, message, icon=icon) + + def show_information_dialog(self, title: str, message: str, icon: str='information') -> None: + """ + [function for controller] + => TODO DESC + + * :message: -> + * RETURN -> + """ + messagebox.showinfo(self, title, message, icon=icon) # END Controller methods