Add Cython setup and enhance installer functionality

Introduced a Cython-based setup to compile Python files for performance improvements. Updated installer scripts with customtkinter modifications for better UI, added support for additional content loading, and enhanced project structure. Adjusted build commands, streamlined file paths, and integrated new UI elements in the HTML resources.
This commit is contained in:
2024-12-22 20:24:23 +01:00
parent ed8b298f3a
commit f49d8874c1
10 changed files with 280 additions and 161 deletions

View File

@@ -82,4 +82,5 @@ private:
void openEinst();
void openIndex();
void getNewContent();
};

View File

@@ -7,8 +7,8 @@ def encode_file_to_base64(file_path):
# Base64-kodierte Strings generieren
exe_base64 = encode_file_to_base64("D:/Repos/toolbox/cmake-build-minsizerel-visual-studio/Toolbox.exe")
icon_base64 = encode_file_to_base64("D:/Repos/toolbox/cmake-build-MinSizeRel-visual-studio/toolbox.ico")
exe_base64 = encode_file_to_base64("../cmake-build-minsizerel-visual-studio/Toolbox.exe")
icon_base64 = encode_file_to_base64("../res/ico/Toolbox.ico")
# Base64-Strings in eine Python-Datei schreiben
with open("base64_strings.py", 'w') as py_file:

View File

@@ -1,4 +1,6 @@
saucer embed res out_embed
pyinstaller --onefile --icon=..\res\ico\toolbox.ico --name=toolbox_installer --uac-admin main.py
pyinstaller --onefile -key "secretkey" --icon=..\res\ico\toolbox.ico --name=toolbox_installer --add-data "../res/ico/toolbox.ico:." --noconsole --uac-admin main-loader.py
python setup.py build_ext --inplace
upx --force --best --lzma dist/toolbox_installer.exe

15
installer/main-loader.py Normal file
View File

@@ -0,0 +1,15 @@
import sys
import os
# Pfad zu deinem `cython`-Ordner
cython_dir = os.path.abspath("./cython")
# Ordner zum Python-Suchpfad hinzufügen
if cython_dir not in sys.path:
sys.path.insert(0, cython_dir)
# Importiere die `main.pyd`-Datei
import main
if __name__ == "__main__":
main.run() # Beispiel: Funktion `run()` aufrufen

View File

@@ -1,14 +1,30 @@
import base64
import sys
import customtkinter as ctk
from tkinter import filedialog, StringVar, DoubleVar, messagebox
import os
from tkinter import Tk, Button, Label, Entry, filedialog, messagebox, StringVar, DoubleVar, Frame, Text
from tkinter.ttk import Progressbar, Style
import base64
import base64_strings
# Base64-Strings für die Dateien
exe_base64 = base64_strings.exe_base64
icon_base64 = base64_strings.icon_base64
# Setup für Stil und Farben
def apply_custom_styles():
ctk.set_appearance_mode("dark") # Dark Mode
ctk.set_default_color_theme("blue")
global style_options
style_options = {
"bg_color": "#1e1e2e",
"text_color": "#ff79c6",
"button_hover_color": "#ff47b4",
"inactive_button_color": "#777",
"progress_color": "#ff79c6",
}
# Funktionen für die Installation
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)
@@ -20,168 +36,196 @@ def decode_base64_to_file(base64_str, output_path, progress_callback=None):
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)%')
title="Installationsordner auswählen"
)
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.")
messagebox.showerror("Fehler", "Bitte wählen Sie einen gültigen Installationsordner.")
return
exe_path = os.path.join(output_directory, "application.exe")
icon_path = os.path.join(output_directory, "toolbox.ico")
try:
# Ensure the output directory exists
# Sicherstellen, dass der Ordner existiert
os.makedirs(output_directory, exist_ok=True)
# Decode and save files
# Dateien decodieren und speichern
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, "Toolbox", icon_path)
messagebox.showinfo("Success", "Installation abgeschlossen!")
messagebox.showinfo("Erfolg", "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 'Toolbox' installiert haben!\n\nWeitere Informationen finden Sie auf unserer Website.")
readme_text.configure(state='disabled')
readme_text.pack(pady=10)
button = Button(readme_window, text="Beenden", command=root.quit)
button.place(relx=0.9, rely=0.9, anchor='se')
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()
messagebox.showerror("Fehler", f"Fehler beim Extrahieren der Dateien: {e}")
# Seiten-Setup
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)
for widget in root.winfo_children():
widget.destroy()
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)
welcome_label = ctk.CTkLabel(
root,
text="Willkommen bei der Installation!",
font=("Arial", 16),
text_color=style_options["text_color"],
)
welcome_label.pack(pady=20)
# Button unten rechts positionieren
button = Button(welcome_page, text="Weiter", command=lambda: (welcome_page.pack_forget(), next_page()))
button.place(relx=0.9, rely=0.9, anchor='se') # Position relativ, unten rechts ankern
description_label = ctk.CTkLabel(
root, text="Klicken Sie auf 'Weiter', um fortzufahren.", text_color="#bbb"
)
description_label.pack(pady=10)
next_button = ctk.CTkButton(
root,
text="Weiter",
fg_color=style_options["text_color"],
hover_color=style_options["button_hover_color"],
text_color="white",
command=setup_page_2,
)
next_button.place(relx=1.0, rely=1.0, anchor="se", x=-20, y=-20)
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)
for widget in root.winfo_children():
widget.destroy()
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 = Button(folder_page, text="Weiter", command=lambda: (folder_page.pack_forget(), next_page()))
button.place(relx=0.9, rely=0.9, anchor='se')
folder_label = ctk.CTkLabel(
root,
text="Installationsordner wählen:",
font=("Arial", 14),
text_color=style_options["text_color"],
)
folder_label.pack(pady=10)
folder_entry = ctk.CTkEntry(
root,
textvariable=output_dir_var,
placeholder_text="Pfad auswählen",
fg_color=style_options["bg_color"],
text_color="white",
)
folder_entry.pack(pady=10)
browse_button = ctk.CTkButton(
root,
text="Durchsuchen",
fg_color=style_options["text_color"],
hover_color=style_options["button_hover_color"],
text_color="white",
command=select_directory,
)
browse_button.pack(pady=10)
next_button = ctk.CTkButton(
root,
text="Weiter",
fg_color=style_options["progress_color"],
hover_color=style_options["button_hover_color"],
text_color="white",
command=setup_page_3,
)
next_button.place(relx=1.0, rely=1.0, anchor="se", x=-20, y=-20)
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)
for widget in root.winfo_children():
widget.destroy()
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 = Button(progress_page, text="Weiter", command=lambda: (progress_page.pack_forget(), show_readme()))
button.place(relx=0.9, rely=0.9, anchor='se')
progress_label = ctk.CTkLabel(
root,
text="Installation läuft...",
font=("Arial", 14),
text_color=style_options["text_color"],
)
progress_label.pack(pady=20)
progressbar = ctk.CTkProgressBar(
root,
variable=progress_var,
fg_color=style_options["progress_color"],
progress_color=style_options["text_color"],
)
progressbar.set(0) # Anfang des Fortschritts
progressbar.pack(pady=20)
finish_button = ctk.CTkButton(
root,
text="Installieren",
fg_color=style_options["text_color"],
hover_color=style_options["button_hover_color"],
text_color="white",
command=lambda: [extract_files(), setup_page_4()],
)
finish_button.place(relx=1.0, rely=1.0, anchor="se", x=-20, y=-20)
def setup_page_4():
for widget in root.winfo_children():
widget.destroy()
complete_label = ctk.CTkLabel(
root,
text="Installation abgeschlossen!",
font=("Arial", 16),
text_color=style_options["text_color"],
)
complete_label.pack(pady=20)
finish_button = ctk.CTkButton(
root,
text="Beenden",
fg_color=style_options["progress_color"],
hover_color=style_options["button_hover_color"],
text_color="white",
command=root.quit,
)
finish_button.place(relx=1.0, rely=1.0, anchor="se", x=-20, y=-20)
def resource_path(relative_path):
"""Ermittelt den absoluten Pfad, um Ressourcen im PyInstaller-Build zu finden."""
base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
return os.path.join(base_path, relative_path)
# Tkinter Setup
root = Tk()
root.title("Installer")
root.geometry("500x350")
def run():
# Hauptprogramm
# 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')
root.title("Toolbox Installer")
output_dir_var = StringVar(value=os.path.join(os.environ['PROGRAMFILES(X86)'], "Toolbox"))
progress_var = DoubleVar()
current_page = 0
# Icon setzen
icon_path = resource_path("toolbox.ico") # Stelle sicher, dass toolbox.ico vorhanden ist
root.iconbitmap(icon_path)
root.geometry("500x350")
setup_page_1()
root.mainloop()
# Globale Variablen
global output_dir_var, progress_var
output_dir_var = StringVar()
progress_var = DoubleVar()
# Stile anwenden und Startseite laden
apply_custom_styles()
setup_page_1()
# Haupt-Loop
root.mainloop()
root = ctk.CTk()
if __name__ == "__main__":
run() # Führt die Methode 'run()' aus, wenn die Datei direkt ausgeführt wird

View File

@@ -0,0 +1,3 @@
customtkinter~=5.2.2
setuptools~=75.6.0
Cython~=3.0.11

11
installer/setup.py Normal file
View File

@@ -0,0 +1,11 @@
from setuptools import setup
from Cython.Build import cythonize
import os
# Zielordner für kompilierten Code
build_dir = os.path.join(os.getcwd(), "cython")
setup(
ext_modules=cythonize("main.py", compiler_directives={'language_level': "3"}),
script_args=["build_ext", "--build-lib", build_dir]
)

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Toolbox</title>
<link rel="stylesheet" href="../highlight/styles/github-dark.css">
<link rel="stylesheet" href="../highlight/styles/dark.css" media="all" title="dark">
<style>
body {
font-family: Arial, sans-serif;
@@ -125,6 +125,16 @@
overflow-x: auto;
}
.general {
flex: 1;
background: #1e1e2e;
padding: 15px;
display: none; /* Standardmäßig ausgeblendet */
flex-direction: column;
align-items: center;
justify-content: center;
}
.footer {
background: #1b1b2b;
padding: 10px 20px;
@@ -140,7 +150,7 @@
<div class="sidebar">
<h2>Einstellungen</h2>
<ul>
<li><a href="#" onclick="showContent('none')">Einstellungen</a></li>
<li><a href="#" onclick="showContent('general')">Einstellungen</a></li>
<li><a href="#" onclick="showContent('editor')">Skript</a></li>
<li><a href="#" onclick="showContent('none')">Option 3</a></li>
<li><a href="#" onclick="showContent('none')">Option 4</a></li>
@@ -153,15 +163,18 @@
<!-- Header mit Zurück-Button -->
<div class="header">
<h1>Einstellungen</h1>
<button class="back-button" onclick="goBack()">Zurück</button>
<button class="back-button" onclick="goBack()"></button>
</div>
<!-- Zentraler Editor (Standardmäßig versteckt) -->
<div class="editor" id="editor">
<label for="code-editor"></label>
<textarea id="code-editor" placeholder="Write Lua code here..."></textarea>
<pre><code id="highlightedOutput" class="language-lua"></code></pre>
</div>
<div class="general" id="general"></div>
<!-- Footer -->
<div class="footer">
<!--Schmidti.Digital &copy; 2024-->
@@ -169,46 +182,39 @@
</div>
<script src="../highlight/highlight.min.js"></script>
<script src="../highlight/languages/lua.min.js"></script>
<script>hljs.highlightAll();</script>
<script>
function goBack() {
function goBack() {
saucer.exposed.openIndex();
}
function showContent(option) {
const general = document.getElementById('general');
const editor = document.getElementById('editor');
// Editor anzeigen, wenn "Option 2" ausgewählt wird
if (option === 'editor') {
editor.style.display = 'flex';
general.style.display = 'none';
} else if (option === 'general') {
general.style.display = 'flex';
editor.style.display = 'none';
} else {
editor.style.display = 'none';
general.style.display = 'none';
}
}
document.addEventListener('DOMContentLoaded', (event) => {
const editor = document.getElementById('code-editor');
const output = document.getElementById('highlightedOutput');
document.addEventListener('DOMContentLoaded', () => {
const editor = document.getElementById('code-editor');
const output = document.getElementById('highlightedOutput');
editor.addEventListener('input', () => {
// Update the code block with the content of the textarea
output.textContent = editor.value;
// Apply syntax highlighting
hljs.highlightElement(output);
});
// Synchronisiere den Inhalt von textarea mit dem Codeblock
editor.addEventListener('input', () => {
output.innerHTML = ''; // Vorheriges Highlighting entfernen
output.textContent = editor.value; // Aktuellen Text setzen
hljs.highlightAll();
});
});
// Optional: Adjust the height of the textarea to match the content
editor.addEventListener('input', () => {
// Update the code block with the content of the textarea
output.textContent = editor.value;
// Apply syntax highlighting
hljs.highlightElement(output);
});
function adjustTextareaHeight() {
editor.style.height = 'auto';
editor.style.height = editor.scrollHeight + 'px';
}
});
</script>
</body>

View File

@@ -23,21 +23,41 @@
position: relative; /* Für absolute Positionierung des Buttons */
}
.options-button {
.button {
position: absolute;
top: 50%;
right: 20px;
transform: translateY(-50%);
background: #2b2c3b;
border: none;
color: #ff79c6;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
transition: background 0.3s;
}
.options-button:hover {
#file-system-button {
position: absolute;
top: 50%;
right: 20px;
padding: 8px 16px;
border-radius: 4px;
}
#reload-button{
position: absolute;
top: 50%;
left: 20px;
padding: 8px 16px;
border-radius: 4px;
}
#option-button {
position: absolute;
top: 50%;
right: 80px;
padding: 8px 16px;
border-radius: 4px;
}
.button:hover {
background: #32334a;
}
.header h1 {
@@ -146,15 +166,22 @@
</head>
<body>
<div class="header">
<button onclick="getNewContent()" class="button" id="reload-button"></button>
<button onclick="openEinst()" class="button" id="option-button">⚙️</button>
<h1>Toolbox</h1>
<button onclick="openFileSystem()" id="options-button" class="options-button">Ordner</button>
<button onclick="openFileSystem()" id="file-system-button" class="button">📁</button>
</div>
<div class="main" id="toolbox">
<!-- Tool Cards werden hier dynamisch geladen -->
</div>
<div class="footer">
<!--Schmidti.Digital &copy; 2024-->
</div><script>
</div>
<script>
function getNewContent(){
saucer.exposed.getNewContent();
}
function runLuaWithId(event) {
if (event.target.tagName === 'BUTTON') {
// Falls der Button gedrückt wurde, brich die Funktion ab

View File

@@ -135,6 +135,11 @@ void ToolboxWindow::ExposeToJs() {
m_webview.expose("openIndex", [&]() {
openIndex();
},saucer::launch::async);
m_webview.expose("getNewContent", [&]()
{
getNewContent();
},saucer::launch::async);
}
void ToolboxWindow::SetJsonItems() {// Konvertierung des Vektors in JSON
@@ -367,6 +372,11 @@ void ToolboxWindow::openIndex() {
if (!serveHtmlFile("html/index.html", false)) {
return;
}
getNewContent();
}
void ToolboxWindow::getNewContent()
{
configureWindow();
fillToolboxMenu();
SetJsonItems();