make it beauty and shiny

This commit is contained in:
2026-01-10 12:58:18 +01:00
parent f8986da1aa
commit afd3ae1cc4
2 changed files with 272 additions and 107 deletions

View File

@@ -7,3 +7,5 @@ transformers==4.28.1
torch==1.13.1 torch==1.13.1
numpy==1.24.2 numpy==1.24.2
python-docx python-docx
openpyxl
python-pptx

View File

@@ -7,11 +7,21 @@ import zipfile
import io import io
import traceback import traceback
# NEU: Word Support # --- OPTIONALE IMPORTE ---
try: try:
import docx import docx
except ImportError: except ImportError:
docx = None # Fallback, falls nicht installiert docx = None
try:
import openpyxl
except ImportError:
openpyxl = None
try:
from pptx import Presentation
except ImportError:
Presentation = None
from sentence_transformers import SentenceTransformer, util from sentence_transformers import SentenceTransformer, util
from rapidfuzz import process, fuzz from rapidfuzz import process, fuzz
@@ -21,8 +31,8 @@ from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
QHBoxLayout, QLineEdit, QPushButton, QLabel, QHBoxLayout, QLineEdit, QPushButton, QLabel,
QFileDialog, QProgressBar, QMessageBox, QFileDialog, QProgressBar, QMessageBox,
QListWidget, QListWidgetItem, QSplitter, QFrame, QListWidget, QListWidgetItem, QSplitter, QFrame,
QSplashScreen, QScrollArea, QStyle) QSplashScreen, QScrollArea, QStyle, QGraphicsDropShadowEffect)
from PyQt6.QtGui import QDesktopServices, QPixmap, QCursor, QAction from PyQt6.QtGui import QDesktopServices, QPixmap, QCursor, QAction, QColor, QPalette, QFont
# --- 0. LOGGING & SETUP --- # --- 0. LOGGING & SETUP ---
@@ -51,12 +61,117 @@ class Logger(object):
sys.stdout = Logger() sys.stdout = Logger()
sys.stderr = sys.stdout sys.stderr = sys.stdout
print(f"--- START LOGGING ---") # --- STYLESHEET ---
print(f"Logfile: {log_file_path}") STYLESHEET = """
QMainWindow {
background-color: #f4f7f6;
}
# Check ob docx installiert ist und warnen im Log /* Sidebar Styles */
if docx is None: QFrame#Sidebar {
print("WARNUNG: 'python-docx' ist nicht installiert. .docx Dateien werden ignoriert.") background-color: #2c3e50;
border: none;
}
QLabel#SidebarTitle {
color: #ecf0f1;
font-weight: bold;
font-size: 16px;
padding: 10px;
}
QListWidget {
background-color: #34495e;
color: #ecf0f1;
border: none;
outline: none;
font-size: 13px;
}
QListWidget::item {
padding: 8px;
border-bottom: 1px solid #2c3e50;
}
QListWidget::item:selected {
background-color: #1abc9c;
color: white;
}
QListWidget::item:hover {
background-color: #16a085;
}
/* Sidebar Buttons */
QPushButton#SidebarBtn {
background-color: #34495e;
color: #bdc3c7;
border: 1px solid #2c3e50;
padding: 8px;
text-align: left;
border-radius: 4px;
margin: 2px 10px;
}
QPushButton#SidebarBtn:hover {
background-color: #1abc9c;
color: white;
border: 1px solid #16a085;
}
QPushButton#CancelBtn {
background-color: #e74c3c;
color: white;
font-weight: bold;
border-radius: 4px;
margin: 10px;
padding: 8px;
}
/* Main Area */
QLineEdit {
padding: 10px;
border: 1px solid #bdc3c7;
border-radius: 20px;
font-size: 14px;
background-color: white;
selection-background-color: #3498db;
}
QLineEdit:focus {
border: 2px solid #3498db;
}
QPushButton#SearchBtn {
background-color: #3498db;
color: white;
font-weight: bold;
border-radius: 20px;
padding: 10px 20px;
font-size: 14px;
}
QPushButton#SearchBtn:hover {
background-color: #2980b9;
}
QPushButton#SearchBtn:pressed {
background-color: #1f618d;
}
/* Scroll Area & Results */
QScrollArea {
border: none;
background-color: transparent;
}
QWidget#ResultsContainer {
background-color: transparent;
}
QLabel#StatusLabel {
color: #7f8c8d;
font-size: 12px;
margin-left: 10px;
}
QProgressBar {
border: none;
background-color: #ecf0f1;
height: 4px;
text-align: center;
}
QProgressBar::chunk {
background-color: #1abc9c;
}
"""
def qt_message_handler(mode, context, message): def qt_message_handler(mode, context, message):
msg_lower = message.lower() msg_lower = message.lower()
@@ -65,7 +180,6 @@ def qt_message_handler(mode, context, message):
"unable to create font", "fontbbox", "script" "unable to create font", "fontbbox", "script"
] ]
if any(k in msg_lower for k in ignore_keywords): return if any(k in msg_lower for k in ignore_keywords): return
try: try:
sys.stdout.write(f"[Qt] {message}\n") sys.stdout.write(f"[Qt] {message}\n")
except: pass except: pass
@@ -74,45 +188,58 @@ 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"
# --- NEUE KOMPONENTE: Ein einzelnes Suchergebnis als Widget --- # --- WIDGET: Modernes Suchergebnis (Fixed Tooltips) ---
class SearchResultItem(QFrame): class SearchResultItem(QFrame):
"""
Stellt ein einzelnes Suchergebnis als 'Karte' dar.
"""
def __init__(self, filename, filepath, snippet, parent=None): def __init__(self, filename, filepath, snippet, parent=None):
super().__init__(parent) super().__init__(parent)
self.filepath = filepath self.filepath = filepath
# WICHTIG: Tooltip auf das gesamte Frame setzen, nicht nur auf Kinder
self.setToolTip(filepath)
# Design der Karte
self.setFrameShape(QFrame.Shape.StyledPanel) self.setFrameShape(QFrame.Shape.StyledPanel)
self.setStyleSheet(""" self.setStyleSheet("""
SearchResultItem { SearchResultItem {
background-color: #ffffff; background-color: white;
border: 1px solid #ddd; border: 1px solid #e0e0e0;
border-radius: 5px; border-radius: 8px;
margin-bottom: 5px;
} }
SearchResultItem:hover { SearchResultItem:hover {
background-color: #f0f8ff; border: 1px solid #3498db;
border: 1px solid #2980b9; background-color: #fbfbfb;
} }
""") """)
layout = QVBoxLayout(self) # Schatten-Effekt
layout.setContentsMargins(10, 10, 10, 10) shadow = QGraphicsDropShadowEffect(self)
shadow.setBlurRadius(10)
shadow.setXOffset(0)
shadow.setYOffset(2)
shadow.setColor(QColor(0, 0, 0, 30))
self.setGraphicsEffect(shadow)
# 1. Dateiname layout = QVBoxLayout(self)
layout.setContentsMargins(15, 15, 15, 15)
layout.setSpacing(5)
# 1. Titel (Dateiname)
self.btn_title = QPushButton(filename) self.btn_title = QPushButton(filename)
self.btn_title.setCursor(Qt.CursorShape.PointingHandCursor) self.btn_title.setCursor(Qt.CursorShape.PointingHandCursor)
# MouseTracking aktivieren hilft manchmal bei schnellen Bewegungen
self.btn_title.setMouseTracking(True)
self.btn_title.setStyleSheet(""" self.btn_title.setStyleSheet("""
QPushButton { QPushButton {
text-align: left; text-align: left;
font-weight: bold; font-weight: bold;
font-size: 14pt; font-size: 16px;
color: #2980b9; color: #2c3e50;
border: none; border: none;
background: transparent; background: transparent;
padding: 0px;
} }
QPushButton:hover { QPushButton:hover {
color: #3498db;
text-decoration: underline; text-decoration: underline;
} }
""") """)
@@ -121,26 +248,30 @@ class SearchResultItem(QFrame):
# 2. Snippet # 2. Snippet
self.lbl_snippet = QLabel(snippet) self.lbl_snippet = QLabel(snippet)
self.lbl_snippet.setWordWrap(True) self.lbl_snippet.setWordWrap(True)
self.lbl_snippet.setStyleSheet("color: #444; font-size: 10pt; margin-top: 5px;") self.lbl_snippet.setStyleSheet("color: #555; font-size: 13px; line-height: 1.4;")
# 3. Pfad (unten, klein)
path_layout = QHBoxLayout()
lbl_icon = QLabel("📄")
lbl_icon.setStyleSheet("font-size: 10px; color: #95a5a6;")
# 3. Pfad
self.lbl_path = QLabel(filepath) self.lbl_path = QLabel(filepath)
self.lbl_path.setStyleSheet("color: #888; font-size: 8pt; margin-top: 5px;") self.lbl_path.setStyleSheet("color: #95a5a6; font-size: 11px;")
path_layout.addWidget(lbl_icon)
path_layout.addWidget(self.lbl_path)
path_layout.addStretch()
layout.addWidget(self.btn_title) layout.addWidget(self.btn_title)
layout.addWidget(self.lbl_snippet) layout.addWidget(self.lbl_snippet)
layout.addWidget(self.lbl_path) layout.addLayout(path_layout)
def open_file(self): def open_file(self):
print(f"Öffne Datei: {self.filepath}")
target_path = self.filepath target_path = self.filepath
if " :: " in target_path: if " :: " in target_path:
target_path = target_path.split(" :: ")[0] target_path = target_path.split(" :: ")[0]
url = QUrl.fromLocalFile(target_path) url = QUrl.fromLocalFile(target_path)
success = QDesktopServices.openUrl(url) QDesktopServices.openUrl(url)
if not success:
print("Fehler: Konnte Datei nicht öffnen.")
# --- 1. DATENBANK MANAGER --- # --- 1. DATENBANK MANAGER ---
@@ -191,7 +322,6 @@ class DatabaseHandler:
def search(self, query): def search(self, query):
if not query.strip() or not self.model: return [] if not query.strip() or not self.model: return []
# 1. Semantik
q_vec = self.model.encode(query, convert_to_tensor=False) q_vec = self.model.encode(query, convert_to_tensor=False)
conn = sqlite3.connect(self.db_name) conn = sqlite3.connect(self.db_name)
cursor = conn.cursor() cursor = conn.cursor()
@@ -207,7 +337,6 @@ class DatabaseHandler:
scores = np.clip(scores, 0, 1) scores = np.clip(scores, 0, 1)
sem_map = {did: float(s) for did, s in zip(doc_ids, scores)} sem_map = {did: float(s) for did, s in zip(doc_ids, scores)}
# 2. Lexikalisch
words = query.replace('"', '').split() words = query.replace('"', '').split()
if not words: words = [query] if not words: words = [query]
fts_query = " OR ".join([f'"{w}"*' for w in words]) fts_query = " OR ".join([f'"{w}"*' for w in words])
@@ -222,7 +351,6 @@ class DatabaseHandler:
r2 = fuzz.partial_token_set_ratio(query.lower(), content[:5000].lower()) r2 = fuzz.partial_token_set_ratio(query.lower(), content[:5000].lower())
lex_map[did] = max(r1, r2) / 100.0 lex_map[did] = max(r1, r2) / 100.0
# 3. Hybrid
final = {} final = {}
ALPHA = 0.65 ALPHA = 0.65
BETA = 0.35 BETA = 0.35
@@ -233,7 +361,6 @@ class DatabaseHandler:
if s_score > 0.4 and l_score > 0.6: h_score += 0.1 if s_score > 0.4 and l_score > 0.6: h_score += 0.1
final[did] = h_score final[did] = h_score
# 4. Fetch
sorted_ids = sorted(final.keys(), key=lambda x: final[x], reverse=True)[:50] sorted_ids = sorted(final.keys(), key=lambda x: final[x], reverse=True)[:50]
results = [] results = []
for did in sorted_ids: for did in sorted_ids:
@@ -269,7 +396,6 @@ class IndexerThread(QThread):
ext = os.path.splitext(filename)[1].lower() ext = os.path.splitext(filename)[1].lower()
text = "" text = ""
try: try:
# --- PDF ---
if ext == ".pdf": if ext == ".pdf":
try: try:
with pdfplumber.open(stream) as pdf: with pdfplumber.open(stream) as pdf:
@@ -277,17 +403,34 @@ class IndexerThread(QThread):
if t := p.extract_text(): text += t + "\n" if t := p.extract_text(): text += t + "\n"
except: pass except: pass
# --- WORD / DOCX ---
elif ext == ".docx" and docx is not None: elif ext == ".docx" and docx is not None:
try: try:
# python-docx kann file-like objects (BytesIO oder file) lesen
doc = docx.Document(stream) doc = docx.Document(stream)
for para in doc.paragraphs: for para in doc.paragraphs: text += para.text + "\n"
text += para.text + "\n" except: pass
except Exception as e:
print(f"Docx Error {filename}: {e}") elif ext == ".xlsx" and openpyxl is not None:
try:
wb = openpyxl.load_workbook(stream, data_only=True, read_only=True)
for sheet in wb.worksheets:
text += f"\n--- {sheet.title} ---\n"
for row in sheet.iter_rows(values_only=True):
row_text = " ".join([str(c) for c in row if c is not None])
if row_text.strip(): text += row_text + "\n"
except: pass
elif ext == ".pptx" and Presentation is not None:
try:
prs = Presentation(stream)
for i, slide in enumerate(prs.slides):
text += f"\n--- Folie {i+1} ---\n"
for shape in slide.shapes:
if shape.has_text_frame:
for paragraph in shape.text_frame.paragraphs:
for run in paragraph.runs: text += run.text + " "
text += "\n"
except: pass
# --- PLAIN TEXT ---
elif ext in [".txt", ".md", ".py", ".json", ".csv", ".html", ".log", ".ini", ".xml"]: elif ext in [".txt", ".md", ".py", ".json", ".csv", ".html", ".log", ".ini", ".xml"]:
try: try:
content = stream.read() content = stream.read()
@@ -301,7 +444,6 @@ class IndexerThread(QThread):
conn = sqlite3.connect(self.db_name) conn = sqlite3.connect(self.db_name)
cursor = conn.cursor() cursor = conn.cursor()
# Cleanup
cursor.execute("SELECT rowid FROM documents WHERE path LIKE ?", (f"{self.folder_path}%",)) cursor.execute("SELECT rowid FROM documents WHERE path LIKE ?", (f"{self.folder_path}%",))
ids = [r[0] for r in cursor.fetchall()] ids = [r[0] for r in cursor.fetchall()]
if ids: if ids:
@@ -333,12 +475,16 @@ class IndexerThread(QThread):
indexed += 1 indexed += 1
except: skipped += 1 except: skipped += 1
else: else:
try:
with open(path, "rb") as f: with open(path, "rb") as f:
content = self._extract_text(f, file) file_content = io.BytesIO(f.read())
content = self._extract_text(file_content, file)
if content and len(content.strip()) > 20: if content and len(content.strip()) > 20:
self._save(cursor, file, path, content) self._save(cursor, file, path, content)
indexed += 1 indexed += 1
else: skipped += 1 else: skipped += 1
except: skipped += 1
if cancelled: break if cancelled: break
conn.commit() conn.commit()
@@ -351,7 +497,7 @@ class IndexerThread(QThread):
vec = self.model.encode(content[:8000], convert_to_tensor=False).tobytes() vec = self.model.encode(content[:8000], convert_to_tensor=False).tobytes()
cursor.execute("INSERT INTO embeddings (doc_id, vec) VALUES (?, ?)", (did, vec)) cursor.execute("INSERT INTO embeddings (doc_id, vec) VALUES (?, ?)", (did, vec))
# --- 3. UI --- # --- 3. UI MAIN WINDOW ---
class UffWindow(QMainWindow): class UffWindow(QMainWindow):
def __init__(self, splash=None): def __init__(self, splash=None):
@@ -362,90 +508,111 @@ class UffWindow(QMainWindow):
self.load_saved_folders() self.load_saved_folders()
def initUI(self): def initUI(self):
self.setWindowTitle("UFF Search") self.setWindowTitle("UFF Search v7.2 (Stable Tooltips)")
self.resize(1000, 700) self.resize(1100, 750)
self.setStyleSheet(STYLESHEET)
central = QWidget() central = QWidget()
self.setCentralWidget(central) self.setCentralWidget(central)
main_layout = QHBoxLayout(central) main_layout = QHBoxLayout(central)
main_layout.setContentsMargins(0, 0, 0, 0)
main_layout.setSpacing(0)
# -- LINKS (Sidebar) -- # -- SIDEBAR --
left_panel = QFrame() left_panel = QFrame()
left_panel.setFixedWidth(250) left_panel.setObjectName("Sidebar")
left_panel.setStyleSheet("background-color: #f0f0f0; border-right: 1px solid #ccc;") left_panel.setFixedWidth(260)
left_layout = QVBoxLayout(left_panel) left_layout = QVBoxLayout(left_panel)
left_layout.setContentsMargins(0, 20, 0, 20)
lbl_title = QLabel(" UFF SEARCH")
lbl_title.setObjectName("SidebarTitle")
self.folder_list = QListWidget() self.folder_list = QListWidget()
self.folder_list.setStyleSheet("border: 1px solid #ddd; background: white;")
btn_add = QPushButton(" + Ordner") icon_add = self.style().standardIcon(QStyle.StandardPixmap.SP_FileDialogNewFolder)
icon_del = self.style().standardIcon(QStyle.StandardPixmap.SP_TrashIcon)
icon_refresh = self.style().standardIcon(QStyle.StandardPixmap.SP_BrowserReload)
icon_stop = self.style().standardIcon(QStyle.StandardPixmap.SP_DialogCancelButton)
btn_add = QPushButton(" Ordner hinzufügen")
btn_add.setObjectName("SidebarBtn")
btn_add.setIcon(icon_add)
btn_add.clicked.connect(self.add_new_folder) btn_add.clicked.connect(self.add_new_folder)
btn_del = QPushButton(" - Löschen")
btn_del = QPushButton(" Ordner entfernen")
btn_del.setObjectName("SidebarBtn")
btn_del.setIcon(icon_del)
btn_del.clicked.connect(self.delete_selected_folder) btn_del.clicked.connect(self.delete_selected_folder)
self.btn_rescan = QPushButton(" ↻ Scan")
self.btn_rescan = QPushButton(" Neu scannen")
self.btn_rescan.setObjectName("SidebarBtn")
self.btn_rescan.setIcon(icon_refresh)
self.btn_rescan.clicked.connect(self.rescan_selected_folder) self.btn_rescan.clicked.connect(self.rescan_selected_folder)
self.btn_cancel = QPushButton("🛑 Stop")
self.btn_cancel = QPushButton("STOPPEN")
self.btn_cancel.setObjectName("CancelBtn")
self.btn_cancel.setIcon(icon_stop)
self.btn_cancel.clicked.connect(self.cancel_indexing) self.btn_cancel.clicked.connect(self.cancel_indexing)
self.btn_cancel.hide() self.btn_cancel.hide()
self.btn_cancel.setStyleSheet("background-color: #ffcccc; color: red;")
left_layout.addWidget(QLabel("<b>📂 Indizierte Ordner</b>")) left_layout.addWidget(lbl_title)
left_layout.addSpacing(10)
left_layout.addWidget(self.folder_list) left_layout.addWidget(self.folder_list)
left_layout.addSpacing(10)
left_layout.addWidget(btn_add) left_layout.addWidget(btn_add)
left_layout.addWidget(btn_del) left_layout.addWidget(btn_del)
left_layout.addStretch()
left_layout.addWidget(self.btn_rescan) left_layout.addWidget(self.btn_rescan)
left_layout.addWidget(self.btn_cancel) left_layout.addWidget(self.btn_cancel)
# -- RECHTS (Suche & Ergebnisse) -- # -- RECHTS (Hauptbereich) --
right_panel = QWidget() right_panel = QWidget()
right_panel.setObjectName("MainArea")
right_layout = QVBoxLayout(right_panel) right_layout = QVBoxLayout(right_panel)
right_layout.setContentsMargins(30, 30, 30, 30)
right_layout.setSpacing(15)
# Suchleiste # Header
search_box = QHBoxLayout() search_box = QHBoxLayout()
self.input_search = QLineEdit() self.input_search = QLineEdit()
self.input_search.setPlaceholderText("Suchbegriff eingeben...") self.input_search.setPlaceholderText("Wonach suchst du heute?")
self.input_search.setStyleSheet("padding: 8px; font-size: 14px;")
self.input_search.returnPressed.connect(self.perform_search) self.input_search.returnPressed.connect(self.perform_search)
self.btn_go = QPushButton("Suchen") self.btn_go = QPushButton("Suchen")
self.btn_go.setFixedWidth(100) self.btn_go.setObjectName("SearchBtn")
self.btn_go.setStyleSheet("background-color: #2980b9; color: white; padding: 8px; font-weight: bold;") self.btn_go.setCursor(Qt.CursorShape.PointingHandCursor)
self.btn_go.clicked.connect(self.perform_search) self.btn_go.clicked.connect(self.perform_search)
search_box.addWidget(self.input_search) search_box.addWidget(self.input_search)
search_box.addWidget(self.btn_go) search_box.addWidget(self.btn_go)
# Status & Progress # Status
self.lbl_status = QLabel("Warte auf Modell...") status_box = QHBoxLayout()
self.lbl_status = QLabel("Modell wird geladen...")
self.lbl_status.setObjectName("StatusLabel")
self.progress_bar = QProgressBar() self.progress_bar = QProgressBar()
self.progress_bar.hide() self.progress_bar.hide()
status_box.addWidget(self.lbl_status)
status_box.addWidget(self.progress_bar)
# ERGEBNIS-BEREICH (QScrollArea statt QTextBrowser) # Ergebnisse
self.scroll_area = QScrollArea() self.scroll_area = QScrollArea()
self.scroll_area.setWidgetResizable(True) self.scroll_area.setWidgetResizable(True)
self.scroll_area.setStyleSheet("background-color: #fafafa; border: none;")
# Container Widget für die Ergebnisse
self.results_container = QWidget() self.results_container = QWidget()
self.results_container.setStyleSheet("background-color: transparent;") self.results_container.setObjectName("ResultsContainer")
self.results_layout = QVBoxLayout(self.results_container) self.results_layout = QVBoxLayout(self.results_container)
self.results_layout.setAlignment(Qt.AlignmentFlag.AlignTop) self.results_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
self.results_layout.setSpacing(10) self.results_layout.setSpacing(15)
self.scroll_area.setWidget(self.results_container) self.scroll_area.setWidget(self.results_container)
right_layout.addLayout(search_box) right_layout.addLayout(search_box)
right_layout.addWidget(self.lbl_status) right_layout.addLayout(status_box)
right_layout.addWidget(self.progress_bar)
right_layout.addWidget(self.scroll_area) right_layout.addWidget(self.scroll_area)
# Splitter main_layout.addWidget(left_panel)
splitter = QSplitter() main_layout.addWidget(right_panel)
splitter.addWidget(left_panel)
splitter.addWidget(right_panel)
splitter.setSizes([250, 750])
main_layout.addWidget(splitter)
self.set_ui_enabled(False) self.set_ui_enabled(False)
def set_ui_enabled(self, enabled): def set_ui_enabled(self, enabled):
@@ -465,44 +632,38 @@ class UffWindow(QMainWindow):
QMessageBox.critical(self, "Fehler", "Modell konnte nicht geladen werden.") QMessageBox.critical(self, "Fehler", "Modell konnte nicht geladen werden.")
return return
self.db.model = model self.db.model = model
self.lbl_status.setText("Bereit.") self.lbl_status.setText("Bereit für deine Suche.")
self.set_ui_enabled(True) self.set_ui_enabled(True)
def perform_search(self): def perform_search(self):
query = self.input_search.text() query = self.input_search.text()
if not query: return if not query: return
self.lbl_status.setText("Suche läuft...") self.lbl_status.setText("Suche läuft...")
QApplication.processEvents() QApplication.processEvents()
# 1. Alte Ergebnisse löschen
while self.results_layout.count(): while self.results_layout.count():
child = self.results_layout.takeAt(0) child = self.results_layout.takeAt(0)
if child.widget(): if child.widget(): child.widget().deleteLater()
child.widget().deleteLater()
# 2. Suchen
results = self.db.search(query) results = self.db.search(query)
self.lbl_status.setText(f"{len(results)} Treffer.") self.lbl_status.setText(f"{len(results)} Treffer gefunden.")
# 3. Neue Ergebnisse als Widgets hinzufügen
if not results: if not results:
lbl = QLabel("Keine Ergebnisse gefunden.") lbl = QLabel("Leider keine Ergebnisse.")
lbl.setStyleSheet("color: #777; font-size: 14pt; margin-top: 20px;") lbl.setStyleSheet("color: #95a5a6; font-size: 18px; margin-top: 40px;")
lbl.setAlignment(Qt.AlignmentFlag.AlignHCenter) lbl.setAlignment(Qt.AlignmentFlag.AlignHCenter)
self.results_layout.addWidget(lbl) self.results_layout.addWidget(lbl)
else: else:
for fname, fpath, snippet in results: for fname, fpath, snippet in results:
item = SearchResultItem(fname, fpath, snippet) self.results_layout.addWidget(SearchResultItem(fname, fpath, snippet))
self.results_layout.addWidget(item)
self.results_layout.addStretch() self.results_layout.addStretch()
# --- Folder Management ---
def load_saved_folders(self): def load_saved_folders(self):
self.folder_list.clear() self.folder_list.clear()
for f in self.db.get_folders(): for f in self.db.get_folders():
self.folder_list.addItem(QListWidgetItem(f)) item = QListWidgetItem(self.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon), f)
item.setToolTip(f)
self.folder_list.addItem(item)
def add_new_folder(self): def add_new_folder(self):
f = QFileDialog.getExistingDirectory(self, "Ordner wählen") f = QFileDialog.getExistingDirectory(self, "Ordner wählen")
@@ -534,19 +695,21 @@ class UffWindow(QMainWindow):
def idx_done(self, n, s, c): def idx_done(self, n, s, c):
self.set_ui_enabled(True) self.set_ui_enabled(True)
self.btn_cancel.hide(); self.btn_rescan.show(); self.progress_bar.hide() self.btn_cancel.hide(); self.btn_rescan.show(); self.progress_bar.hide()
msg = "Abgebrochen" if c else "Fertig" msg = "Abgebrochen" if c else "Indexierung fertig"
self.lbl_status.setText(f"{msg}: {n} neu, {s} übersprungen.") self.lbl_status.setText(f"{msg}: {n} neu, {s} übersprungen.")
if __name__ == "__main__": if __name__ == "__main__":
app = QApplication(sys.argv) app = QApplication(sys.argv)
font = QFont("Segoe UI", 10)
app.setFont(font)
splash = None splash = None
try: try:
if os.path.exists("assets/uff_banner.jpeg"): if os.path.exists("assets/uff_banner.jpeg"):
splash = QSplashScreen(QPixmap("assets/uff_banner.jpeg")) splash = QSplashScreen(QPixmap("assets/uff_banner.jpeg"))
splash.show() splash.show()
except: pass except: pass
w = UffWindow(splash) w = UffWindow(splash)
w.show() w.show()
w.start_model_loading() w.start_model_loading()