diff --git a/.gitignore b/.gitignore index 2e11b42..63b9a5b 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,7 @@ data-backup/ nuitka-crash-report.xml combined_config.txt firefox_config.txt +test/ +assets/ +lang/ +test.py \ No newline at end of file diff --git a/install-linux.sh b/install-linux.sh index 16737a8..ed75c67 100755 --- a/install-linux.sh +++ b/install-linux.sh @@ -16,6 +16,10 @@ pip3 install nuitka pip3 install tqdm +# For Images +#pip3 tkintertable +#pip3 Pillow --upgrade --force --no-cache-dir + # Install additional Python packages pip3 install requests sudo apt install libgirepository1.0-dev -y diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..d9fa933 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +tkinter +tkintertable +customtkinter +Pillow +tqdm +requests \ No newline at end of file diff --git a/src/download_main.py b/src/download_main.py index 7179bcc..39cf0d5 100644 --- a/src/download_main.py +++ b/src/download_main.py @@ -8,6 +8,9 @@ from download_gitlab import download_and_update import download_github import install_main +# Global flag to check if the installation should be canceled +cancel_installation = False + def sanity_check(download_folder,install_folder): # Ensure that the directory structure exists if not os.path.exists(download_folder): @@ -26,22 +29,18 @@ def sanity_check(download_folder,install_folder): with open(download_folder+"config.json", "w") as json_file: json.dump(default_data, json_file, indent=4) -# Placeholder functions for SourceForge and Gitea -def download_from_sourceforge(url, destination, progress_bar, version): - # Placeholder function for downloading from SourceForge - print("Sourceforge not supported yet") - -def download_from_gitea(api_url, project_id, token, destination, progress_bar, version): - # Placeholder function for downloading from Gitea - print("Gita not supported, yet") - def unzip_file(zip_path, extract_path): with zipfile.ZipFile(zip_path, 'r') as zip_ref: zip_ref.extractall(extract_path) os.remove(zip_path) +def cancel_install(): + global cancel_installation + cancel_installation = True + # Function to install all packages def download_all_packages(progress_bar, progress_label, download_location, install_location): + global cancel_installation download_folder = download_location + "/" install_folder = install_location + "/" print(f"Install location: {install_folder}") @@ -54,15 +53,18 @@ def download_all_packages(progress_bar, progress_label, download_location, insta total_packages = len(packages) for index, package in enumerate(packages, start=1): + if cancel_installation: + print("Installation cancelled.") + return + app_name, app_data = package.popitem() source_info = app_data.get("source", {}) source_type = source_info.get("source_type", "") version = app_data.get("version", "") try: - progress_label.set_text(f"Downloading {app_name}") - progress_value = index / total_packages/2 - progress_bar.set_fraction(progress_value) + progress_label = f"Downloading {app_name}" + progress_bar = index / total_packages / 2 if source_type == "github": download_github.download_from_github( @@ -70,8 +72,7 @@ def download_all_packages(progress_bar, progress_label, download_location, insta os.path.join(download_folder,app_name), tqdm, version - ) - #api_url, project_id, token, destination, progress_bar + ) elif source_type == "gitlab": download_and_update( source_info["gitlab_api_url"], @@ -80,12 +81,10 @@ def download_all_packages(progress_bar, progress_label, download_location, insta os.path.join(download_folder,app_name), tqdm, version - ) - #api_url, project_id, private_token, destination_folder, progress_bar, current_version - + ) # Add other source types as needed else: - progress_label.set_text(f"Unsupported source type for {app_name}: {source_type}") + progress_label = f"Unsupported source type for {app_name}: {source_type}" print(f"Unsupported source type for {app_name}: {source_type}") # Check if there are any zip files inside the package folder and unzip them @@ -99,13 +98,14 @@ def download_all_packages(progress_bar, progress_label, download_location, insta except Exception as e: import traceback traceback.print_exc() - progress_label.set_text(f"Error processing {app_name}: {e}") + progress_label = f"Error processing {app_name}: {e}" print(f"Error processing {app_name}: {e}") - install_main.install_all_packages(data) - except FileNotFoundError: - progress_label.set_text(f"Config file not found.") - print("Config file not found.") + install_main.install_all_packages(data) + + except FileNotFoundError: + progress_label = f"Config file not found." + print("Config file not found.") # # Check if the script is executed directly # if __name__ == "__main__": diff --git a/src/gui_main.py b/src/gui_main.py index 0ff63e4..d702424 100644 --- a/src/gui_main.py +++ b/src/gui_main.py @@ -16,10 +16,28 @@ def run_installer(download_folder_tmp): def print_ahoj(): print("ahoj") + # Function to start the installation process + def start_install(): + # Switch to Tab3 + switch_to_tab("Tab3") + + # Create a CTkLabel for displaying progress + progress_label = CTkLabel(master=container, text="Download in progress...") + + # Run download_all_packages in a separate thread + threading.Thread( + target=download_main.download_all_packages, + args=(progress_label, "your_app_name", "./", "./data") + ).start() + + # Pack the label to display it + progress_label.pack(pady=20, padx=20) + + # Dictionary to store tabs and their widgets tabs = { "Tab1": [ - CTkButton, + (CTkButton, {"text": "Install", "command": print_ahoj}), # Tuple containing widget class and details CTkCheckBox, CTkComboBox, CTkEntry, @@ -36,15 +54,27 @@ def run_installer(download_folder_tmp): CTkProgressBar ], "Tab3": [ - CTkButton, - CTkCheckBox, - CTkComboBox + CTkProgressBar, + (CTkButton, {"text": "Start Install", "command": start_install}) ] } # List to store the currently visible widgets visible_widgets = [] + # Variable to store the CTkProgressBar in Tab3 + progress_bar = None + + # Function to switch to a specified tab + def switch_to_tab(tab_name): + global visible_widgets + # Remove currently visible widgets + for widget in visible_widgets: + widget.pack_forget() + # Set the specified tab as the current tab + tab_combobox.set(tab_name) + visible_widgets = create_widgets_for_current_tab() + # Function to switch to the next tab def switch_to_next_tab(): global visible_widgets @@ -78,12 +108,28 @@ def run_installer(download_folder_tmp): # Function to create widgets for the currently selected tab def create_widgets_for_current_tab(): current_tab = tab_combobox.get() - widget_classes = tabs.get(current_tab, []) - widgets = [widget_class(master=container) for widget_class in widget_classes] + widget_specs = tabs.get(current_tab, []) + widgets = [create_widget(spec, container) for spec in widget_specs] for widget in widgets: widget.pack(pady=20, padx=20) return widgets + # Function to create a widget based on its specifications + def create_widget(spec, master): + global progress_bar + if isinstance(spec, tuple): + widget_class, widget_args = spec[0], spec[1] + if widget_class == CTkButton: + # If it's a CTkButton, create the button with specified details + return CTkButton(master=master, **widget_args) + elif widget_class == CTkProgressBar: + # If it's a CTkProgressBar, save the reference to update later + progress_bar = CTkProgressBar(master=master, **widget_args) + return progress_bar + else: + # For other widget classes, create them without additional details + return spec(master=master) + # Create a container frame for widgets container = CTkFrame(app) container.pack() @@ -101,7 +147,7 @@ def run_installer(download_folder_tmp): # Button to trigger the gradual slide-to-left effect CTkButton(master=app, text="Previous", command=switch_to_previous_tab).pack(pady=10) - # Button to print "ahoj" when pressed - CTkButton(master=app, text="Print Ahoj", command=print_ahoj).pack(pady=10) + app.mainloop() - app.mainloop() \ No newline at end of file +# # Example usage +# run_installer("your_download_folder") diff --git a/src/main.py b/src/main.py index 45e5d03..b59fa10 100644 --- a/src/main.py +++ b/src/main.py @@ -1,159 +1,14 @@ import sys -if sys.platform == 'win32': - import os - import tkinter as tk - from tkinter import ttk - import requests - import zipfile - import certifi - import ssl - import subprocess - import tempfile - -def get_temp_folder(): - # Get the current user's temporary directory - temp_dir = tempfile.gettempdir() - - # Create a subdirectory inside the temporary directory - user_temp_folder = os.path.join(temp_dir, 'SpitfireBrowserTmp') - - # Create the directory if it doesn't exist - if not os.path.exists(user_temp_folder): - os.makedirs(user_temp_folder) - - return user_temp_folder - - -def download_file(url, destination, progress_callback=None, label_callback=None): - response = requests.get(url, stream=True) - total_size = int(response.headers.get('content-length', 0)) - block_size = 8192 - current_size = 0 - with open(destination, 'wb') as out_file: - for buffer in response.iter_content(block_size): - if not buffer: - break - out_file.write(buffer) - current_size += len(buffer) - if progress_callback: - progress_callback(current_size, total_size) - if label_callback: - label_callback("Downloading") - - if label_callback: - label_callback("Download Complete") - - -def extract_zip(zip_file, extract_path, label_callback=None): - with zipfile.ZipFile(zip_file, 'r') as zip_ref: - zip_ref.extractall(extract_path) - - if label_callback: - label_callback("Extracting") - - -def download_gtk_dlls(download_path, dll_url, dll_files, progress_callback=None, label_callback=None): - for dll_file in dll_files: - file_url = dll_url + dll_file - - if not os.path.exists(os.path.join(download_path, dll_file)): - download_file(file_url, os.path.join(download_path, dll_file), progress_callback, label_callback) - - # Check if the downloaded file is a zip file, extract it, and remove the zip file - if dll_file.endswith('.zip'): - extract_path = os.path.join(download_path, dll_file.replace('.zip', '')) - extract_zip(os.path.join(download_path, dll_file), extract_path, label_callback) - os.remove(os.path.join(download_path, dll_file)) - - -def set_path_variable(download_path): - - current = subprocess.run("echo %Path%", stdout=subprocess.PIPE, shell=True, text=True) - - output_string = str(current.stdout).strip() + ';' + download_path - - # Split the output_string into individual elements - elements = output_string.split(';') - - # Remove duplicates - unique_elements = list(set(elements)) - - # Join the unique elements back together - output_string_unique = ';'.join(unique_elements) - - # Refresh the system environment variables - subprocess.run("SETX Path " + output_string_unique, shell=True) - - print(f"Added PATH: {output_string_unique}") - - def main(): - if sys.platform != 'win32': - from gui_main import run_installer - run_installer(".") + # do an OS specific precheck + if sys.platform == 'win32': + from main_win import run_init_win + run_init_win() return - - download_path = get_temp_folder() - - dll_url = 'https://downloads.sourceforge.net/project/spitfire-browser/nightly/components/GTK4/' - - # Check if all required files are already present - dll_files = [ - 'libs.zip' - ] - - # Get the names of the folders corresponding to ".zip" files - zip_folders = [filename.replace('.zip', '') for filename in dll_files if filename.endswith('.zip')] - - # Check if all corresponding folders exist - all_folders_exist = all(os.path.exists(os.path.join(download_path, folder)) for folder in zip_folders) - - if not all_folders_exist: - root = tk.Tk() - root.title("Preparing for first start") - root.geometry("240x120") - - progress_var = tk.DoubleVar() - progress = ttk.Progressbar(root, variable=progress_var, length=200) - progress.grid(row=0, column=0, padx=20, pady=20) - - label_var = tk.StringVar() - label = tk.Label(root, textvariable=label_var) - label.grid(row=1, column=0, padx=20, pady=10) - - def update_progress_bar(current_size, total_size): - progress_value = (current_size / total_size) * 100 - progress_var.set(progress_value) - root.update_idletasks() - - def update_label(new_label): - label_var.set(new_label) - root.update_idletasks() - - def start_download(): - download_gtk_dlls(download_path, dll_url, dll_files, update_progress_bar, update_label) - if sys.platform == 'win32': - set_path_variable(download_path) - - for zip_folder in zip_folders: - set_path_variable(os.path.join(download_path, zip_folder)) - - root.destroy() - - - # Run your main application function - - # Run your main application function - from gui_main import run_installer - run_installer(download_path) - - root.after(100, start_download) - root.mainloop() - else: - from gui_main import run_installer - run_installer(download_path) - + + from gui_main import run_installer + run_installer(".") if __name__ == "__main__": main() \ No newline at end of file diff --git a/src/main_win.py b/src/main_win.py new file mode 100644 index 0000000..7ff8947 --- /dev/null +++ b/src/main_win.py @@ -0,0 +1,147 @@ +import os +import tkinter as tk +from tkinter import ttk +import requests +import zipfile +import certifi +import ssl +import subprocess +import tempfile + +def get_temp_folder(): + # Get the current user's temporary directory + temp_dir = tempfile.gettempdir() + + # Create a subdirectory inside the temporary directory + user_temp_folder = os.path.join(temp_dir, 'SpitfireBrowserTmp') + + # Create the directory if it doesn't exist + if not os.path.exists(user_temp_folder): + os.makedirs(user_temp_folder) + + return user_temp_folder + + +def download_file(url, destination, progress_callback=None, label_callback=None): + response = requests.get(url, stream=True) + total_size = int(response.headers.get('content-length', 0)) + block_size = 8192 + current_size = 0 + with open(destination, 'wb') as out_file: + for buffer in response.iter_content(block_size): + if not buffer: + break + out_file.write(buffer) + current_size += len(buffer) + if progress_callback: + progress_callback(current_size, total_size) + if label_callback: + label_callback("Downloading") + + if label_callback: + label_callback("Download Complete") + + +def extract_zip(zip_file, extract_path, label_callback=None): + with zipfile.ZipFile(zip_file, 'r') as zip_ref: + zip_ref.extractall(extract_path) + + if label_callback: + label_callback("Extracting") + + +def download_gtk_dlls(download_path, dll_url, dll_files, progress_callback=None, label_callback=None): + for dll_file in dll_files: + file_url = dll_url + dll_file + + if not os.path.exists(os.path.join(download_path, dll_file)): + download_file(file_url, os.path.join(download_path, dll_file), progress_callback, label_callback) + + # Check if the downloaded file is a zip file, extract it, and remove the zip file + if dll_file.endswith('.zip'): + extract_path = os.path.join(download_path, dll_file.replace('.zip', '')) + extract_zip(os.path.join(download_path, dll_file), extract_path, label_callback) + os.remove(os.path.join(download_path, dll_file)) + + +def set_path_variable(download_path): + + current = subprocess.run("echo %Path%", stdout=subprocess.PIPE, shell=True, text=True) + + output_string = str(current.stdout).strip() + ';' + download_path + + # Split the output_string into individual elements + elements = output_string.split(';') + + # Remove duplicates + unique_elements = list(set(elements)) + + # Join the unique elements back together + output_string_unique = ';'.join(unique_elements) + + # Refresh the system environment variables + subprocess.run("SETX Path " + output_string_unique, shell=True) + + print(f"Added PATH: {output_string_unique}") + + +def run_init(): + + download_path = get_temp_folder() + + dll_url = 'https://downloads.sourceforge.net/project/spitfire-browser/nightly/components/GTK4/' + + # Check if all required files are already present + dll_files = [ + 'libs.zip' + ] + + # Get the names of the folders corresponding to ".zip" files + zip_folders = [filename.replace('.zip', '') for filename in dll_files if filename.endswith('.zip')] + + # Check if all corresponding folders exist + all_folders_exist = all(os.path.exists(os.path.join(download_path, folder)) for folder in zip_folders) + + if not all_folders_exist: + root = tk.Tk() + root.title("Preparing for first start") + root.geometry("240x120") + + progress_var = tk.DoubleVar() + progress = ttk.Progressbar(root, variable=progress_var, length=200) + progress.grid(row=0, column=0, padx=20, pady=20) + + label_var = tk.StringVar() + label = tk.Label(root, textvariable=label_var) + label.grid(row=1, column=0, padx=20, pady=10) + + def update_progress_bar(current_size, total_size): + progress_value = (current_size / total_size) * 100 + progress_var.set(progress_value) + root.update_idletasks() + + def update_label(new_label): + label_var.set(new_label) + root.update_idletasks() + + def start_download(): + download_gtk_dlls(download_path, dll_url, dll_files, update_progress_bar, update_label) + set_path_variable(download_path) + + for zip_folder in zip_folders: + set_path_variable(os.path.join(download_path, zip_folder)) + + root.destroy() + + + # Run your main application function + + # Run your main application function + from gui_main import run_installer + run_installer(download_path) + + root.after(100, start_download) + root.mainloop() + else: + from gui_main import run_installer + run_installer(download_path) \ No newline at end of file