Add installer GUI and file system access feature
This commit introduces a new Tkinter-based GUI installer script (`installer/main.py`) that manages file extraction and shortcut creation. Additionally, a new file system access feature was added to the `ToolboxWindow` class, allowing users to open directories with platform-specific commands. Improvements also include platform-specific adjustments for Windows executables in the CMake configuration.
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,3 +2,5 @@
|
||||
/cmake-build-debug/
|
||||
/cmake-build-debug-visual-studio/
|
||||
/cmake-build-release-visual-studio/
|
||||
/cmake-build-minsizerel-visual-studio/
|
||||
/installer/.idea/
|
||||
|
||||
@@ -45,3 +45,7 @@ add_executable(Toolbox main.cpp ${SOURCES} ${RESOURCE_FILES} ${PLATFORM_SOURCES}
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE "out_embed")
|
||||
target_link_libraries(Toolbox PRIVATE saucer::saucer sol2::sol2 lua tray nlohmann_json cppcodec)
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(Toolbox PROPERTIES WIN32_EXECUTABLE TRUE)
|
||||
endif()
|
||||
@@ -45,6 +45,12 @@ private:
|
||||
std::thread m_threadMonitor;
|
||||
std::atomic<bool> m_runningMonitor = true;
|
||||
|
||||
#ifdef _WIN32
|
||||
std::filesystem::path userPath = std::getenv("USERPROFILE"); // Windows
|
||||
#else
|
||||
std::filesystem::path userPath = std::getenv("HOME"); // Unix/Linux/MacOS
|
||||
#endif
|
||||
|
||||
void configureWindow();
|
||||
void loadResources();
|
||||
void runLuaScript();
|
||||
@@ -60,4 +66,6 @@ private:
|
||||
void run_lua(int id);
|
||||
|
||||
void monitor_focus();
|
||||
|
||||
void open_file_system();
|
||||
};
|
||||
|
||||
20
installer/coder.py
Normal file
20
installer/coder.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import base64
|
||||
|
||||
|
||||
def encode_file_to_base64(file_path):
|
||||
with open(file_path, 'rb') as file:
|
||||
return base64.b64encode(file.read()).decode('utf-8')
|
||||
|
||||
|
||||
# Base64-kodierte Strings generieren
|
||||
exe_base64 = encode_file_to_base64("D:/Repos/toolbox/cmake-build-release-visual-studio/Toolbox.exe")
|
||||
icon_base64 = encode_file_to_base64("D:/Repos/toolbox/cmake-build-release-visual-studio/toolbox.ico")
|
||||
|
||||
# Base64-Strings in Textdateien schreiben
|
||||
with open("Toolbox_exe_base64.txt", 'w') as exe_file:
|
||||
exe_file.write(exe_base64)
|
||||
|
||||
with open("Toolbox_icon_base64.txt", 'w') as icon_file:
|
||||
icon_file.write(icon_base64)
|
||||
|
||||
print("Base64-Daten erfolgreich in Textdateien gespeichert.")
|
||||
177
installer/main.py
Normal file
177
installer/main.py
Normal file
@@ -0,0 +1,177 @@
|
||||
import base64
|
||||
import os
|
||||
from tkinter import Tk, Button, Label, Entry, filedialog, messagebox, StringVar, DoubleVar, Frame, Text
|
||||
from tkinter.ttk import Progressbar, Style
|
||||
|
||||
# Base64-Strings für die Dateien (als Platzhalter)
|
||||
exe_base64 = "..." # Base64-String der .exe
|
||||
icon_base64 = "..." # Base64-String der .ico
|
||||
|
||||
|
||||
def decode_base64_to_file(base64_str, output_path, progress_callback=None):
|
||||
"""Decodes a Base64 string and writes it to a file."""
|
||||
data = base64.b64decode(base64_str)
|
||||
total_bytes = len(data)
|
||||
chunk_size = 1024
|
||||
with open(output_path, 'wb') as file:
|
||||
for i in range(0, total_bytes, chunk_size):
|
||||
file.write(data[i:i + chunk_size])
|
||||
if progress_callback:
|
||||
progress_callback(min((i + chunk_size) / total_bytes * 100, 100))
|
||||
|
||||
|
||||
def update_progress(value):
|
||||
"""Updates the progress bar."""
|
||||
progress_var.set(value)
|
||||
root.update_idletasks()
|
||||
|
||||
|
||||
def select_directory():
|
||||
"""Opens a directory selection dialog."""
|
||||
directory = filedialog.askdirectory(
|
||||
title="Select Output Directory",
|
||||
initialdir=output_dir_var.get() or os.path.expandvars('%PROGRAMFILES(X86)%')
|
||||
)
|
||||
if directory:
|
||||
os.makedirs(directory, exist_ok=True) # Create the folder if it doesn't exist
|
||||
output_dir_var.set(directory)
|
||||
|
||||
|
||||
def create_shortcut(target, shortcut_name, icon_path):
|
||||
"""Creates shortcuts on Desktop and Startup folder."""
|
||||
desktop = os.path.join(os.environ['USERPROFILE'], 'Desktop')
|
||||
path = os.path.join(desktop, shortcut_name + ".lnk")
|
||||
|
||||
startup = os.path.join(os.getenv('APPDATA'), r'Microsoft\Windows\Start Menu\Programs\Startup')
|
||||
startup_shortcut = os.path.join(startup, shortcut_name + ".lnk")
|
||||
|
||||
vbs_script = f'''
|
||||
Set oWS = WScript.CreateObject("WScript.Shell")
|
||||
sLinkFile = "{path}"
|
||||
Set oLink = oWS.CreateShortcut(sLinkFile)
|
||||
oLink.TargetPath = "{target}"
|
||||
oLink.IconLocation = "{icon_path}"
|
||||
oLink.Save
|
||||
'''
|
||||
startup_vbs_script = f'''
|
||||
Set oWS = WScript.CreateObject("WScript.Shell")
|
||||
sLinkFile = "{startup_shortcut}"
|
||||
Set oLink = oWS.CreateShortcut(sLinkFile)
|
||||
oLink.TargetPath = "{target}"
|
||||
oLink.IconLocation = "{icon_path}"
|
||||
oLink.Save
|
||||
'''
|
||||
|
||||
for script, output in [(vbs_script, "create_shortcut.vbs"), (startup_vbs_script, "create_startup_shortcut.vbs")]:
|
||||
with open(output, "w") as file:
|
||||
file.write(script)
|
||||
os.system(f"wscript {output}")
|
||||
os.remove(output)
|
||||
|
||||
|
||||
def extract_files():
|
||||
"""Extracts the files to the selected directory and creates shortcuts."""
|
||||
output_directory = output_dir_var.get()
|
||||
if not output_directory:
|
||||
messagebox.showerror("Error", "Please select a valid output directory.")
|
||||
return
|
||||
|
||||
exe_path = os.path.join(output_directory, "application.exe")
|
||||
icon_path = os.path.join(output_directory, "icon.ico")
|
||||
|
||||
try:
|
||||
# Ensure the output directory exists
|
||||
os.makedirs(output_directory, exist_ok=True)
|
||||
|
||||
# Decode and save files
|
||||
decode_base64_to_file(exe_base64, exe_path, update_progress)
|
||||
decode_base64_to_file(icon_base64, icon_path, update_progress)
|
||||
|
||||
# Create shortcuts
|
||||
create_shortcut(exe_path, "DeinProgrammName", icon_path)
|
||||
|
||||
messagebox.showinfo("Success", "Installation abgeschlossen!")
|
||||
except Exception as e:
|
||||
messagebox.showerror("Error", f"Failed to extract files: {e}")
|
||||
|
||||
|
||||
def show_readme():
|
||||
"""Displays the final README page."""
|
||||
readme_window = Frame(root, bg='#2e2e2e')
|
||||
readme_window.pack(fill='both', expand=True)
|
||||
Label(readme_window, text="Installation abgeschlossen!", bg="#2e2e2e", fg="white", font=("Helvetica", 16)).pack(pady=20)
|
||||
readme_text = Text(readme_window, bg="#444", fg="white", wrap='word', height=10, width=50)
|
||||
readme_text.insert('1.0', "Vielen Dank, dass Sie 'DeinProgrammName' installiert haben!\n\nWeitere Informationen finden Sie auf unserer Website.")
|
||||
readme_text.configure(state='disabled')
|
||||
readme_text.pack(pady=10)
|
||||
Button(readme_window, text="Beenden", command=root.quit).pack(pady=10)
|
||||
|
||||
|
||||
def next_page():
|
||||
"""Handles the navigation between pages."""
|
||||
global current_page
|
||||
if current_page == 1:
|
||||
setup_page_2()
|
||||
elif current_page == 2:
|
||||
extract_files()
|
||||
setup_page_3()
|
||||
|
||||
|
||||
def setup_page_1():
|
||||
"""Sets up the welcome page."""
|
||||
global current_page
|
||||
current_page = 1
|
||||
welcome_page = Frame(root, bg='#2e2e2e')
|
||||
welcome_page.pack(fill='both', expand=True)
|
||||
|
||||
Label(welcome_page, text="Willkommen beim Installer!", bg="#2e2e2e", fg="white", font=("Helvetica", 16)).pack(pady=20)
|
||||
Label(welcome_page, text="Klicken Sie auf 'Weiter', um fortzufahren.", bg="#2e2e2e", fg="white").pack(pady=10)
|
||||
Button(welcome_page, text="Weiter", command=lambda: (welcome_page.pack_forget(), next_page())).pack()
|
||||
|
||||
|
||||
def setup_page_2():
|
||||
"""Sets up the installation folder selection page."""
|
||||
global current_page
|
||||
current_page = 2
|
||||
folder_page = Frame(root, bg='#2e2e2e')
|
||||
folder_page.pack(fill='both', expand=True)
|
||||
|
||||
Label(folder_page, text="Installationsordner wählen:", bg="#2e2e2e", fg="white").pack(pady=5)
|
||||
Entry(folder_page, textvariable=output_dir_var, width=50).pack(pady=5)
|
||||
Button(folder_page, text="Durchsuchen...", command=select_directory).pack(pady=5)
|
||||
Button(folder_page, text="Weiter", command=lambda: (folder_page.pack_forget(), next_page())).pack()
|
||||
|
||||
|
||||
def setup_page_3():
|
||||
"""Sets up the installation progress page."""
|
||||
global current_page
|
||||
current_page = 3
|
||||
progress_page = Frame(root, bg='#2e2e2e')
|
||||
progress_page.pack(fill='both', expand=True)
|
||||
|
||||
Label(progress_page, text="Installation läuft...", bg="#2e2e2e", fg="white").pack(pady=20)
|
||||
progress_bar = Progressbar(progress_page, orient='horizontal', length=400, variable=progress_var,
|
||||
style='Horizontal.TProgressbar')
|
||||
progress_bar.pack(pady=10)
|
||||
Button(progress_page, text="Weiter", command=lambda: (progress_page.pack_forget(), show_readme())).pack()
|
||||
|
||||
|
||||
# Tkinter Setup
|
||||
root = Tk()
|
||||
root.title("Installer")
|
||||
root.geometry("500x350")
|
||||
|
||||
# Darkmode Style
|
||||
style = Style(root)
|
||||
style.theme_use('clam')
|
||||
style.configure('TLabel', background='#2e2e2e', foreground='white', font=('Helvetica', 12))
|
||||
style.configure('TButton', background='#444', foreground='white', font=('Helvetica', 10))
|
||||
style.configure('Horizontal.TProgressbar', troughcolor='#444', background='#5a9')
|
||||
root.configure(background='#2e2e2e')
|
||||
|
||||
output_dir_var = StringVar(value=os.path.join(os.environ['PROGRAMFILES(X86)'], "DeinProgrammName"))
|
||||
progress_var = DoubleVar()
|
||||
current_page = 0
|
||||
|
||||
setup_page_1()
|
||||
root.mainloop()
|
||||
0
jenkinsfile
Normal file
0
jenkinsfile
Normal file
22
main.cpp
22
main.cpp
@@ -3,11 +3,8 @@
|
||||
#include "tray.h"
|
||||
#include "resource.h"
|
||||
|
||||
ToolboxWindow* g_toolboxWindow = nullptr;
|
||||
|
||||
void on_toggle(struct tray_menu *item) {
|
||||
item->checked = !item->checked;
|
||||
}
|
||||
ToolboxWindow* g_toolboxWindow = nullptr;
|
||||
|
||||
void on_quit(struct tray_menu *item) {
|
||||
tray_exit();
|
||||
@@ -20,8 +17,7 @@ void on_click(struct tray_menu *item) {
|
||||
g_toolboxWindow->show();
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int start(){
|
||||
|
||||
// Neue Anwendung erstellen
|
||||
auto app = saucer::application::acquire({
|
||||
@@ -33,13 +29,12 @@ int main(int argc, char** argv) {
|
||||
toolboxWindow.initialize();
|
||||
|
||||
g_toolboxWindow = &toolboxWindow;
|
||||
toolboxWindow.show();
|
||||
//toolboxWindow.show();
|
||||
|
||||
HICON hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1));
|
||||
|
||||
struct tray_menu tray_menu_items[] = {
|
||||
{"<EFBFBD>ffnen", 0, 0, NULL, on_click, 0},
|
||||
{"Option umschalten", 0, 0, NULL, on_toggle, 0},
|
||||
{"-", 0, 0, NULL, nullptr, 0},
|
||||
{"Beenden", 0, 0, NULL, on_quit, 0},
|
||||
{nullptr, 0, 0, NULL, nullptr, 0}
|
||||
@@ -65,3 +60,14 @@ int main(int argc, char** argv) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
int main(int argc, char** argv) {
|
||||
return start(); // Konsole bleibt aktiv im Debug-Modus
|
||||
}
|
||||
#else
|
||||
#include <Windows.h>
|
||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
|
||||
return start(); // Keine Konsole im Release-Modus
|
||||
}
|
||||
#endif
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -147,7 +147,7 @@
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Toolbox</h1>
|
||||
<button id="options-button" class="options-button">Optionen</button>
|
||||
<button onclick="openFileSystem()" id="options-button" class="options-button">Ordner</button>
|
||||
</div>
|
||||
<div class="main" id="toolbox">
|
||||
<!-- Tool Cards werden hier dynamisch geladen -->
|
||||
@@ -162,7 +162,7 @@
|
||||
if (toolCard) {
|
||||
const toolId = toolCard.getAttribute('data-tool-id');
|
||||
|
||||
if (!button.disabled) {
|
||||
if (sessionStorage.getItem(`toolButtonDisabled_${toolId}`) !== 'true') {
|
||||
console.log(`Disabling button for tool ID: ${toolId}`);
|
||||
saucer.exposed.run_lua(parseInt(toolId, 10));
|
||||
|
||||
@@ -183,6 +183,10 @@
|
||||
saucer.exposed.debug_print();
|
||||
}
|
||||
|
||||
function openFileSystem() {
|
||||
saucer.exposed.open_file_system();
|
||||
}
|
||||
|
||||
function enableButtonByToolId(toolId) {
|
||||
saucer.exposed.debug_print();
|
||||
// Finden Sie den Button mit dem entsprechenden data-tool-id
|
||||
|
||||
@@ -14,7 +14,11 @@ void ToolboxWindow::initialize() {
|
||||
configureWindow();
|
||||
loadResources();
|
||||
ExposeToJs();
|
||||
m_webview.set_always_on_top(true);
|
||||
m_webview.set_decorations(false);
|
||||
#ifdef _DEBUG
|
||||
m_webview.set_dev_tools(true);
|
||||
#endif
|
||||
//m_webview.set_decorations(false);
|
||||
//m_webview.set_always_on_top(true);
|
||||
|
||||
@@ -122,6 +126,10 @@ void ToolboxWindow::ExposeToJs() {
|
||||
m_webview.expose("debug_print", [&]() {
|
||||
debug_print();
|
||||
}, saucer::launch::async);
|
||||
|
||||
m_webview.expose("open_file_system", [&]() {
|
||||
open_file_system();
|
||||
},saucer::launch::async);
|
||||
}
|
||||
|
||||
void ToolboxWindow::OptionWindow() {
|
||||
@@ -193,13 +201,6 @@ void encodeImageToBase64(const std::filesystem::path& imagePath, ToolboxWindow::
|
||||
|
||||
void ToolboxWindow::fillToolboxMenu() {
|
||||
// Bestimme das Benutzerverzeichnis abhängig vom Betriebssystem
|
||||
std::filesystem::path userPath;
|
||||
|
||||
#ifdef _WIN32
|
||||
userPath = std::getenv("USERPROFILE"); // Windows
|
||||
#else
|
||||
userPath = std::getenv("HOME"); // Unix/Linux/MacOS
|
||||
#endif
|
||||
|
||||
std::filesystem::path toolboxDir = userPath / ".Toolbox";
|
||||
|
||||
@@ -279,3 +280,31 @@ void ToolboxWindow::monitor_focus() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ToolboxWindow::open_file_system() {
|
||||
std::filesystem::path toolboxDir = userPath / ".Toolbox";
|
||||
|
||||
if (std::filesystem::exists(toolboxDir) && std::filesystem::is_directory(toolboxDir)) {
|
||||
std::string path = toolboxDir.string();
|
||||
std::string command;
|
||||
|
||||
#ifdef _WIN32
|
||||
command = "explorer \"" + path + "\"";
|
||||
#elif __APPLE__
|
||||
command = "open \"" + path + "\"";
|
||||
#elif __linux__
|
||||
command = "xdg-open \"" + path + "\"";
|
||||
#else
|
||||
std::cerr << "Fehler: Plattform nicht unterstützt." << std::endl;
|
||||
return;
|
||||
#endif
|
||||
|
||||
int result = std::system(command.c_str());
|
||||
if (result != 0) {
|
||||
std::cerr << "Fehler beim Öffnen des Verzeichnisses: " << result << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Fehler: Das Verzeichnis .Toolbox existiert nicht." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user