made installer and fixed releated bug fixes

This commit is contained in:
2026-01-10 14:46:36 +01:00
parent 8739455231
commit f1413a2eca
8 changed files with 107 additions and 28 deletions

2
.gitignore vendored
View File

@@ -4,4 +4,4 @@
/build /build
__pycache__/ __pycache__/
*.iss *.iss
*.spec UFF-Search.spec

View File

@@ -2,10 +2,6 @@
# UFF Search - Unsorted Folder Full-Text Search # UFF Search - Unsorted Folder Full-Text Search
![GitHub stars](https://img.shields.io/github/stars/BildoBeucklin/unsorted-folder-full-text-search?style=social)
![GitHub forks](https://img.shields.io/github/forks/BildoBeucklin/unsorted-folder-full-text-search?style=social)
![GitHub license](https://img.shields.io/github/license/BildoBeucklin/unsorted-folder-full-text-search)
UFF Search is a powerful desktop application for Windows that allows you to perform fast, intelligent, and fuzzy full-text searches on your local files, including searching inside ZIP archives. UFF Search is a powerful desktop application for Windows that allows you to perform fast, intelligent, and fuzzy full-text searches on your local files, including searching inside ZIP archives.
It builds a local search index for the folders you specify, allowing you to quickly find documents based on their meaning (semantic search) and specific keywords, even with typos in your search query. It builds a local search index for the folders you specify, allowing you to quickly find documents based on their meaning (semantic search) and specific keywords, even with typos in your search query.
@@ -68,12 +64,18 @@ To create a standalone executable from the source code, you can use `pyinstaller
2. **Build the executable:** 2. **Build the executable:**
```bash ```bash
pyinstaller --name "UFF_Search" --windowed --onefile --icon="favicon.ico" --add-data "assets;assets" main.py pyinstaller --noconfirm --onedir --windowed --add-data "assets;assets" --icon "assets/favicon.ico" main.py
``` ```
This command will create a single executable file in the `dist` folder. Or:
```bash
pyinstaller main.spec
```
Both of these commands will create a single executable file in the `dist` folder. It may take some time to build.
## Usage ## Usage
(texts are only in german)
1. Start the application. 1. Start the application.
2. Click **" + Hinzufügen"** (Add) to select a folder you want to index. The application will start scanning it immediately. 2. Click **" + Hinzufügen"** (Add) to select a folder you want to index. The application will start scanning it immediately.
3. Once indexing is complete, type your search query into the search bar and press Enter or click **"Suchen"** (Search). 3. Once indexing is complete, type your search query into the search bar and press Enter or click **"Suchen"** (Search).
@@ -94,6 +96,7 @@ This command will create a single executable file in the `dist` folder.
* `openpyxl` for `.xlsx` files. * `openpyxl` for `.xlsx` files.
* `python-pptx` for `.pptx` files. * `python-pptx` for `.pptx` files.
* **Index Location:** The search index database (`uff_index.db`) is stored in `%LOCALAPPDATA%\UFF_Search` on Windows. * **Index Location:** The search index database (`uff_index.db`) is stored in `%LOCALAPPDATA%\UFF_Search` on Windows.
* **Size:** (ca. 400-600 MB)
## License ## License

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

View File

@@ -15,22 +15,32 @@ if not os.path.exists(APP_DATA_DIR):
DB_NAME = os.path.join(APP_DATA_DIR, "uff_index.db") DB_NAME = os.path.join(APP_DATA_DIR, "uff_index.db")
LOG_FILE = os.path.join(APP_DATA_DIR, "uff.log") LOG_FILE = os.path.join(APP_DATA_DIR, "uff.log")
def resource_path(relative_path):
"""
Holt den absoluten Pfad zu Ressourcen.
Funktioniert für Dev-Modus UND für PyInstaller EXE (_MEIPASS).
"""
try:
# PyInstaller erstellt temporären Ordner _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
# --- LOGGING KLASSE --- # --- LOGGING KLASSE ---
class Logger(object): class Logger(object):
def __init__(self): def __init__(self):
# "w" überschreibt bei jedem Start. Nutze "a" für anhängen (append). self.terminal = sys.stdout
self.terminal = sys.stdout # Optional: Falls du es AUCH im Terminal sehen willst
self.log = open(LOG_FILE, "w", encoding="utf-8") self.log = open(LOG_FILE, "w", encoding="utf-8")
def write(self, message): def write(self, message):
# Optional: ins Terminal schreiben (auskommentieren, wenn du nur Logfile willst)
# self.terminal.write(message)
self.log.write(message) self.log.write(message)
self.log.flush() self.log.flush()
def flush(self): def flush(self):
# self.terminal.flush()
self.log.flush() self.log.flush()
# --- AKTIVIERUNG DES LOGGERS --- # --- AKTIVIERUNG DES LOGGERS ---

17
main.py
View File

@@ -7,7 +7,7 @@ from PyQt6.QtGui import QPixmap, QFont, QIcon
from PyQt6.QtCore import qInstallMessageHandler, QTimer, Qt from PyQt6.QtCore import qInstallMessageHandler, QTimer, Qt
# Config zuerst! # Config zuerst!
from config import qt_message_handler, LOG_FILE from config import qt_message_handler, LOG_FILE, resource_path
from ui import UffWindow, ModernSplashScreen, ModelLoaderThread from ui import UffWindow, ModernSplashScreen, ModelLoaderThread
@@ -19,15 +19,14 @@ if __name__ == "__main__":
app = QApplication(sys.argv) app = QApplication(sys.argv)
app.setFont(QFont("Segoe UI", 10)) app.setFont(QFont("Segoe UI", 10))
# 1. ICON SETZEN (Für die ganze App) icon_path = resource_path("assets/uff_icon.jpeg") # <--- HIER
# Wenn assets/icon.png existiert, wird es genutzt. if os.path.exists(icon_path):
if os.path.exists("assets/icon.png"): app_icon = QIcon(icon_path)
app_icon = QIcon("assets/icon.png")
app.setWindowIcon(app_icon) app.setWindowIcon(app_icon)
# 2. SPLASH SCREEN ERSTELLEN # SPLASH LADEN (Mit resource_path)
splash_pix = QPixmap("assets/uff_banner.jpeg") banner_path = resource_path("assets/uff_banner.jpeg")
# Falls kein Bild da ist, nehmen wir ein leeres (damit es nicht crasht) splash_pix = QPixmap(banner_path)
if splash_pix.isNull(): if splash_pix.isNull():
splash_pix = QPixmap(600, 400) splash_pix = QPixmap(600, 400)
splash_pix.fill(Qt.GlobalColor.white) splash_pix.fill(Qt.GlobalColor.white)
@@ -35,8 +34,6 @@ if __name__ == "__main__":
splash = ModernSplashScreen(splash_pix) splash = ModernSplashScreen(splash_pix)
splash.show() splash.show()
# 3. LADEN SIMULIEREN & STARTEN
# Wir nutzen einen kleinen Trick, um den Start visuell zu "begleiten"
splash.set_progress(10, "Lade Konfiguration...") splash.set_progress(10, "Lade Konfiguration...")
app.processEvents() app.processEvents()

72
main.spec Normal file
View File

@@ -0,0 +1,72 @@
# -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import collect_all
# --- 1. SPEZIELLE BIBLIOTHEKEN SAMMELN ---
# sentence_transformers und rapidfuzz sind komplex, wir holen alles automatisch
datas = [('assets', 'assets')]
binaries = []
hiddenimports = [
'docx',
'openpyxl',
'pptx',
'pdfplumber',
'rapidfuzz',
'sentence_transformers',
'numpy'
]
# Sammle alle Daten für die KI-Bibliothek (verhindert Import-Fehler)
tmp_ret = collect_all('sentence_transformers')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
# Sammle rapidfuzz sicherheitshalber auch komplett
tmp_ret = collect_all('rapidfuzz')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
# --- 2. ANALYSE ---
a = Analysis(
['main.py'],
pathex=[],
binaries=binaries,
datas=datas,
hiddenimports=hiddenimports,
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
# --- 3. EXE ERSTELLEN ---
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='UFF_Search', # Name der Datei (UFF_Search.exe)
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon='assets\\favicon.ico', # Pfad zum Icon
)
# --- 4. ORDNER ZUSAMMENSTELLEN ---
coll = COLLECT(
exe,
a.binaries,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='UFF_Search', # Name des Ordners
)

5
ui.py
View File

@@ -1,6 +1,6 @@
# ui.py # ui.py
import os import os
from PyQt6.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, from PyQt6.QtWidgets import (QApplication, 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,
@@ -133,9 +133,6 @@ class UffWindow(QMainWindow):
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()