Merge branch 'denizsafak:main' into main

This commit is contained in:
Juraj Borza
2025-07-08 13:02:47 +02:00
committed by GitHub
11 changed files with 334 additions and 42 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 585 B

+31
View File
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg height="800px" width="800px" version="1.1" id="_x32_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 512 512" xml:space="preserve">
<style type="text/css">
.st0{fill:#808080;}
</style>
<g>
<path class="st0" d="M502.325,307.303l-39.006-30.805c-6.215-4.908-9.665-12.429-9.668-20.348c0-0.084,0-0.168,0-0.252
c-0.014-7.936,3.44-15.478,9.667-20.396l39.007-30.806c8.933-7.055,12.093-19.185,7.737-29.701l-17.134-41.366
c-4.356-10.516-15.167-16.86-26.472-15.532l-49.366,5.8c-7.881,0.926-15.656-1.966-21.258-7.586
c-0.059-0.06-0.118-0.119-0.177-0.178c-5.597-5.602-8.476-13.36-7.552-21.225l5.799-49.363
c1.328-11.305-5.015-22.116-15.531-26.472L337.004,1.939c-10.516-4.356-22.646-1.196-29.701,7.736l-30.805,39.005
c-4.908,6.215-12.43,9.665-20.349,9.668c-0.084,0-0.168,0-0.252,0c-7.935,0.014-15.477-3.44-20.395-9.667L204.697,9.675
c-7.055-8.933-19.185-12.092-29.702-7.736L133.63,19.072c-10.516,4.356-16.86,15.167-15.532,26.473l5.799,49.366
c0.926,7.881-1.964,15.656-7.585,21.257c-0.059,0.059-0.118,0.118-0.178,0.178c-5.602,5.598-13.36,8.477-21.226,7.552
l-49.363-5.799c-11.305-1.328-22.116,5.015-26.472,15.531L1.939,174.996c-4.356,10.516-1.196,22.646,7.736,29.701l39.006,30.805
c6.215,4.908,9.665,12.429,9.668,20.348c0,0.084,0,0.167,0,0.251c0.014,7.935-3.44,15.477-9.667,20.395L9.675,307.303
c-8.933,7.055-12.092,19.185-7.736,29.701l17.134,41.365c4.356,10.516,15.168,16.86,26.472,15.532l49.366-5.799
c7.882-0.926,15.656,1.965,21.258,7.586c0.059,0.059,0.118,0.119,0.178,0.178c5.597,5.603,8.476,13.36,7.552,21.226l-5.799,49.364
c-1.328,11.305,5.015,22.116,15.532,26.472l41.366,17.134c10.516,4.356,22.646,1.196,29.701-7.736l30.804-39.005
c4.908-6.215,12.43-9.665,20.348-9.669c0.084,0,0.168,0,0.251,0c7.936-0.014,15.478,3.44,20.396,9.667l30.806,39.007
c7.055,8.933,19.185,12.093,29.701,7.736l41.366-17.134c10.516-4.356,16.86-15.168,15.532-26.472l-5.8-49.366
c-0.926-7.881,1.965-15.656,7.586-21.257c0.059-0.059,0.119-0.119,0.178-0.178c5.602-5.597,13.36-8.476,21.225-7.552l49.364,5.799
c11.305,1.328,22.117-5.015,26.472-15.531l17.134-41.365C514.418,326.488,511.258,314.358,502.325,307.303z M281.292,329.698
c-39.68,16.436-85.172-2.407-101.607-42.087c-16.436-39.68,2.407-85.171,42.087-101.608c39.68-16.436,85.172,2.407,101.608,42.088
C339.815,267.771,320.972,313.262,281.292,329.698z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

+1
View File
@@ -161,6 +161,7 @@ class HandlerDialog(QDialog):
self.treeWidget.expandAll()
if self.treeWidget.topLevelItemCount() > 0:
self.treeWidget.setCurrentItem(self.treeWidget.topLevelItem(0))
self.treeWidget.setFocus()
# Update checkbox states
self._update_checkbox_states()
+16
View File
@@ -102,3 +102,19 @@ SAMPLE_VOICE_TEXTS = {
"p": "Este é um exemplo da voz selecionada.",
"z": "这是所选语音的示例。",
}
COLORS = {
"BLUE": "#007dff",
"RED": "#c0392b",
"YELLOW_BACKGROUND": "rgba(255, 221, 51, 0.40)",
"GREY_BACKGROUND": "rgba(128, 128, 128, 0.15)",
"RED_BACKGROUND": "rgba(232, 78, 60, 0.15)",
# Theme palette colors
"DARK_BG": "#202326",
"DARK_BASE": "#141618",
"DARK_ALT": "#2c2f31",
"DARK_BUTTON": "#292c30",
"DARK_DISABLED": "#535353",
"LIGHT_BG": "#eff0f1",
"LIGHT_DISABLED": "#9a9999",
}
+5 -5
View File
@@ -693,8 +693,8 @@ class ConversionThread(QThread):
if 'ass' in subtitle_format:
# Generate ASS subtitle
is_centered = 'centered' in subtitle_format
is_narrow = 'narrow' in subtitle_format
is_centered = subtitle_format in ("ass_centered_wide", "ass_centered_narrow")
is_narrow = subtitle_format in ("ass_narrow", "ass_centered_narrow")
self._write_ass_subtitle(chapter_subtitle_path, chapter_subtitle_entries, is_centered, is_narrow)
else:
# Generate SRT subtitle (default)
@@ -772,9 +772,9 @@ class ConversionThread(QThread):
subtitle_path = os.path.splitext(final_out_path)[0] + f".{file_extension}"
if 'ass' in subtitle_format:
# Generate ASS subtitle with optional centering and margin
is_centered = 'centered' in subtitle_format
is_narrow = 'narrow' in subtitle_format
# Generate ASS subtitle
is_centered = subtitle_format in ("ass_centered_wide", "ass_centered_narrow")
is_narrow = subtitle_format in ("ass_narrow", "ass_centered_narrow")
self._write_ass_subtitle(subtitle_path, subtitle_entries, is_centered, is_narrow)
else:
# Generate SRT subtitle (default)
+201 -10
View File
@@ -41,6 +41,7 @@ from PyQt5.QtCore import (
QIODevice,
QSize,
QTimer,
QEvent,
)
from PyQt5.QtGui import (
QTextCursor,
@@ -73,6 +74,7 @@ from constants import (
LANGUAGE_DESCRIPTIONS,
VOICES_INTERNAL,
SUPPORTED_LANGUAGES_FOR_SUBTITLE_GENERATION,
COLORS
)
from threading import Thread
from voice_formula_gui import VoiceFormulaDialog
@@ -81,7 +83,22 @@ from voice_profiles import load_profiles
# Import ctypes for Windows-specific taskbar icon
if platform.system() == "Windows":
import ctypes
import winreg
class DarkTitleBarEventFilter(QObject):
def __init__(self, is_windows, get_dark_mode_func, set_title_bar_dark_mode_func):
super().__init__()
self.is_windows = is_windows
self.get_dark_mode = get_dark_mode_func
self.set_title_bar_dark_mode = set_title_bar_dark_mode_func
def eventFilter(self, obj, event):
if event.type() == QEvent.Show:
# Only apply to QWidget windows
if isinstance(obj, QWidget) and obj.isWindow():
if self.is_windows and self.get_dark_mode():
self.set_title_bar_dark_mode(obj, True)
return False
class ShowWarningSignalEmitter(QObject): # New class to handle signal emission
show_warning_signal = pyqtSignal(str, str)
@@ -517,11 +534,26 @@ class TextboxDialog(QDialog):
self.update_char_count()
self.text_edit.setFocus()
def migrate_subtitle_format(config):
"""Convert old subtitle_format values to new internal keys."""
old_to_new = {
"srt": "srt",
"ass (wide)": "ass_wide",
"ass (narrow)": "ass_narrow",
"ass (centered wide)": "ass_centered_wide",
"ass (centered narrow)": "ass_centered_narrow",
}
val = config.get("subtitle_format")
if val in old_to_new:
config["subtitle_format"] = old_to_new[val]
save_config(config)
class abogen(QWidget):
def __init__(self):
super().__init__()
self.config = load_config()
self.apply_theme(self.config.get("theme", "system"))
migrate_subtitle_format(self.config)
self.check_updates = self.config.get("check_updates", True)
self.save_option = self.config.get("save_option", "Save next to input file")
self.selected_output_folder = self.config.get("selected_output_folder", None)
@@ -811,7 +843,7 @@ class abogen(QWidget):
gpu_layout.addLayout(gpu_checkbox_layout)
# Settings button with icon
settings_icon_path = get_resource_path("abogen.assets", "settings.png")
settings_icon_path = get_resource_path("abogen.assets", "settings.svg")
self.settings_btn = QPushButton(self)
if settings_icon_path and os.path.exists(settings_icon_path):
self.settings_btn.setIcon(QIcon(settings_icon_path))
@@ -1470,7 +1502,7 @@ class abogen(QWidget):
# Pass separate_chapters_format setting
self.conversion_thread.separate_chapters_format = self.separate_chapters_format
# Pass subtitle format setting
self.conversion_thread.subtitle_format = self.config.get("subtitle_format", "srt")
self.conversion_thread.subtitle_format = self.config.get("subtitle_format", "ass_centered_narrow")
# Pass chapter count for EPUB or PDF files
if self.selected_file_type in ["epub", "pdf"] and hasattr(
self, "selected_chapters"
@@ -2053,10 +2085,160 @@ class abogen(QWidget):
# If dialog is rejected, cancel the conversion
self.cancel_conversion()
def apply_theme(self, theme):
from PyQt5.QtGui import QPalette, QColor
from PyQt5.QtWidgets import QStyleFactory
app = QApplication.instance()
is_windows = platform.system() == "Windows"
available_styles = [s.lower() for s in QStyleFactory.keys()]
def is_windows_dark_mode():
try:
import winreg
with winreg.OpenKey(
winreg.HKEY_CURRENT_USER,
r"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"
) as key:
value, _ = winreg.QueryValueEx(key, "AppsUseLightTheme")
return value == 0
except Exception:
return False
# --- Theme selection logic ---
def set_dark_palette():
palette = QPalette()
dark_bg = QColor(COLORS["DARK_BG"])
base_bg = QColor(COLORS["DARK_BASE"])
alt_bg = QColor(COLORS["DARK_ALT"])
button_bg = QColor(COLORS["DARK_BUTTON"])
disabled_fg = QColor(COLORS["DARK_DISABLED"])
palette.setColor(QPalette.ColorRole.Window, dark_bg)
palette.setColor(QPalette.ColorRole.WindowText, Qt.GlobalColor.white)
palette.setColor(QPalette.ColorRole.Base, base_bg)
palette.setColor(QPalette.ColorRole.AlternateBase, alt_bg)
palette.setColor(QPalette.ColorRole.ToolTipBase, dark_bg)
palette.setColor(QPalette.ColorRole.ToolTipText, Qt.GlobalColor.white)
palette.setColor(QPalette.ColorRole.Text, Qt.GlobalColor.white)
palette.setColor(QPalette.ColorRole.Button, button_bg)
palette.setColor(QPalette.ColorRole.ButtonText, Qt.GlobalColor.white)
# Disabled roles
palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.WindowText, disabled_fg)
palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, disabled_fg)
palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, disabled_fg)
palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Base, dark_bg)
palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Button, dark_bg)
app.setPalette(palette)
def set_light_palette():
palette = QPalette()
disabled_fg = QColor(COLORS["LIGHT_DISABLED"])
palette.setColor(QPalette.ColorRole.Window, QColor(COLORS["LIGHT_BG"]))
palette.setColor(QPalette.ColorRole.WindowText, Qt.GlobalColor.black)
palette.setColor(QPalette.ColorRole.Base, Qt.GlobalColor.white)
palette.setColor(QPalette.ColorRole.AlternateBase, Qt.GlobalColor.white)
palette.setColor(QPalette.ColorRole.ToolTipBase, Qt.GlobalColor.white)
palette.setColor(QPalette.ColorRole.ToolTipText, Qt.GlobalColor.black)
palette.setColor(QPalette.ColorRole.Text, Qt.GlobalColor.black)
palette.setColor(QPalette.ColorRole.Button, Qt.GlobalColor.white)
palette.setColor(QPalette.ColorRole.ButtonText, Qt.GlobalColor.black)
# Disabled roles
palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.WindowText, disabled_fg)
palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, disabled_fg)
palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, disabled_fg)
palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Base, Qt.GlobalColor.white)
palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Button, Qt.GlobalColor.white)
app.setPalette(palette)
# --- Dark title bar support for Windows ---
def set_title_bar_dark_mode(window, enable):
if is_windows:
try:
window.update()
DWMWA_USE_IMMERSIVE_DARK_MODE = 20
set_window_attribute = ctypes.windll.dwmapi.DwmSetWindowAttribute
hwnd = int(window.winId())
value = ctypes.c_int(2 if enable else 0)
set_window_attribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, ctypes.byref(value), ctypes.sizeof(value))
except Exception:
pass
# Main logic
dark_mode = theme == "dark" or (theme == "system" and is_windows and is_windows_dark_mode())
if dark_mode:
app.setStyle("Fusion")
set_dark_palette()
elif (theme == "light" or theme == "system") and is_windows:
if "windowsvista" in available_styles:
app.setStyle("windowsvista")
else:
app.setStyle("Fusion")
app.setPalette(QPalette())
elif theme == "light":
app.setStyle("Fusion")
set_light_palette()
else:
app.setStyle("Fusion")
app.setPalette(QPalette())
# Always set the title bar mode according to the current theme for all top-level widgets
for widget in app.topLevelWidgets():
set_title_bar_dark_mode(widget, dark_mode)
# Refresh all top-level widgets
style_name = app.style().objectName()
app.setStyle(style_name)
for widget in app.topLevelWidgets():
app.style().polish(widget)
widget.update()
# Remove old event filter if present, then install a new one for dark title bar on new windows
if hasattr(app, "_dark_titlebar_event_filter"):
app.removeEventFilter(app._dark_titlebar_event_filter)
delattr(app, "_dark_titlebar_event_filter")
def get_dark_mode():
return theme == "dark" or (theme == "system" and is_windows and is_windows_dark_mode())
app._dark_titlebar_event_filter = DarkTitleBarEventFilter(
is_windows, get_dark_mode, set_title_bar_dark_mode
)
app.installEventFilter(app._dark_titlebar_event_filter)
# Save config if changed
if self.config.get("theme", "system") != theme:
self.config["theme"] = theme
save_config(self.config)
def show_settings_menu(self):
"""Show a dropdown menu for settings options."""
menu = QMenu(self)
theme_menu = QMenu("Theme", self)
theme_menu.setToolTip("Choose the application theme")
theme_group = QActionGroup(self)
theme_group.setExclusive(True)
# Theme options: (internal_value, display_text)
theme_options = [
("system", "System"),
("light", "Light"),
("dark", "Dark"),
]
# Get current theme from config, default to "system"
current_theme = self.config.get("theme", "system")
for value, text in theme_options:
theme_action = QAction(text, self)
theme_action.setCheckable(True)
theme_action.setChecked(current_theme == value)
theme_action.triggered.connect(lambda checked, v=value: self.apply_theme(v))
theme_group.addAction(theme_action)
theme_menu.addAction(theme_action)
menu.addMenu(theme_menu)
# Add replace single newlines option
newline_action = QAction("Replace single newlines with spaces", self)
newline_action.setCheckable(True)
@@ -2072,19 +2254,28 @@ class abogen(QWidget):
# Add subtitle format option
subtitle_format_menu = QMenu("Subtitle format", self)
subtitle_format_menu.setToolTip("Choose the format for generated subtitles")
subtitle_format_group = QActionGroup(self)
subtitle_format_group.setExclusive(True)
subtitle_format = self.config.get("subtitle_format", "srt")
for format_option in ["srt", "ass (wide)", "ass (narrow)", "ass (centered wide)", "ass (centered narrow)"]:
format_action = QAction(f"{format_option}", self)
# Define mapping: (internal_value, display_text)
subtitle_formats = [
("srt", "SRT (standard)"),
("ass_wide", "ASS (wide)"),
("ass_narrow", "ASS (narrow)"),
("ass_centered_wide", "ASS (centered wide)"),
("ass_centered_narrow", "ASS (centered narrow)"),
]
subtitle_format = self.config.get("subtitle_format", "ass_centered_narrow")
for value, text in subtitle_formats:
format_action = QAction(text, self)
format_action.setCheckable(True)
format_action.setChecked(subtitle_format == format_option)
format_action.triggered.connect(lambda checked, fmt=format_option: self.set_subtitle_format(fmt))
format_action.setChecked(subtitle_format == value)
format_action.triggered.connect(lambda checked, fmt=value: self.set_subtitle_format(fmt))
subtitle_format_group.addAction(format_action)
subtitle_format_menu.addAction(format_action)
menu.addMenu(subtitle_format_menu)
# Add separate chapters format option
+6 -2
View File
@@ -6,11 +6,15 @@ def check():
except Exception:
return False
nvidia_keywords = ['nvidia', 'rtx', 'gtx', 'quadro', 'tesla', 'titan', 'mx']
for gpu in stats.gpus:
print(gpu.name)
if 'nvidia' in gpu.name.lower():
name = gpu.name.lower()
if any(keyword in name for keyword in nvidia_keywords):
return True
return False
if __name__ == "__main__":
stats = gpustat.new_query()
for gpu in stats.gpus:
print(gpu.name)
print(check())
+20
View File
@@ -3,6 +3,7 @@ import sys
import platform
from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import qInstallMessageHandler, QtMsgType
# Add the directory to Python path
sys.path.insert(0, os.path.join(os.path.dirname(__file__)))
@@ -24,6 +25,25 @@ if sys.stderr is None:
if platform.system() == "Darwin" and platform.processor() == "arm":
os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1"
# Custom message handler to filter out specific Qt warnings
def qt_message_handler(mode, context, message):
if "Wayland does not support QWindow::requestActivate()" in message:
return # Suppress this specific message
if "setGrabPopup called with a parent, QtWaylandClient" in message:
return
if mode == QtMsgType.QtWarningMsg:
print(f"Qt Warning: {message}")
elif mode == QtMsgType.QtCriticalMsg:
print(f"Qt Critical: {message}")
elif mode == QtMsgType.QtFatalMsg:
print(f"Qt Fatal: {message}")
elif mode == QtMsgType.QtInfoMsg:
print(f"Qt Info: {message}")
# Install the custom message handler
qInstallMessageHandler(qt_message_handler)
# Set application ID for Windows taskbar icon
if platform.system() == "Windows":
import ctypes
+49 -21
View File
@@ -24,14 +24,18 @@ from PyQt5.QtWidgets import (
QMenu,
QAction,
QComboBox,
QApplication,
)
from PyQt5.QtCore import Qt, QTimer, QPoint, QRect, QSize
from PyQt5.QtGui import QPixmap, QIcon, QColor
from PyQt5.QtGui import QPixmap, QIcon
from constants import (
VOICES_INTERNAL,
SUPPORTED_LANGUAGES_FOR_SUBTITLE_GENERATION,
LANGUAGE_DESCRIPTIONS,
COLORS,
)
import re
import platform
from utils import get_resource_path
from voice_profiles import (
load_profiles,
@@ -221,6 +225,19 @@ class VoiceMixer(QWidget):
self.slider.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)
self.slider.setFixedWidth(SLIDER_WIDTH)
# Fix slider in Windows
if platform.system() == "Windows":
appstyle = QApplication.instance().style().objectName().lower()
if appstyle != "windowsvista":
# Set custom groove color for disabled state using COLORS["GREY_BACKGROUND"]
self.slider.setStyleSheet(f"""
QSlider::groove:vertical:disabled {{
background: {COLORS.get("GREY_BACKGROUND")};
width: 4px;
border-radius: 4px;
}}
""")
# Connect controls
self.slider.valueChanged.connect(lambda val: self.spin_box.setValue(val / 100))
self.spin_box.valueChanged.connect(
@@ -271,9 +288,9 @@ class HoverLabel(QLabel):
self.delete_button = QPushButton("×", self)
self.delete_button.setFixedSize(16, 16)
self.delete_button.setStyleSheet(
"""
QPushButton {
background-color: #ff5555;
f"""
QPushButton {{
background-color: {COLORS.get("RED")};
color: white;
border-radius: 7px;
font-weight: bold;
@@ -281,11 +298,11 @@ class HoverLabel(QLabel):
border: none;
padding: 0px;
margin: 0px;
}
QPushButton:hover {
}}
QPushButton:hover {{
background-color: red;
}
"""
}}
"""
)
# Make sure the entire button is clickable, not just the text
self.delete_button.setFocusPolicy(Qt.NoFocus)
@@ -350,6 +367,9 @@ class VoiceFormulaDialog(QDialog):
profile_layout.addLayout(header_layout)
# Profile list
self.profile_list = QListWidget()
self.profile_list.setSelectionMode(QListWidget.SingleSelection)
self.profile_list.setSelectionBehavior(QListWidget.SelectRows)
self.profile_list.setStyleSheet("QListWidget::item:selected { background: palette(highlight); color: palette(highlighted-text); }")
icon = QIcon(get_resource_path("abogen.assets", "profile.png"))
if self._virtual_new_profile:
item = QListWidgetItem(icon, "New profile")
@@ -738,7 +758,7 @@ class VoiceFormulaDialog(QDialog):
percentage = weight / total * 100
# Make the voice name bold and include percentage
voice_label = HoverLabel(
f'<b><span style="color:#007dff">{name}: {percentage:.1f}%</span></b>',
f'<b><span style="color:{COLORS.get("BLUE")}">{name}: {percentage:.1f}%</span></b>',
name,
)
voice_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
@@ -953,6 +973,18 @@ class VoiceFormulaDialog(QDialog):
return
super().closeEvent(event)
def _parse_rgba_to_qcolor(self, rgba_str):
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QColor
"""Helper to convert 'rgba(R,G,B,A_float)' string to QColor."""
match = re.match(r"rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)", rgba_str)
if match:
r, g, b = int(match.group(1)), int(match.group(2)), int(match.group(3))
a_float = float(match.group(4))
a_int = int(a_float * 255)
return QColor(r, g, b, a_int)
return Qt.GlobalColor.transparent
def mark_profile_modified(self):
item = self.profile_list.currentItem()
if item and not item.text().startswith("*"):
@@ -1339,23 +1371,20 @@ class VoiceFormulaDialog(QDialog):
self.profile_list.setItemWidget(item, widget)
def update_profile_list_colors(self):
from PyQt5.QtCore import Qt
profiles = load_profiles()
for i in range(self.profile_list.count()):
item = self.profile_list.item(i)
name = item.text().lstrip("*")
if self._virtual_new_profile and name == "New profile":
color = QColor("#ffff00") # yellow
color.setAlpha(64) # Set transparency
item.setBackground(color)
color = self._parse_rgba_to_qcolor(COLORS.get("YELLOW_BACKGROUND"))
item.setData(Qt.BackgroundRole, color)
elif item.text().startswith("*"):
color = QColor("#ffff00") # yellow
color.setAlpha(64) # Set transparency
item.setBackground(color)
color = self._parse_rgba_to_qcolor(COLORS.get("YELLOW_BACKGROUND"))
item.setData(Qt.BackgroundRole, color)
else:
item.setBackground(self.profile_list.palette().base().color()) # Use default list item background
item.setData(Qt.BackgroundRole, self.profile_list.palette().base().color())
weights = profiles.get(name, {}).get("voices", [])
# Defensive: only sum if weights is a list of (voice, weight) pairs
total = 0
if isinstance(weights, list):
for entry in weights:
@@ -1366,9 +1395,8 @@ class VoiceFormulaDialog(QDialog):
):
total += entry[1]
if total == 0:
color = QColor("#ff0000") # red
color.setAlpha(64) # Set transparency
item.setBackground(color)
color = self._parse_rgba_to_qcolor(COLORS.get("RED_BACKGROUND"))
item.setData(Qt.BackgroundRole, color)
self.update_profile_save_buttons()
def preview_current_mix(self):