more beauty

This commit is contained in:
2026-01-10 13:31:11 +01:00
parent 1855810c14
commit 8739455231
2 changed files with 130 additions and 33 deletions

72
main.py
View File

@@ -1,14 +1,15 @@
# main.py # main.py
import sys import sys
import os import os
import time
from PyQt6.QtWidgets import QApplication
from PyQt6.QtGui import QPixmap, QFont, QIcon
from PyQt6.QtCore import qInstallMessageHandler, QTimer, Qt
# Config zuerst!
from config import qt_message_handler, LOG_FILE from config import qt_message_handler, LOG_FILE
from PyQt6.QtWidgets import QApplication, QSplashScreen from ui import UffWindow, ModernSplashScreen, ModelLoaderThread
from PyQt6.QtGui import QPixmap, QFont
from PyQt6.QtCore import qInstallMessageHandler
from ui import UffWindow
qInstallMessageHandler(qt_message_handler) qInstallMessageHandler(qt_message_handler)
os.environ["QT_LOGGING_RULES"] = "qt.text.font.db=false;qt.qpa.fonts=false" os.environ["QT_LOGGING_RULES"] = "qt.text.font.db=false;qt.qpa.fonts=false"
@@ -18,18 +19,61 @@ if __name__ == "__main__":
app = QApplication(sys.argv) app = QApplication(sys.argv)
app.setFont(QFont("Segoe UI", 10)) app.setFont(QFont("Segoe UI", 10))
splash = None # 1. ICON SETZEN (Für die ganze App)
if os.path.exists("assets/uff_banner.jpeg"): # Wenn assets/icon.png existiert, wird es genutzt.
try: if os.path.exists("assets/icon.png"):
splash = QSplashScreen(QPixmap("assets/uff_banner.jpeg")) app_icon = QIcon("assets/icon.png")
splash.show() app.setWindowIcon(app_icon)
except: pass
window = UffWindow(splash) # 2. SPLASH SCREEN ERSTELLEN
window.show() splash_pix = QPixmap("assets/uff_banner.jpeg")
window.start_model_loading() # Falls kein Bild da ist, nehmen wir ein leeres (damit es nicht crasht)
if splash_pix.isNull():
splash_pix = QPixmap(600, 400)
splash_pix.fill(Qt.GlobalColor.white)
splash = ModernSplashScreen(splash_pix)
splash.show()
# 3. LADEN SIMULIEREN & STARTEN
# Wir nutzen einen kleinen Trick, um den Start visuell zu "begleiten"
splash.set_progress(10, "Lade Konfiguration...")
app.processEvents()
time.sleep(0.3) # Nur für den Effekt
splash.set_progress(30, "Verbinde Datenbank...")
app.processEvents()
# Hauptfenster erstellen (aber noch versteckt lassen)
window = UffWindow()
splash.set_progress(50, "Lade Benutzeroberfläche...")
app.processEvents()
time.sleep(0.2)
# 4. DAS SCHWERE KI-MODELL LADEN
splash.set_progress(60, "Lade KI-Modell (das dauert kurz)...")
app.processEvents()
# Wir starten den Thread, aber wir müssen warten bis er fertig ist,
# bevor wir den Splash schließen.
loader = ModelLoaderThread()
def on_loaded(model):
splash.set_progress(100, "Fertig!")
app.processEvents()
time.sleep(0.5) # Kurz warten bei 100%
window.on_model_loaded(model) # Modell an Fenster übergeben
window.show() # Fenster zeigen
splash.finish(window) # Splash schließen
loader.model_loaded.connect(on_loaded)
loader.start()
sys.exit(app.exec()) sys.exit(app.exec())
except Exception as e: except Exception as e:
import traceback import traceback
print("CRITICAL MAIN CRASH:") print("CRITICAL MAIN CRASH:")

89
ui.py
View File

@@ -1,26 +1,76 @@
# ui.py # ui.py
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, import os
from PyQt6.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLineEdit, QPushButton, QLabel, QFileDialog, QLineEdit, QPushButton, QLabel, QFileDialog,
QProgressBar, QMessageBox, QListWidget, QListWidgetItem, QProgressBar, QMessageBox, QListWidget, QListWidgetItem,
QSplitter, QFrame, QScrollArea, QStyle, QGraphicsDropShadowEffect) QSplitter, QFrame, QScrollArea, QStyle, QGraphicsDropShadowEffect,
from PyQt6.QtCore import Qt, QUrl, QThread, pyqtSignal QSplashScreen) # QSplashScreen hier wichtig
from PyQt6.QtGui import QDesktopServices, QColor, QFont from PyQt6.QtCore import Qt, QUrl, QThread, pyqtSignal, QRect
from PyQt6.QtGui import QDesktopServices, QColor, QFont, QPainter, QIcon, QPixmap # Painter & Icon neu
from sentence_transformers import SentenceTransformer from sentence_transformers import SentenceTransformer
from database import DatabaseHandler from database import DatabaseHandler
from indexer import IndexerThread from indexer import IndexerThread
from config import STYLESHEET from config import STYLESHEET
# Thread zum Laden des Modells (UI-Helper) # --- NEU: Ein moderner Splash Screen mit Ladebalken ---
class ModernSplashScreen(QSplashScreen):
def __init__(self, pixmap):
super().__init__(pixmap)
self.progress = 0
self.message = "Initialisiere..."
# Schriftart für den Ladetext
self.font = QFont("Segoe UI", 10, QFont.Weight.Bold)
def set_progress(self, value, text):
self.progress = value
self.message = text
self.repaint() # Erzwingt neuzeichnen
def drawContents(self, painter):
# 1. Das normale Bild zeichnen
super().drawContents(painter)
# 2. Ladebalken-Hintergrund (unten)
# Wir malen direkt auf das Bild
bg_rect = self.rect()
bar_height = 20
# Position: Ganz unten am Bild
bar_rect = QRect(0, bg_rect.height() - bar_height, bg_rect.width(), bar_height)
# Hintergrund des Balkens (dunkelgrau)
painter.setPen(Qt.PenStyle.NoPen)
painter.setBrush(QColor(50, 50, 50))
painter.drawRect(bar_rect)
# 3. Der Fortschritt (türkis/blau)
# Breite basierend auf % berechnen
progress_width = int(bg_rect.width() * (self.progress / 100))
prog_rect = QRect(0, bg_rect.height() - bar_height, progress_width, bar_height)
painter.setBrush(QColor("#3498db")) # UFF-Blau
painter.drawRect(prog_rect)
# 4. Text zeichnen (zentriert über dem Balken oder darin)
painter.setPen(QColor("white"))
painter.setFont(self.font)
# Text etwas oberhalb des Balkens zeichnen
text_rect = QRect(0, bg_rect.height() - bar_height - 30, bg_rect.width(), 25)
painter.drawText(text_rect, Qt.AlignmentFlag.AlignCenter, self.message)
# --- Thread zum Laden des Modells ---
class ModelLoaderThread(QThread): class ModelLoaderThread(QThread):
model_loaded = pyqtSignal(object) model_loaded = pyqtSignal(object)
def run(self): def run(self):
try: try:
# Das ist der schwere Teil, der dauert
model = SentenceTransformer('all-MiniLM-L6-v2') model = SentenceTransformer('all-MiniLM-L6-v2')
self.model_loaded.emit(model) self.model_loaded.emit(model)
except: self.model_loaded.emit(None) except:
self.model_loaded.emit(None)
# Widget für einzelne Ergebnisse # --- SearchResultItem (Unverändert, aber der Vollständigkeit halber hier) ---
class SearchResultItem(QFrame): class SearchResultItem(QFrame):
def __init__(self, filename, filepath, snippet, parent=None): def __init__(self, filename, filepath, snippet, parent=None):
super().__init__(parent) super().__init__(parent)
@@ -76,16 +126,21 @@ class SearchResultItem(QFrame):
target = self.filepath.split(" :: ")[0] if " :: " in self.filepath else self.filepath target = self.filepath.split(" :: ")[0] if " :: " in self.filepath else self.filepath
QDesktopServices.openUrl(QUrl.fromLocalFile(target)) QDesktopServices.openUrl(QUrl.fromLocalFile(target))
# --- Das Hauptfenster ---
class UffWindow(QMainWindow): class UffWindow(QMainWindow):
def __init__(self, splash=None): def __init__(self):
super().__init__() super().__init__()
self.splash = splash
self.db = DatabaseHandler() self.db = DatabaseHandler()
self.initUI() self.initUI()
# NEU: Icon setzen
if os.path.exists("assets/uff_icon.jpeg"):
self.setWindowIcon(QIcon("assets/uff_icon.jpeg"))
self.load_saved_folders() self.load_saved_folders()
def initUI(self): def initUI(self):
self.setWindowTitle("UFF Search") self.setWindowTitle("UFF Search v1.0")
self.resize(1100, 750) self.resize(1100, 750)
self.setStyleSheet(STYLESHEET) self.setStyleSheet(STYLESHEET)
@@ -158,7 +213,7 @@ class UffWindow(QMainWindow):
search_box.addWidget(self.btn_go) search_box.addWidget(self.btn_go)
status_box = QHBoxLayout() status_box = QHBoxLayout()
self.lbl_status = QLabel("Modell wird geladen...") self.lbl_status = QLabel("Bereit.")
self.lbl_status.setObjectName("StatusLabel") self.lbl_status.setObjectName("StatusLabel")
self.prog = QProgressBar() self.prog = QProgressBar()
self.prog.hide() self.prog.hide()
@@ -187,14 +242,8 @@ class UffWindow(QMainWindow):
self.btn_go.setEnabled(enabled) self.btn_go.setEnabled(enabled)
self.folder_list.setEnabled(enabled) self.folder_list.setEnabled(enabled)
def start_model_loading(self): # Methoden für Model Loading (wird jetzt von main gesteuert)
if self.splash: self.splash.showMessage("Lade KI-Modell...", Qt.AlignmentFlag.AlignBottom, Qt.GlobalColor.white)
self.loader = ModelLoaderThread()
self.loader.model_loaded.connect(self.on_model_loaded)
self.loader.start()
def on_model_loaded(self, model): def on_model_loaded(self, model):
if self.splash: self.splash.finish(self)
if not model: if not model:
QMessageBox.critical(self, "Fehler", "Modell konnte nicht geladen werden.") QMessageBox.critical(self, "Fehler", "Modell konnte nicht geladen werden.")
return return
@@ -202,6 +251,10 @@ class UffWindow(QMainWindow):
self.lbl_status.setText("Bereit für deine Suche.") self.lbl_status.setText("Bereit für deine Suche.")
self.set_ui_enabled(True) self.set_ui_enabled(True)
# ... RESTLICHE METHODEN (search, add_folder etc.) bleiben gleich wie vorher ...
# (Kopiere hier einfach die Methoden aus deiner alten ui.py rein,
# search, load_saved_folders, add_new_folder, delete_selected_folder, rescan, start_idx, cancel_idx, idx_done)
def search(self): def search(self):
query = self.input.text() query = self.input.text()
if not query: return if not query: return