diff --git a/Application.py b/Application.py index fcd40b5..87ca7e6 100644 --- a/Application.py +++ b/Application.py @@ -4,6 +4,8 @@ import os import sys from Libraries.BRS_Python_Libraries.BRS.Debug.LoadingLog import LoadingLog +from Libraries.BRS_Python_Libraries.BRS.Network.APIs.GitHub import GitHub +from Programs.Local.Updating.GitUpdating import DownloadLatestVersion LoadingLog.Start("Application.py") #===================================================================# # Imports @@ -58,6 +60,7 @@ #====================================================================# # Classes #====================================================================# +from Programs.Pages.DownloadProgress import DownloadProgress_Screens # ------------------------------------------------------------------------ LoadingLog.Class("Application") class Application(MDApp): @@ -106,7 +109,6 @@ def build(self): #Window.left = -1024 #Window.top = 600 #Window.fullscreen = False - Window.size = (720, 576) # Create screen manager diff --git a/Libraries/BRS_Python_Libraries b/Libraries/BRS_Python_Libraries index ae97071..3b254ea 160000 --- a/Libraries/BRS_Python_Libraries +++ b/Libraries/BRS_Python_Libraries @@ -1 +1 @@ -Subproject commit ae97071715290b51d29cf140ca9f35c6885d821b +Subproject commit 3b254ea003211b5297266300193311e6b1fa2931 diff --git a/Local/Drivers/Debugger/Programs/GUI/Navigation.py b/Local/Drivers/Debugger/Programs/GUI/Navigation.py index 0a60e47..69df259 100644 --- a/Local/Drivers/Debugger/Programs/GUI/Navigation.py +++ b/Local/Drivers/Debugger/Programs/GUI/Navigation.py @@ -12,7 +12,6 @@ #====================================================================# # Loading Logs #====================================================================# -from msilib.schema import Icon from Libraries.BRS_Python_Libraries.BRS.Debug.LoadingLog import LoadingLog LoadingLog.Start("AppLoading.py") #====================================================================# diff --git a/Programs/Local/Loading/AppLoadingHandler.py b/Programs/Local/Loading/AppLoadingHandler.py index a94e9fa..f06c940 100644 --- a/Programs/Local/Loading/AppLoadingHandler.py +++ b/Programs/Local/Loading/AppLoadingHandler.py @@ -13,6 +13,9 @@ # Loading Logs #====================================================================# from Libraries.BRS_Python_Libraries.BRS.Debug.LoadingLog import LoadingLog +from Programs.Local.Updating.GitUpdating import CreateUpdatePopUp, DownloadLatestVersion +from Programs.Pages.DownloadProgress import DownloadProgress_Screens +from Programs.Pages.ProfileMenu import ProfileMenu_Screens LoadingLog.Start("AppLoadingHandler.py") #====================================================================# # Imports @@ -834,6 +837,7 @@ def KontrolGitHub() -> bool: Debug.Log("Kontrol's GitHub verified") LoadingSteps[step][ParamEnum.ErrorType] = ErrorTypeEnum.Success LoadingSteps[step][ParamEnum.ErrorMessage] = _("Good") + Debug.End() return False LoadingLog.Function("KontrolGitHub_CallBack") @@ -866,6 +870,11 @@ def KontrolGitHub_CallBack() -> bool: if(LoadingSteps[step][ParamEnum.ErrorType] == ErrorTypeEnum.Warning): Debug.Warn("Some things were not right. Appending warning windows for future display") PopUpsHandler.Add(PopUpTypeEnum.Warning, Message = LoadingSteps[step][ParamEnum.ErrorMessage]) + + DownloadProgress_Screens.SetCaller(ProfileMenu_Screens, "ProfileMenu", _("Downloading latest version of Kontrol"), DownloadLatestVersion) + DownloadProgress_Screens.SetExiter(ProfileMenu_Screens, "ProfileMenu") + CreateUpdatePopUp(ProfileMenu_Screens) + Debug.End() return False diff --git a/Programs/Local/Updating/GitUpdating.py b/Programs/Local/Updating/GitUpdating.py new file mode 100644 index 0000000..c770947 --- /dev/null +++ b/Programs/Local/Updating/GitUpdating.py @@ -0,0 +1,152 @@ +#====================================================================# +# File Information +#====================================================================# +""" + GitUpdating.py + ============= + This file contains BRS Kontrol's specific functions used to update + and download new versions of itself. +""" +#====================================================================# +# Loading Logs +#====================================================================# +from Libraries.BRS_Python_Libraries.BRS.Debug.LoadingLog import LoadingLog +from Programs.Pages.DownloadProgress import DownloadProgress_Screens +LoadingLog.Start("GitUpdating.py") +#====================================================================# +# Imports +#====================================================================# +#region ------------------------------------------------------ Python +LoadingLog.Import("Python") +import os +import subprocess +import asyncio +import git +import threading +#endregion +#region --------------------------------------------------------- BRS +LoadingLog.Import("Libraries") +from Libraries.BRS_Python_Libraries.BRS.Network.APIs.GitHub import GitHub, ManualGitHub, DownloadRepositoryAtPath +from Libraries.BRS_Python_Libraries.BRS.Utilities.Enums import Execution +from Libraries.BRS_Python_Libraries.BRS.Debug.consoleLog import Debug +from Libraries.BRS_Python_Libraries.BRS.Utilities.LanguageHandler import _ +#endregion +#region -------------------------------------------------------- Kivy +LoadingLog.Import("Kivy") +#endregion +#region ------------------------------------------------------ KivyMD +LoadingLog.Import("KivyMD") +from kivymd.uix.progressbar import MDProgressBar +#endregion +LoadingLog.Import("Local") +from ...Pages.PopUps import PopUpsHandler, PopUpTypeEnum +#====================================================================# +# Functions +#====================================================================# +# ---------------------------------------------------------------- +def CreateUpdatePopUp(cancelHandler=None) -> Execution: + """ + CreateUpdatePopUp + ================= + Summary: + -------- + This function is used to create and queue a specific + pop up displayed to the user when their Kontrol version + is out of date. This pop up prompts the user with a + simple question asking them if they want to update their + Kontrol or not. + + If they click on Yes, they will be taken to the update page + otherwise, pop ups will exit to their caller screen. + + Args: + -------- + - `cancelHandler`: screen handler that will be called if the user cancels + + Returns: + -------- + - `Execution.Passed` = pop up created + - `Execution.Unecessary` = git versions matched + - `Execution.Failed` = Not enough git api requests left + """ + Debug.Start("CreateUpdatePopUp") + + Debug.Log("Getting Kontrol versions") + if(ManualGitHub.GetRequestsLeft() > 5): + newVersion = GitHub.LatestTag + currentVersion = GitHub.CurrentTag + if(newVersion != currentVersion): + Debug.Log("Creating pop up") + PopUpsHandler.Add(Icon="update", + Message=(_("Do you want to update Kontrol to its latest version?") + ("\nYour version: ") + currentVersion + _("\nNew version: ") + newVersion), + CanContinue=True, + Type=PopUpTypeEnum.Custom, + ButtonAText=_("Update"), + ButtonBText=_("Cancel"), + ButtonAHandler=DownloadProgress_Screens, + ButtonBHandler=cancelHandler + ) + else: + Debug.Error("Versions match.") + return Execution.Unecessary + else: + Debug.Error("Not enough API requests left to update Kontrol") + Debug.End() + return Execution.Failed + + Debug.End() + return Execution.Passed +# ---------------------------------------------------------------- +def DownloadLatestVersion(progressBar:MDProgressBar, DownloadProgressHandler) -> Execution: + """ + DownloadLatestVersion.py + ======================== + Summary: + -------- + This function will create a new folder where Kontrol's + Git folder is located. It will then download using + git terminal functions the new version of Kontrol + in that folder. Doing so ensures that if Kontrol could + not be fully downloaded, the old version is not lost. + + The name of that folder will be the name of the latest + release of Kontrol. + + Args: + ------- + - `progressBar` : MDProgressbar updated by download functions running asynchronously. This way your kivy app gets a live update of the download's progression. + - `FailedDownload` : Function executed if the download fails + - `GoodDownload` : Function to execute if the download works + + Returns: + ------- + - `Execution.Passed` = Kontrol was successfully downloaded in a new folder + - `Execution.Failed` = Kontrol failed to download a new version for X reason. + - `Execution.NoConnection` = Kontrol failed to download because internet is not available + """ + Debug.Start("DownloadLatestVersion") + + currentPath = os.getcwd() + parentPath = currentPath.rstrip(os.path.basename(currentPath)) + Debug.Log(f"Parent folder: {parentPath}") + + Debug.Log("Creating new folder") + directoryName = GitHub.LatestTag + directoryName = "0.0.0" + directoryPath = os.path.join(parentPath, directoryName) + Debug.Log(f"New folder's path: {directoryPath}") + + try: + os.mkdir(directoryPath) + Debug.Log("Success: Directory created") + except: + Debug.Error(f"Failed to create directory with name: {directoryName}") + Debug.End() + return Execution.Failed + + Debug.Log("Downloading Kontrol in new folder") + Debug.End() + return DownloadRepositoryAtPath("https://github.com/LyamBRS/BRS-Kontrol.git", directoryPath, progressBar, DownloadProgressHandler) + + +LoadingLog.End("GitUpdating.py") \ No newline at end of file diff --git a/Programs/Pages/DownloadProgress.py b/Programs/Pages/DownloadProgress.py new file mode 100644 index 0000000..bf9f6b4 --- /dev/null +++ b/Programs/Pages/DownloadProgress.py @@ -0,0 +1,426 @@ +#====================================================================# +# File Information +#====================================================================# + +#====================================================================# +from Libraries.BRS_Python_Libraries.BRS.Debug.LoadingLog import LoadingLog +from Libraries.BRS_Python_Libraries.BRS.Utilities.Enums import Execution, FileIntegrity +LoadingLog.Start("DownloadProgress.py") +#====================================================================# +# Imports +#====================================================================# +#region ------------------------------------------------------ Python +LoadingLog.Import("Python") +#endregion +#region --------------------------------------------------------- BRS +LoadingLog.Import("Libraries") +from Libraries.BRS_Python_Libraries.BRS.Debug.consoleLog import Debug +from Libraries.BRS_Python_Libraries.BRS.Utilities.AppScreenHandler import AppManager +from Libraries.BRS_Python_Libraries.BRS.Utilities.LanguageHandler import _ +from Programs.Local.GUI.Cards import DeviceDriverInstallerCard +#endregion +#region -------------------------------------------------------- Kivy +LoadingLog.Import("Kivy") +from kivy.uix.screenmanager import ScreenManager, Screen, WipeTransition, CardTransition, SlideTransition +from kivy.clock import Clock +#endregion +#region ------------------------------------------------------ KivyMD +LoadingLog.Import("KivyMD") +from kivymd.uix.label import MDLabel +from kivymd.uix.boxlayout import MDBoxLayout +from kivymd.uix.spinner import MDSpinner +from kivymd.uix.progressbar import MDProgressBar +#endregion +#region ------------------------------------------------------ Kontrol +LoadingLog.Import("Local") +from ..Local.GUI.Navigation import AppNavigationBar +from ..Local.GUI.Cards import DeviceDriverCard +from ..Pages.PopUps import PopUpsHandler, PopUps_Screens, PopUpTypeEnum +#endregion +#====================================================================# +# thread class +#====================================================================# +class DownloadProgressHandler(): + """ + DownloadProgressHandler: + ======================== + Summary: + -------- + This class's purpose is to hold static instances of functions + and members which will be used throughout threads and + asynchronous functions executed throughout download processes. + This allows your KivyMD application to keep track of the + download progress and execute fails or good function depending + on the results of the download. + """ + downloadResult = None + """ + Variable which holds the result of the download attempt. + if set to `Execution.Passed`, the download passed. + if set to `Execution.Failed`, the download didnt work. + Defaults to `None` + """ + + def GoodDownload() -> Execution: + """ + DownloadWorked: + =============== + Function to execute if the download succeeds + replace it with your own function before calling the + downloadprogress_screens class. + """ + PopUpsHandler.Clear() + PopUpsHandler.Add(Icon="check-decagram", + Message=_("The download was successful and has installed successfully."), + ButtonAText="Ok", + ButtonAHandler=DownloadProgress_Screens._exitClass, + Type=PopUpTypeEnum.Custom) + PopUps_Screens.SetCaller(DownloadProgress, "DownloadProgress") + PopUps_Screens.SetExiter(DownloadProgress, "DownloadProgress") + PopUps_Screens.Call() + pass + + def FailedDownload() -> Execution: + """ + DownloadFailed: + =============== + Function to execute if the download succeeds + replace it with your own function before calling the + downloadprogress_screens class. + """ + PopUpsHandler.Clear() + PopUpsHandler.Add(Icon="check-decagram", + Message=_("The download failed and couldn't complete."), + ButtonAText="Damn", + ButtonAHandler=DownloadProgress_Screens._exitClass, + Type=PopUpTypeEnum.Custom) + PopUps_Screens.SetCaller(DownloadProgress, "DownloadProgress") + PopUps_Screens.SetExiter(DownloadProgress, "DownloadProgress") + PopUps_Screens.Call() + pass +#====================================================================# +# Screen class +#====================================================================# +LoadingLog.Class("DownloadProgress_Screens") +class DownloadProgress_Screens: + """ + DownloadProgress_Screens: + ================ + Summary: + -------- + This class allows the handling of the transitional screen + :class:`DriverMenu`. + + Description: + ------------ + This class holds the different types of callers of the AppLoading + screen as well as the different exit screens that this transitional + screen can go to. You must specify the names of the wanted exit screens + prior to calling the transition function. + + An exit screen is basically which screens should be loaded if something + happens in the transition screen. + """ + #region ---- Members + _exitClass = None + _callerClass = None + + _callerName = None + _exitName = None + + _callerTransition = SlideTransition + _exitTransition = SlideTransition + + _callerDirection = "up" + _exitDirection = "up" + + _callerDuration = 0.5 + _exitDuration = 0.5 + + _downloadName:str = None + _downloadFunction = None + #endregion + #region ---- Methods + def SetExiter(screenClass, screenName:str, transition=SlideTransition, duration:float=0, direction:str="up") -> bool: + """ + Function which sets the screen that this screen should transition to on exit. + This allows transitional screens to be reused by any screens at any time. + + Args: + `screenClass (_type_)`: The screen class of the screen this handler should transition to on exit. + `screenName (str)`: The name of the screen class. It needs to be the same as :ref:`screenClass`. + `transition`: Optional kivy transition class. Defaults as `WipeTransition` + `duration (float)`: Optional specification of the transition's duration. Defaults to 0.5 seconds + `direction (str)`: Optional direction which the transition should go. Defaults as `"up"`. + + Returns: + bool: `True`: Something went wrong. `False`: Success + """ + # Attempt to add the screen class as a widget of the AppManager + DownloadProgress_Screens._exitClass = screenClass + DownloadProgress_Screens._exitName = screenName + + DownloadProgress_Screens._exitTransition = transition + DownloadProgress_Screens._exitDuration = duration + DownloadProgress_Screens._exitDirection = direction + return False + + def SetCaller(screenClass, screenName:str, downloadName:str, downloadFunction, transition=SlideTransition, duration:float=0, direction:str="down") -> bool: + """ + SetCaller: + ========== + Summary: + -------- + Function which sets the screen to load if an error occured. This is used to "go back" to whoever attempted + to call this screen. + + Args: + ----- + `downloadName (str)`: name of the download + `downloadFunction (str)`: Function which will be executed to download whatever. NEEDS A CALLBACK FUNCTION AS INPUT PARAMETER. + `screenClass (_type_)`: The screen class of the screen that wants to transition to this one. + `screenName (str)`: The name of the screen class. It needs to be the same as :ref:`screenClass`. + `transition`: Optional kivy transition class. Defaults as `WipeTransition` + `duration (float)`: Optional specification of the transition's duration. Defaults to 0.5 seconds + `direction (str)`: Optional direction which the transition should go. Defaults as `"up"`. + + Returns: + bool: `True`: Something went wrong. `False`: Success + """ + # Attempt to add the screen class as a widget of the AppManager + DownloadProgress_Screens._callerClass = screenClass + DownloadProgress_Screens._callerName = screenName + + DownloadProgress_Screens._downloadName = downloadName + DownloadProgress_Screens._downloadFunction = downloadFunction + + DownloadProgress_Screens._callerTransition = transition + DownloadProgress_Screens._callerDuration = duration + DownloadProgress_Screens._callerDirection = direction + return False + + def _Exit(*args) -> bool: + """ + Attempt to go to the specified screen that was set using :ref:`SetExiter`. + + Returns: + bool: `True`: Something went wrong and the wanted exiter screen can't be loaded. `False`: Success + """ + Debug.Start("DownloadProgress_Screens -> _Exit()") + # Attempt to add the screen class as a widget of the AppManager + try: + # Check if exit class was specified + Debug.Log("Checking exit class") + if(DownloadProgress_Screens._exitClass == None): + Debug.Error("Attempted to call exit while no exit class were specified") + Debug.End() + return True + + Debug.Log("Checking exit name") + if(DownloadProgress_Screens._exitName == None): + Debug.Error("Attempted to call exit while no exit name were specified") + Debug.End() + return True + + Debug.Log("Checking exit class Call()") + try: + Debug.Log("Trying to call exit class caller.") + DownloadProgress_Screens._exitClass.Call() + Debug.Log("Success") + Debug.End() + return False + except: + Debug.Log("Class specified wasn't an _Screen class.") + AppManager.manager.add_widget(DownloadProgress_Screens._exitClass(name=DownloadProgress_Screens._exitName)) + except: + Debug.Error("AppLoading -> Exception occured.") + Debug.End() + return True + + # Attempt to call the added screen + AppManager.manager.transition = DownloadProgress_Screens._exitTransition() + AppManager.manager.transition.duration = DownloadProgress_Screens._exitDuration + AppManager.manager.transition.direction = DownloadProgress_Screens._exitDirection + + # try: + AppManager.manager.current = DownloadProgress_Screens._exitName + # except: + # return True + Debug.End() + return False + + def Call(*args) -> bool: + """ + Attempt to go to the main screen that is being handled by this class. + + Returns: + bool: `True`: Something went wrong and the screen can't be loaded. `False`: Success + """ + Debug.Start("DownloadProgress_Screens -> Call()") + # Attempt to add the screen class as a widget of the AppManager + # try: + # Check if caller class was specified + Debug.Log("Checking caller class") + if(DownloadProgress_Screens._callerClass == None): + Debug.Error("No caller class specified.") + Debug.End() + return True + + Debug.Log("Checking caller name") + if(DownloadProgress_Screens._callerName == None): + Debug.Error("No caller name specified.") + Debug.End() + return True + + Debug.Log("Attempting to add widget") + AppManager.manager.add_widget(DownloadProgress(name="DownloadProgress")) + # except: + Debug.Error("Exception occured while handling Call()") + Debug.End() + # return True + + # Attempt to call the added screen + AppManager.manager.transition = DownloadProgress_Screens._callerTransition() + AppManager.manager.transition.duration = DownloadProgress_Screens._callerDuration + AppManager.manager.transition.direction = DownloadProgress_Screens._callerDirection + + # try: + AppManager.manager.current = "DownloadProgress" + Debug.Log("Screen successfully changed") + # except: + # Debug.Error("Failed to add DownloadProgress as current screen.") + # Debug.End() + # return True + Debug.End() + return False + #endregion +#====================================================================# +# Classes +#====================================================================# + +LoadingLog.Class("DownloadProgress") +class DownloadProgress(Screen): + """ + DownloadProgress: + ================= + Summary + ------- + Screen class handling the construction and deleting of a + download screen which displays the current download time + left to a download function through callbacks and threadings. + """ + #region --------------------------- MEMBERS + progressBar:MDProgressBar = None + #endregion + #region --------------------------- CONSTRUCTOR + def __init__(self, **kwargs): + super().__init__(**kwargs) + Debug.Start("DriverMenu -> __init__") + Debug.End() + #endregion +# ------------------------------------------------------------------------ + def on_pre_enter(self, *args): + """ + Load the JSONs available, and create 1 profile card + per available profiles. + """ + Debug.Start("DownloadProgress -> on_pre_enter") + + self.padding = 0 + self.spacing = 0 + + #region ---- Background + import os + from Libraries.BRS_Python_Libraries.BRS.GUI.Utilities.Application_Themes import GetBackgroundImage + from Libraries.BRS_Python_Libraries.BRS.Utilities.FileHandler import AppendPath + path = os.getcwd() + background = GetBackgroundImage(AppendPath(path, "/Libraries/Backgrounds/Menus/Dark.png"), + AppendPath(path, "/Libraries/Backgrounds/Menus/Light.png")) + #endregion + + #region ---------------------------- Layouts + self.Layout = MDBoxLayout() + self.Layout.orientation = "vertical" + #endregion + + #region ---------------------------- Spinner + self.spinner = MDSpinner() + self.spinner.size_hint = (0.25, 0.25) + self.spinner.pos_hint = {"center_x":0.5, "center_y":0.5} + #endregion + + #region ---------------------------- Progress bar + self.progressBar = MDProgressBar() + self.progressBar.value = 0 + self.progressBar.max = 100 + self.progressBar.type = "determinate" + self.progressBar.size_hint = (1, None) + self.progressBar.pos_hint = {"center_x":0.5, "center_y":0.5} + #endregion + + Clock.schedule_once(self.CheckDownload, 1) + + self.add_widget(background) + self.Layout.add_widget(self.spinner) + self.Layout.add_widget(self.progressBar) + self.add_widget(self.Layout) + Debug.End() +# ------------------------------------------------------------------------ + def on_enter(self, *args): + """ + Function called when the screen is fully entered in view. + In this case, once the view is fully loaded, the cards + will slowly elevate to the wanted value. + """ + Debug.Start("DownloadProgress -> on_enter") + Debug.Log("Starting specified download...") + result = DownloadProgress_Screens._downloadFunction(self.progressBar, DownloadProgressHandler) + if(result != Execution.Passed): + Debug.Error("Failed to launch download") + else: + Debug.Log("Download started") + Debug.End() +# ------------------------------------------------------------------------ + def on_pre_leave(self, *args): + """ + Function called before the screen is fully left. This gives + us time to animate the screen leaving before destroying the + objects and instances it created. + """ + Debug.Start("DownloadProgress -> on_pre_leave") + Debug.End() +# ------------------------------------------------------------------------ + def on_leave(self, *args): + """ + Function called when the screen is fully out of view. + """ + Debug.Start("DownloadProgress -> on_leave") + Debug.Log("Attempting to remove self from AppManager's widgets") + self.clear_widgets() + AppManager.manager.remove_widget(self) + Debug.End() + self.clear_widgets() +# ------------------------------------------------------------------------ + def CheckDownload(self, *args): + """ + CheckDownload + ============= + Summary: + -------- + Check if the download is finished at a constant interval. + Will constantly reschedule itself until download is finished. + """ + Debug.Start("CheckDownload") + if(DownloadProgressHandler.downloadResult == Execution.Passed): + DownloadProgressHandler.GoodDownload() + DownloadProgressHandler.downloadResult = None + return + + if(DownloadProgressHandler.downloadResult == Execution.Failed): + DownloadProgressHandler.FailedDownload() + DownloadProgressHandler.downloadResult = None + return + + Clock.schedule_once(self.CheckDownload, 1) + Debug.End() +LoadingLog.End("DriverMenu.py") \ No newline at end of file diff --git a/Programs/Pages/PopUps.py b/Programs/Pages/PopUps.py index 34dbfe9..dc9c7d9 100644 --- a/Programs/Pages/PopUps.py +++ b/Programs/Pages/PopUps.py @@ -358,27 +358,27 @@ def Call() -> bool: # Attempt to add the screen class as a widget of the AppManager if(len(PopUpsHandler.PopUps) > 0): Debug.Log("Some pop ups can be displayed.") - try: + # try: # Check if exit class was specified - Debug.Log("Checking caller class") - if(PopUps_Screens._callerClass == None): - Debug.Error("No caller class specified.") - Debug.End() - return True - - Debug.Log("Checking caller name") - if(PopUps_Screens._callerName == None): - Debug.Error("No caller name specified.") - Debug.End() - return True - - Debug.Log("Adding widget") - AppManager.manager.add_widget(PopUps(name="PopUps")) - except: - Debug.Error("Exception occured while handling Call()") + Debug.Log("Checking caller class") + if(PopUps_Screens._callerClass == None): + Debug.Error("No caller class specified.") + Debug.End() + return True + + Debug.Log("Checking caller name") + if(PopUps_Screens._callerName == None): + Debug.Error("No caller name specified.") Debug.End() return True + Debug.Log("Adding widget") + AppManager.manager.add_widget(PopUps(name="PopUps")) + # except: + # Debug.Error("Exception occured while handling Call()") + # Debug.End() + # return True + # Attempt to call the added screen AppManager.manager.transition = PopUps_Screens._callerTransition() AppManager.manager.transition.duration = PopUps_Screens._callerDuration diff --git a/Test2.py b/Test2.py index 392f581..690c3e7 100644 --- a/Test2.py +++ b/Test2.py @@ -1,41 +1,58 @@ +from kivymd.app import MDApp +from kivymd.uix.progressbar import MDProgressBar +import asyncio +import git +import threading +from enum import Enum -# To change the kivy default settings -# we use this module config -from kivy.config import Config - -# 0 being off 1 being on as in true / false -# you can use 0 or 1 && True or False -Config.set('graphics', 'resizable', '0') - -# fix the width of the window -Config.set('graphics', 'width', '1024') -Config.set('graphics', 'height', '600') - -# import kivy module -import kivy - -# this restrict the kivy version i.e -# below this kivy version you cannot use the app -kivy.require("1.9.1") - -# base Class of your App inherits from the App class. -# app:always refers to the instance of your application -from kivy.app import App - -# if you not import label and use it through error -from kivy.uix.label import Label - -# defining the App class -class MyLabelApp(App): +class Execution(Enum): + Passed = 1 + Failed = 2 + +async def download_git_repo_async(repo_url: str, local_path: str, callback_fn=None) -> Execution: + try: + # Clone the Git repository asynchronously + await asyncio.create_task(git.Repo.clone_from(repo_url, local_path, progress=callback_fn)) + return Execution.Passed + except Exception as e: + print(f"Failed to download Git repo: {e}") + return Execution.Failed + +def start_download_thread(repo_url: str, local_path: str, progress_bar: MDProgressBar, callback_fn=None): + # Define the function to run in the separate thread + def download_thread(): + # Create a new event loop for this thread + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + # Define the progress update function for the callback + def update_progress(op_code, cur_count, max_count=None, message=''): + # Update the progress bar with the current progress + progress = cur_count / max_count if max_count else 0 + progress_bar.value = progress * 100 + + # Call the download_git_repo_async function asynchronously with the progress update callback + loop.run_until_complete(download_git_repo_async(repo_url, local_path, update_progress)) + + # Stop the event loop + loop.stop() + loop.close() + + # Start a new thread for the download function + thread = threading.Thread(target=download_thread) + thread.start() + +class MyApp(MDApp): def build(self): - # label display the text on screen - # markup text with different colour - l2 = Label(text ="[color = ff3333][b]Hello !!!!!!!!!!![/b] [/color]\n [color = 3333ff]GFG !!:):):):)[/color]", - font_size ='20sp', markup = True) - return l2 - -# creating the object -label = MyLabelApp() - -# run the window -label.run() \ No newline at end of file + # Create a progress bar to display the download progress + self.progress_bar = MDProgressBar(value=0, max=100) + return self.progress_bar + + def on_start(self): + # Start the download function in a separate thread + start_download_thread(repo_url='https://github.com/username/repo.git', + local_path='/path/to/local/folder', + progress_bar=self.progress_bar) + +if __name__ == "__main__": + MyApp().run() \ No newline at end of file