Added icons, imrpoved voice mixer gui and documentation

This commit is contained in:
Deniz Şafak
2025-04-30 16:13:34 +03:00
parent 854ad1adea
commit 68dbdf123d
6 changed files with 32 additions and 17 deletions
+1
View File
@@ -163,6 +163,7 @@ Feel free to explore the code and make any changes you like.
- Abogen uses [Kokoro](https://github.com/hexgrad/kokoro) for its high-quality, natural-sounding text-to-speech synthesis. Huge thanks to the Kokoro team for making this possible. - Abogen uses [Kokoro](https://github.com/hexgrad/kokoro) for its high-quality, natural-sounding text-to-speech synthesis. Huge thanks to the Kokoro team for making this possible.
- Thanks to [@wojiushixiaobai](https://github.com/wojiushixiaobai) for [Embedded Python](https://github.com/wojiushixiaobai/Python-Embed-Win64) packages. These modified packages include pip pre-installed, enabling Abogen to function as a standalone application without requiring users to separately install Python in Windows. - Thanks to [@wojiushixiaobai](https://github.com/wojiushixiaobai) for [Embedded Python](https://github.com/wojiushixiaobai/Python-Embed-Win64) packages. These modified packages include pip pre-installed, enabling Abogen to function as a standalone application without requiring users to separately install Python in Windows.
- Special thanks to the [PyQt](https://www.riverbankcomputing.com/software/pyqt/) team for providing the cross-platform GUI toolkit that powers Abogen's interface. - Special thanks to the [PyQt](https://www.riverbankcomputing.com/software/pyqt/) team for providing the cross-platform GUI toolkit that powers Abogen's interface.
- [Female](https://icons8.com/icon/uI49hxbpxTkp/female), [Male](https://icons8.com/icon/12351/male) and [Mix](https://icons8.com/icon/21700/adjust) icons by [Icons8](https://icons8.com/).
## `License` ## `License`
This project is available under the MIT License - see the [LICENSE](https://github.com/denizsafak/abogen/blob/main/LICENSE) file for details. This project is available under the MIT License - see the [LICENSE](https://github.com/denizsafak/abogen/blob/main/LICENSE) file for details.
Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

+6 -1
View File
@@ -574,7 +574,8 @@ class abogen(QWidget):
# Voice formula button # Voice formula button
self.btn_voice_formula_mixer = QPushButton(self) self.btn_voice_formula_mixer = QPushButton(self)
self.btn_voice_formula_mixer.setText("🛠") # TODO add voice formula icon mixer_icon_path = get_resource_path("abogen.assets", "voice_mixer.png")
self.btn_voice_formula_mixer.setIcon(QIcon(mixer_icon_path))
self.btn_voice_formula_mixer.setToolTip("Mix and match voices") self.btn_voice_formula_mixer.setToolTip("Mix and match voices")
self.btn_voice_formula_mixer.setFixedSize(40, 36) self.btn_voice_formula_mixer.setFixedSize(40, 36)
self.btn_voice_formula_mixer.setStyleSheet("QPushButton { padding: 6px 12px; }") self.btn_voice_formula_mixer.setStyleSheet("QPushButton { padding: 6px 12px; }")
@@ -1318,6 +1319,7 @@ class abogen(QWidget):
self.btn_preview.setEnabled(False) self.btn_preview.setEnabled(False)
self.btn_preview.setToolTip("Loading...") self.btn_preview.setToolTip("Loading...")
self.voice_combo.setEnabled(False) self.voice_combo.setEnabled(False)
self.btn_voice_formula_mixer.setEnabled(False) # Disable mixer button
self.btn_start.setEnabled(False) # Disable start button during preview self.btn_start.setEnabled(False) # Disable start button during preview
# start loading animation # start loading animation
self.loading_movie.start() self.loading_movie.start()
@@ -1339,6 +1341,7 @@ class abogen(QWidget):
self.btn_preview.setEnabled(True) self.btn_preview.setEnabled(True)
self.btn_preview.setToolTip("Preview selected voice") self.btn_preview.setToolTip("Preview selected voice")
self.voice_combo.setEnabled(True) self.voice_combo.setEnabled(True)
self.btn_voice_formula_mixer.setEnabled(True) # Re-enable mixer button
self.btn_start.setEnabled(True) # Re-enable start button on error self.btn_start.setEnabled(True) # Re-enable start button on error
return return
@@ -1365,6 +1368,7 @@ class abogen(QWidget):
self.btn_preview.setEnabled(True) self.btn_preview.setEnabled(True)
self.btn_preview.setToolTip("Preview selected voice") self.btn_preview.setToolTip("Preview selected voice")
self.voice_combo.setEnabled(True) self.voice_combo.setEnabled(True)
self.btn_voice_formula_mixer.setEnabled(True) # Re-enable mixer button
self.btn_start.setEnabled(True) self.btn_start.setEnabled(True)
return return
# stop loading animation, switch to stop icon # stop loading animation, switch to stop icon
@@ -1414,6 +1418,7 @@ class abogen(QWidget):
self.btn_preview.setToolTip("Preview selected voice") self.btn_preview.setToolTip("Preview selected voice")
self.btn_preview.setEnabled(True) self.btn_preview.setEnabled(True)
self.voice_combo.setEnabled(True) self.voice_combo.setEnabled(True)
self.btn_voice_formula_mixer.setEnabled(True) # Re-enable mixer button
self.btn_start.setEnabled(True) self.btn_start.setEnabled(True)
def _preview_error(self, msg): def _preview_error(self, msg):
+25 -16
View File
@@ -16,10 +16,12 @@ from PyQt5.QtWidgets import (
QStyle, QStyle,
) )
from PyQt5.QtCore import Qt, QTimer, QPoint, QRect, QSize from PyQt5.QtCore import Qt, QTimer, QPoint, QRect, QSize
from PyQt5.QtGui import QPixmap
from constants import VOICES_INTERNAL, FLAGS from constants import VOICES_INTERNAL, FLAGS
from utils import get_resource_path
# Constants # Constants
VOICE_MIXER_WIDTH = 160 VOICE_MIXER_WIDTH = 120
SLIDER_WIDTH = 32 SLIDER_WIDTH = 32
MIN_WINDOW_WIDTH = 600 MIN_WINDOW_WIDTH = 600
MIN_WINDOW_HEIGHT = 400 MIN_WINDOW_HEIGHT = 400
@@ -119,6 +121,7 @@ class VoiceMixer(QWidget):
): ):
super().__init__() super().__init__()
self.voice_name = voice_name self.voice_name = voice_name
#self.setFixedWidth(VOICE_MIXER_WIDTH)
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
# TODO Set CSS for rounded corners # TODO Set CSS for rounded corners
@@ -133,17 +136,18 @@ class VoiceMixer(QWidget):
self.checkbox.stateChanged.connect(self.toggle_inputs) self.checkbox.stateChanged.connect(self.toggle_inputs)
layout.addWidget(self.checkbox, alignment=Qt.AlignCenter) layout.addWidget(self.checkbox, alignment=Qt.AlignCenter)
# Voice name label # Voice name label with gender icon
voice_gender = ( is_female = self.voice_name in VOICES_INTERNAL and self.voice_name[1] == "f"
FEMALE gender_icon_path = get_resource_path("abogen.assets", "female.png" if is_female else "male.png")
if self.voice_name in VOICES_INTERNAL and self.voice_name[1] == "f"
else MALE
)
name = voice_name[3:].capitalize() name = voice_name[3:].capitalize()
name_layout = QHBoxLayout() name_layout = QHBoxLayout()
name_layout.addWidget( # Gender icon
QLabel(f"{language_icon} {voice_gender} {name}"), alignment=Qt.AlignCenter pixmap = QPixmap(gender_icon_path)
) if not pixmap.isNull():
gender_label = QLabel()
gender_label.setPixmap(pixmap.scaled(16, 16, Qt.KeepAspectRatio, Qt.SmoothTransformation))
name_layout.addWidget(gender_label)
name_layout.addWidget(QLabel(f"{language_icon} {name}"), alignment=Qt.AlignCenter)
layout.addLayout(name_layout) layout.addLayout(name_layout)
# Spinbox and slider # Spinbox and slider
@@ -211,26 +215,30 @@ class HoverLabel(QLabel):
self.delete_button.setStyleSheet( self.delete_button.setStyleSheet(
""" """
QPushButton { QPushButton {
background-color: red; background-color: #ff5555;
color: white; color: white;
border-radius: 8px; border-radius: 7px;
font-weight: bold; font-weight: bold;
font-size: 12px; font-size: 12px;
border: none; border: none;
padding: 0px; padding: 0px;
text-align: center; margin: 0px;
} }
QPushButton:hover { QPushButton:hover {
background-color: #ff5555; background-color: red;
} }
""" """
) )
# Make sure the entire button is clickable, not just the text
self.delete_button.setFocusPolicy(Qt.NoFocus)
self.delete_button.setAttribute(Qt.WA_TransparentForMouseEvents, False)
self.delete_button.setCursor(Qt.PointingHandCursor) self.delete_button.setCursor(Qt.PointingHandCursor)
self.delete_button.hide() self.delete_button.hide()
def resizeEvent(self, event): def resizeEvent(self, event):
super().resizeEvent(event) super().resizeEvent(event)
self.delete_button.move(self.width() - 16, 0) # Position the button in the top-right corner with a small margin
self.delete_button.move(self.width() - 16, + 0)
def enterEvent(self, event): def enterEvent(self, event):
self.delete_button.show() self.delete_button.show()
@@ -398,7 +406,8 @@ class VoiceFormulaDialog(QDialog):
# Add voice labels # Add voice labels
for name, weight in selected: for name, weight in selected:
percentage = weight / total * 100 percentage = weight / total * 100
voice_label = HoverLabel(f"{name}: {percentage:.1f}%", name) # Make the voice name bold and include percentage
voice_label = HoverLabel(f"<b>{name}: {percentage:.1f}%</b>", name)
voice_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) voice_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
voice_label.delete_button.clicked.connect( voice_label.delete_button.clicked.connect(
lambda _, vn=name: self.disable_voice_by_name(vn) lambda _, vn=name: self.disable_voice_by_name(vn)