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/
|
||||||
/cmake-build-debug-visual-studio/
|
/cmake-build-debug-visual-studio/
|
||||||
/cmake-build-release-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_include_directories(${PROJECT_NAME} PRIVATE "out_embed")
|
||||||
target_link_libraries(Toolbox PRIVATE saucer::saucer sol2::sol2 lua tray nlohmann_json cppcodec)
|
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::thread m_threadMonitor;
|
||||||
std::atomic<bool> m_runningMonitor = true;
|
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 configureWindow();
|
||||||
void loadResources();
|
void loadResources();
|
||||||
void runLuaScript();
|
void runLuaScript();
|
||||||
@@ -60,4 +66,6 @@ private:
|
|||||||
void run_lua(int id);
|
void run_lua(int id);
|
||||||
|
|
||||||
void monitor_focus();
|
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 "tray.h"
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
|
|
||||||
ToolboxWindow* g_toolboxWindow = nullptr;
|
|
||||||
|
|
||||||
void on_toggle(struct tray_menu *item) {
|
ToolboxWindow* g_toolboxWindow = nullptr;
|
||||||
item->checked = !item->checked;
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_quit(struct tray_menu *item) {
|
void on_quit(struct tray_menu *item) {
|
||||||
tray_exit();
|
tray_exit();
|
||||||
@@ -20,8 +17,7 @@ void on_click(struct tray_menu *item) {
|
|||||||
g_toolboxWindow->show();
|
g_toolboxWindow->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int start(){
|
||||||
int main(int argc, char** argv) {
|
|
||||||
|
|
||||||
// Neue Anwendung erstellen
|
// Neue Anwendung erstellen
|
||||||
auto app = saucer::application::acquire({
|
auto app = saucer::application::acquire({
|
||||||
@@ -33,13 +29,12 @@ int main(int argc, char** argv) {
|
|||||||
toolboxWindow.initialize();
|
toolboxWindow.initialize();
|
||||||
|
|
||||||
g_toolboxWindow = &toolboxWindow;
|
g_toolboxWindow = &toolboxWindow;
|
||||||
toolboxWindow.show();
|
//toolboxWindow.show();
|
||||||
|
|
||||||
HICON hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1));
|
HICON hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1));
|
||||||
|
|
||||||
struct tray_menu tray_menu_items[] = {
|
struct tray_menu tray_menu_items[] = {
|
||||||
{"<EFBFBD>ffnen", 0, 0, NULL, on_click, 0},
|
{"<EFBFBD>ffnen", 0, 0, NULL, on_click, 0},
|
||||||
{"Option umschalten", 0, 0, NULL, on_toggle, 0},
|
|
||||||
{"-", 0, 0, NULL, nullptr, 0},
|
{"-", 0, 0, NULL, nullptr, 0},
|
||||||
{"Beenden", 0, 0, NULL, on_quit, 0},
|
{"Beenden", 0, 0, NULL, on_quit, 0},
|
||||||
{nullptr, 0, 0, NULL, nullptr, 0}
|
{nullptr, 0, 0, NULL, nullptr, 0}
|
||||||
@@ -65,3 +60,14 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
return 0;
|
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>
|
<body>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h1>Toolbox</h1>
|
<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>
|
||||||
<div class="main" id="toolbox">
|
<div class="main" id="toolbox">
|
||||||
<!-- Tool Cards werden hier dynamisch geladen -->
|
<!-- Tool Cards werden hier dynamisch geladen -->
|
||||||
@@ -162,7 +162,7 @@
|
|||||||
if (toolCard) {
|
if (toolCard) {
|
||||||
const toolId = toolCard.getAttribute('data-tool-id');
|
const toolId = toolCard.getAttribute('data-tool-id');
|
||||||
|
|
||||||
if (!button.disabled) {
|
if (sessionStorage.getItem(`toolButtonDisabled_${toolId}`) !== 'true') {
|
||||||
console.log(`Disabling button for tool ID: ${toolId}`);
|
console.log(`Disabling button for tool ID: ${toolId}`);
|
||||||
saucer.exposed.run_lua(parseInt(toolId, 10));
|
saucer.exposed.run_lua(parseInt(toolId, 10));
|
||||||
|
|
||||||
@@ -183,6 +183,10 @@
|
|||||||
saucer.exposed.debug_print();
|
saucer.exposed.debug_print();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openFileSystem() {
|
||||||
|
saucer.exposed.open_file_system();
|
||||||
|
}
|
||||||
|
|
||||||
function enableButtonByToolId(toolId) {
|
function enableButtonByToolId(toolId) {
|
||||||
saucer.exposed.debug_print();
|
saucer.exposed.debug_print();
|
||||||
// Finden Sie den Button mit dem entsprechenden data-tool-id
|
// Finden Sie den Button mit dem entsprechenden data-tool-id
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ void ToolboxWindow::initialize() {
|
|||||||
configureWindow();
|
configureWindow();
|
||||||
loadResources();
|
loadResources();
|
||||||
ExposeToJs();
|
ExposeToJs();
|
||||||
|
m_webview.set_always_on_top(true);
|
||||||
|
m_webview.set_decorations(false);
|
||||||
|
#ifdef _DEBUG
|
||||||
m_webview.set_dev_tools(true);
|
m_webview.set_dev_tools(true);
|
||||||
|
#endif
|
||||||
//m_webview.set_decorations(false);
|
//m_webview.set_decorations(false);
|
||||||
//m_webview.set_always_on_top(true);
|
//m_webview.set_always_on_top(true);
|
||||||
|
|
||||||
@@ -122,6 +126,10 @@ void ToolboxWindow::ExposeToJs() {
|
|||||||
m_webview.expose("debug_print", [&]() {
|
m_webview.expose("debug_print", [&]() {
|
||||||
debug_print();
|
debug_print();
|
||||||
}, saucer::launch::async);
|
}, saucer::launch::async);
|
||||||
|
|
||||||
|
m_webview.expose("open_file_system", [&]() {
|
||||||
|
open_file_system();
|
||||||
|
},saucer::launch::async);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ToolboxWindow::OptionWindow() {
|
void ToolboxWindow::OptionWindow() {
|
||||||
@@ -193,13 +201,6 @@ void encodeImageToBase64(const std::filesystem::path& imagePath, ToolboxWindow::
|
|||||||
|
|
||||||
void ToolboxWindow::fillToolboxMenu() {
|
void ToolboxWindow::fillToolboxMenu() {
|
||||||
// Bestimme das Benutzerverzeichnis abhängig vom Betriebssystem
|
// 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";
|
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