5 Commits

5 changed files with 178 additions and 72 deletions

View File

@@ -1,7 +1,7 @@
{ {
"name": "Projekcija", "name": "Projekcija",
"description": "Aplikacija za projekcijo besedil pesmi na zaslon.", "description": "Aplikacija za projekcijo besedil pesmi na zaslon.",
"version": "0.7.0", "version": "0.8.0",
"authors": [ "authors": [
"Uroš Urbanija (izvorna zasnova)", "Uroš Urbanija (izvorna zasnova)",
"Valentin Korenjak (nadgradnje in vzdrževanje)" "Valentin Korenjak (nadgradnje in vzdrževanje)"

View File

@@ -34,12 +34,12 @@ import urllib.request
import tempfile import tempfile
from db_schema import create_tables from db_schema import create_tables
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
APPINFO_PATH = os.path.join(BASE_DIR, 'appinfo.json')
DB_PATH = 'songs.db' DB_PATH = 'songs.db'
SETTINGS_PATH = 'settings.json' SETTINGS_PATH = 'settings.json'
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
class SongProjector: class SongProjector:
def __init__(self, root): def __init__(self, root):
self.root = root self.root = root
@@ -239,30 +239,81 @@ class SongProjector:
""" """
Vrne seznam pod-vrstic, ki skupaj tvorijo `line`, Vrne seznam pod-vrstic, ki skupaj tvorijo `line`,
pri čemer so dolge največ `self.color_width` pikslov. pri čemer so dolge največ `self.color_width` pikslov.
Namesto preprostega 'greedy' preloma izračuna število delov
in poskuša narediti vrstice podobnih dolžin.
""" """
words = line.split() words = line.split()
if not words: if not words:
return [""] return [""]
sub_lines = [] total_width = self.custom_font.measure(line)
current_words = [] if total_width <= self.color_width:
return [line]
# Izračunamo, na koliko delov moramo prelomiti vrstico
num_parts = (total_width // self.color_width) + 1
# Če je num_parts še vedno premalo (zaradi presledkov), preverimo z greedy
temp_sub_lines = []
current_words = []
for word in words: for word in words:
test_line = " ".join(current_words + [word]) test_line = " ".join(current_words + [word])
# Izmerimo širino v pikslih
if self.custom_font.measure(test_line) <= self.color_width: if self.custom_font.measure(test_line) <= self.color_width:
current_words.append(word) current_words.append(word)
else: else:
if current_words: if current_words:
sub_lines.append(" ".join(current_words)) temp_sub_lines.append(" ".join(current_words))
current_words = [word] current_words = [word]
else: else:
# Beseda je sama po sebi predolga (zelo redko) temp_sub_lines.append(word)
sub_lines.append(word)
current_words = [] current_words = []
if current_words: if current_words:
sub_lines.append(" ".join(current_words)) temp_sub_lines.append(" ".join(current_words))
num_parts = max(num_parts, len(temp_sub_lines))
if num_parts <= 1:
return [line]
# Ciljna dolžina vsakega dela
target_width = total_width / num_parts
sub_lines = []
remaining_words = words
# Porazdelimo besede tako, da so vrstice čim bolj enakomerne
for i in range(num_parts - 1):
best_split_idx = 1
min_diff = float('inf')
for j in range(1, len(remaining_words)):
test_line = " ".join(remaining_words[:j])
width = self.custom_font.measure(test_line)
if width > self.color_width:
break
diff = abs(width - target_width)
if diff < min_diff:
min_diff = diff
best_split_idx = j
else:
# Ker širina narašča, ko se enkrat začne diff povečevati,
# smo našli lokalni minimum za to vrstico
break
sub_lines.append(" ".join(remaining_words[:best_split_idx]))
remaining_words = remaining_words[best_split_idx:]
if not remaining_words:
break
if remaining_words:
# Zadnji del (lahko je še vedno predolg, če so bile prejšnje vrstice prekratke)
last_line = " ".join(remaining_words)
if self.custom_font.measure(last_line) <= self.color_width:
sub_lines.append(last_line)
else:
# Če je zadnji del še vedno predolg, ga rekurzivno razbijemo (varnostni mehanizem)
sub_lines.extend(self.split_long_line(last_line))
return sub_lines return sub_lines
@@ -364,16 +415,19 @@ class SongProjector:
processed_stanzas = [] processed_stanzas = []
for stanza in stanzas_raw: for stanza in stanzas_raw:
# Znotraj kitice ohranimo enojne prazne vrstice kot razmike # Razdelimo kitico na odstavke (ločene z eno prazno vrstico)
lines = stanza.splitlines() paragraphs_raw = re.split(r'\n\s*\n', stanza)
stanza_lines = [] stanza_paragraphs = []
for line in lines: for para in paragraphs_raw:
if line.strip(): lines = para.splitlines()
stanza_lines.extend(self.split_long_line(line)) para_lines = []
else: for line in lines:
stanza_lines.append("") # Enojna prazna vrstica znotraj kitice if line.strip():
if stanza_lines: para_lines.extend(self.split_long_line(line))
processed_stanzas.append(stanza_lines) if para_lines:
stanza_paragraphs.append(para_lines)
if stanza_paragraphs:
processed_stanzas.append(stanza_paragraphs)
# 2. Razdeljevanje kitic na strani # 2. Razdeljevanje kitic na strani
pages = [] pages = []
@@ -381,66 +435,80 @@ class SongProjector:
current_height = 0 current_height = 0
split_by_stanza = self.settings.get("split_by_stanza", False) split_by_stanza = self.settings.get("split_by_stanza", False)
for stanza in processed_stanzas: for stanza_paragraphs in processed_stanzas:
stanza_height = len(stanza) * self.line_height # Če je vklopljen split_by_stanza, vsaka kitica dobi svojo stran (ali več, če je predolga)
# Če je vklopljen split_by_stanza, vsaka kitica dobi svojo stran
if split_by_stanza: if split_by_stanza:
if current_page_lines: if current_page_lines:
pages.append("\n".join(current_page_lines)) pages.append("\n".join(current_page_lines))
current_page_lines = [] current_page_lines = []
current_height = 0 current_height = 0
# Če je kitica predolga za eno stran, jo še vedno moramo razdeliti for para in stanza_paragraphs:
if stanza_height > max_height: para_height = len(para) * self.line_height
temp_stanza_lines = [] # Če odstavek ne gre na trenutno stran (ki je v tem načinu prazna ali vsebuje prejšnje odstavke iste kitice)
temp_height = 0 if current_height + para_height + (self.line_height if current_page_lines else 0) > max_height:
for line in stanza: if current_page_lines:
if temp_height + self.line_height > max_height: pages.append("\n".join(current_page_lines))
pages.append("\n".join(temp_stanza_lines)) current_page_lines = []
temp_stanza_lines = [line] current_height = 0
temp_height = self.line_height
# Če je sam odstavek predolg za eno stran, ga razdelimo po vrsticah
if para_height > max_height:
for line in para:
if current_height + self.line_height > max_height:
pages.append("\n".join(current_page_lines))
current_page_lines = [line]
current_height = self.line_height
else:
current_page_lines.append(line)
current_height += self.line_height
else: else:
temp_stanza_lines.append(line) current_page_lines = para[:]
temp_height += self.line_height current_height = para_height
if temp_stanza_lines: else:
pages.append("\n".join(temp_stanza_lines)) if current_page_lines:
else: current_page_lines.append("") # Razmik med odstavki znotraj kitice
pages.append("\n".join(stanza)) current_height += self.line_height
continue current_page_lines.extend(para)
current_height += para_height
# Standardna logika (več kitic na stran, če grejo)
if stanza_height > max_height:
if current_page_lines: if current_page_lines:
pages.append("\n".join(current_page_lines)) pages.append("\n".join(current_page_lines))
current_page_lines = [] current_page_lines = []
current_height = 0 current_height = 0
continue
# Standardna logika (več kitic na stran, če grejo, upoštevajoč odstavke)
for para in stanza_paragraphs:
para_height = len(para) * self.line_height
temp_stanza_lines = [] # Preverimo, če odstavek gre na trenutno stran
temp_height = 0 # (self.line_height if current_page_lines else 0) upošteva prazno vrstico pred odstavkom
for line in stanza: if current_height + para_height + (self.line_height if current_page_lines else 0) > max_height:
if temp_height + self.line_height > max_height: if current_page_lines:
pages.append("\n".join(temp_stanza_lines)) pages.append("\n".join(current_page_lines))
temp_stanza_lines = [line] current_page_lines = []
temp_height = self.line_height current_height = 0
# Če je sam odstavek predolg za eno stran
if para_height > max_height:
for line in para:
if current_height + self.line_height > max_height:
pages.append("\n".join(current_page_lines))
current_page_lines = [line]
current_height = self.line_height
else:
current_page_lines.append(line)
current_height += self.line_height
else: else:
temp_stanza_lines.append(line) current_page_lines = para[:]
temp_height += self.line_height current_height = para_height
if temp_stanza_lines: else:
current_page_lines = temp_stanza_lines if current_page_lines:
current_height = temp_height current_page_lines.append("")
current_height += self.line_height
elif current_height + stanza_height + (self.line_height if current_page_lines else 0) > max_height: current_page_lines.extend(para)
pages.append("\n".join(current_page_lines)) current_height += para_height
current_page_lines = stanza
current_height = stanza_height
else:
if current_page_lines:
current_page_lines.append("") # Razmik med kiticami
current_height += self.line_height
current_page_lines.extend(stanza)
current_height += stanza_height
if current_page_lines: if current_page_lines:
pages.append("\n".join(current_page_lines)) pages.append("\n".join(current_page_lines))
@@ -726,7 +794,7 @@ class SongProjector:
def show_app_info_tkinter(self): def show_app_info_tkinter(self):
"""Prikaže informacije o aplikaciji v glavnem oknu (ukaz 9900).""" """Prikaže informacije o aplikaciji v glavnem oknu (ukaz 9900)."""
try: try:
with open('appinfo.json', 'r', encoding='utf-8') as f: with open(APPINFO_PATH, 'r', encoding='utf-8') as f:
info = json.load(f) info = json.load(f)
self.cursor.execute("SELECT COUNT(*) FROM songs") self.cursor.execute("SELECT COUNT(*) FROM songs")

View File

@@ -204,8 +204,10 @@ def toggle_split():
def get_app_info(): def get_app_info():
"""Vrne informacije o aplikaciji iz appinfo.json""" """Vrne informacije o aplikaciji iz appinfo.json"""
try: try:
# appinfo.json je v korenskem imeniku projekta # appinfo.json je v korenskem imeniku projekta (nad web/)
with open('appinfo.json', 'r', encoding='utf-8') as f: base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
appinfo_path = os.path.join(base_dir, 'appinfo.json')
with open(appinfo_path, 'r', encoding='utf-8') as f:
info = json.load(f) info = json.load(f)
# Dodaj število pesmi v bazi # Dodaj število pesmi v bazi

View File

@@ -533,6 +533,31 @@ textarea.form-control {
font-size: 0.95rem; font-size: 0.95rem;
} }
.about-codes-section {
text-align: left;
background: rgba(255, 255, 255, 0.05);
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
}
.about-codes-section h3 {
margin-top: 0;
font-size: 1rem;
color: #1f8a46;
}
.about-codes-list {
list-style: none;
padding: 0;
margin: 10px 0 0 0;
}
.about-codes-list li {
margin-bottom: 5px;
font-size: 0.95rem;
}
.btn-primary { .btn-primary {
background-color: #1f8a46; background-color: #1f8a46;
color: white; color: white;

View File

@@ -95,6 +95,17 @@
<h3>Avtorji:</h3> <h3>Avtorji:</h3>
<ul id="about-authors" class="about-authors-list"></ul> <ul id="about-authors" class="about-authors-list"></ul>
</div> </div>
<div class="about-codes-section">
<h3>Posebne kode:</h3>
<ul class="about-codes-list">
<li><strong>9999</strong>: Izklop računalnika</li>
<li><strong>9998</strong>: Ponovni zagon računalnika</li>
<li><strong>9997</strong>: Ponovni zagon programa</li>
<li><strong>9988</strong>: Izhod iz programa</li>
<li><strong>9901</strong>: Posodobitev baze pesmi</li>
<li><strong>9900</strong>: Informacije o programu</li>
</ul>
</div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button id="about-close-btn" class="btn-primary">Zapri</button> <button id="about-close-btn" class="btn-primary">Zapri</button>