Commit 9a8be508 authored by Alex Op's avatar Alex Op
Browse files

Move gradient to a popup panel, fix icon and title not displaying on windows

parent df54c80a
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -220,6 +220,8 @@ def main():
    if args.headless:
        app_args = sys.argv + ['-platform', 'offscreen']
    app = QApplication(app_args)
    app.setApplicationName('BalloonsTranslator')
    app.setApplicationVersion(VERSION)

    if not args.headless:
        ps = QGuiApplication.primaryScreen()
+178 −3
Original line number Diff line number Diff line
@@ -2,15 +2,15 @@ from typing import Union, Tuple, Callable

import cv2
import numpy as np
from qtpy.QtCore import Signal, Qt, QPoint
from qtpy.QtGui import QColor, QShowEvent, QPixmap, QImage, QPainter, QFontMetricsF
from qtpy.QtCore import Signal, Qt, QPoint, QPointF
from qtpy.QtGui import QColor, QShowEvent, QPixmap, QImage, QPainter, QFontMetricsF, QLinearGradient, QPainterPath
from qtpy.QtWidgets import QHBoxLayout, QVBoxLayout, QGridLayout, QScrollArea, QGroupBox, QPushButton, QLabel, QDialog

from utils import shared as C
from .misc import pixmap2ndarray, ndarray2pixmap
from utils.fontformat import FontFormat, pt2px
from .custom_widget import Widget, ColorPickerLabel, PaintQSlider

import math

def apply_shadow_effect(img: Union[QPixmap, QImage, np.ndarray], color: QColor, strength=1.0, radius=21) -> Tuple[
    QPixmap, np.ndarray, np.ndarray]:
@@ -226,3 +226,178 @@ class TextEffectPanelDeprecated(QDialog):
        self.shadow_color_picker.setPickerColor(self.fontfmt.shadow_color)
        self.shadow_radius_slider.setValue(int(self.fontfmt.shadow_radius * 100))
        self.shadow_strength_slider.setValue(int(self.fontfmt.shadow_strength * 100))


class GradientPreviewPanel(QDialog):
    apply = Signal()
    gradient_changed = Signal()
    
    def __init__(self, update_text_style_label: Callable, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)

        self.setModal(True)
        self.update_text_style_label = update_text_style_label
        self.fontfmt: FontFormat = None
        self.fontfmt = FontFormat()
        self.active_fontfmt = FontFormat()

        # Preview label setup
        self.preview_label = QLabel(self)
        self.preview_label.setContentsMargins(0, 0, 0, 0)
        font = self.preview_label.font()
        font.setPointSizeF(24)
        self.preview_label.setFont(font)
        self.preview_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.preview_text = self.tr("Gradient")
        fm = QFontMetricsF(font)
        br = fm.boundingRect(self.preview_text)
        br_w, br_h = br.width(), br.height()
        self.preview_pixmap = QPixmap(int(br_w + br_h * 2), int(br_h * 3))
        self.preview_origin = QPointF(int(br_h), int(1.75 * br_h))

        self.preview_label.setFixedHeight(int(br_h * 2))

        # Enable gradient checkbox
        self.gradient_enable = QGroupBox(self.tr("Enable Gradient"))
        self.gradient_enable.setCheckable(True)
        self.gradient_enable.toggled.connect(self.on_gradient_enabled_changed)

        # Color pickers
        start_color_layout = QHBoxLayout()
        start_color_layout.addWidget(QLabel(self.tr("Start Color:")))
        self.gradient_start_picker = ColorPickerLabel(self)
        self.gradient_start_picker.colorChanged.connect(self.on_gradient_start_color_changed)
        start_color_layout.addWidget(self.gradient_start_picker)

        end_color_layout = QHBoxLayout()
        end_color_layout.addWidget(QLabel(self.tr("End Color:")))
        self.gradient_end_picker = ColorPickerLabel(self)
        self.gradient_end_picker.colorChanged.connect(self.on_gradient_end_color_changed)
        end_color_layout.addWidget(self.gradient_end_picker)

        # Angle slider
        self.angle_slider = PaintQSlider(self.tr("Angle"))
        self.angle_slider.setRange(0, 359)
        self.angle_slider.valueChanged.connect(self.on_gradient_angle_changed)

        # Size slider
        self.size_slider = PaintQSlider(self.tr("Size"))
        self.size_slider.setRange(50, 200)
        self.size_slider.valueChanged.connect(self.on_gradient_size_changed)

        # Buttons
        self.apply_btn = QPushButton(self.tr('Apply'))
        self.apply_btn.clicked.connect(self.on_apply_clicked)
        self.cancel_btn = QPushButton(self.tr('Cancel'))
        self.cancel_btn.clicked.connect(self.on_cancel_clicked)

        # Layout
        gradient_layout = QVBoxLayout()
        gradient_layout.addLayout(start_color_layout)
        gradient_layout.addLayout(end_color_layout)
        gradient_layout.addWidget(self.angle_slider)
        gradient_layout.addWidget(self.size_slider)
        self.gradient_enable.setLayout(gradient_layout)

        button_layout = QHBoxLayout()
        button_layout.addWidget(self.apply_btn)
        button_layout.addWidget(self.cancel_btn)

        main_layout = QVBoxLayout(self)
        main_layout.addWidget(self.preview_label)
        main_layout.addWidget(self.gradient_enable)
        main_layout.addLayout(button_layout)

        self.setMaximumHeight(C.TEXTEFFECT_MAXHEIGHT)
        self.updatePreviewPixmap()

    def on_gradient_enabled_changed(self, enabled):
        self.fontfmt.gradient_enabled = enabled
        self.updatePreviewPixmap()

    def on_gradient_start_color_changed(self, is_valid):
        if is_valid:
            color = self.gradient_start_picker.color
            self.fontfmt.gradient_start_color = list(color.getRgb()[:3])  # Convert to list and take only RGB
            self.updatePreviewPixmap()

    def on_gradient_end_color_changed(self, is_valid):
        if is_valid:
            color = self.gradient_end_picker.color
            self.fontfmt.gradient_end_color = list(color.getRgb()[:3])  # Convert to list and take only RGB
            self.updatePreviewPixmap()

    def on_gradient_angle_changed(self, value):
        self.fontfmt.gradient_angle = float(value)
        self.updatePreviewPixmap()

    def on_gradient_size_changed(self, value):
        self.fontfmt.gradient_size = value / 100
        self.updatePreviewPixmap()

    def updatePreviewPixmap(self):
        if not self.isVisible():
            return

        self.preview_pixmap.fill(Qt.GlobalColor.transparent)
        painter = QPainter(self.preview_pixmap)
        painter.setRenderHint(QPainter.RenderHint.TextAntialiasing)
        painter.setFont(self.preview_label.font())

        if self.fontfmt.gradient_enabled:
            # Create gradient
            gradient = QLinearGradient()
            angle = self.fontfmt.gradient_angle
            rad = math.radians(angle)
            dx = math.cos(rad)
            dy = math.sin(rad)
            
            rect = self.preview_pixmap.rect()
            center = rect.center()
            radius = max(rect.width(), rect.height()) * self.fontfmt.gradient_size
            gradient.setStart(center.x() - dx * radius, center.y() - dy * radius)
            gradient.setFinalStop(center.x() + dx * radius, center.y() + dy * radius)
            
            start_color = QColor(*self.fontfmt.gradient_start_color)
            end_color = QColor(*self.fontfmt.gradient_end_color)
            gradient.setColorAt(0, start_color)
            gradient.setColorAt(1, end_color)
            
            painter.setBrush(gradient)
            painter.setPen(Qt.PenStyle.NoPen)
            
            # Draw text with gradient
            path = QPainterPath()
            path.addText(self.preview_origin, self.preview_label.font(), self.preview_text)
            painter.drawPath(path)
        else:
            painter.drawText(self.preview_origin, self.preview_text)
        
        painter.end()
        self.preview_label.setPixmap(self.preview_pixmap)

    def on_apply_clicked(self):
        self.active_fontfmt.gradient_enabled = self.fontfmt.gradient_enabled
        self.active_fontfmt.gradient_start_color = self.fontfmt.gradient_start_color
        self.active_fontfmt.gradient_end_color = self.fontfmt.gradient_end_color
        self.active_fontfmt.gradient_angle = self.fontfmt.gradient_angle
        self.active_fontfmt.gradient_size = self.fontfmt.gradient_size
        
        self.gradient_changed.emit()
        self.update_text_style_label()
        self.apply.emit()
        self.hide()

    def on_cancel_clicked(self):
        self.hide()

    def showEvent(self, e: QShowEvent) -> None:
        self.updatePreviewPixmap()
        return super().showEvent(e)

    def updatePanels(self):
        self.gradient_enable.setChecked(self.fontfmt.gradient_enabled)
        self.gradient_start_picker.setPickerColor(self.fontfmt.gradient_start_color)
        self.gradient_end_picker.setPickerColor(self.fontfmt.gradient_end_color)
        self.angle_slider.setValue(int(self.fontfmt.gradient_angle))
        self.size_slider.setValue(int(self.fontfmt.gradient_size * 100))
+30 −102
Original line number Diff line number Diff line
@@ -11,7 +11,7 @@ from utils import config as C
from utils.fontformat import FontFormat, px2pt, LineSpacingType
from .custom_widget import Widget, ColorPickerLabel, ClickableLabel, CheckableLabel, TextCheckerLabel, AlignmentChecker, QFontChecker, SizeComboBox
from .textitem import TextBlkItem
from .text_graphical_effect import TextEffectPanelDeprecated
from .text_graphical_effect import TextEffectPanelDeprecated, GradientPreviewPanel
from .text_advanced_format import TextAdvancedFormatPanel
from .text_style_presets import TextStylePresetPanel
from . import funcmaps as FM
@@ -411,68 +411,21 @@ class FontFormatPanel(Widget):
        )
        self.textadvancedfmt_panel.param_changed.connect(self.on_param_changed)

        self.effectBtn = ClickableLabel(self.tr("Effect"), self)
        self.effectBtn.clicked.connect(self.on_effectbtn_clicked)
        self.effect_panel = TextEffectPanelDeprecated(update_text_style_label=self.update_text_style_label)
        self.effect_panel.hide()
        self.shadowBtn = ClickableLabel(self.tr("Shadow"), self)
        self.shadowBtn.clicked.connect(self.on_shadow_btn_clicked)
        self.gradientBtn = ClickableLabel(self.tr("Gradient"), self)
        self.gradientBtn.clicked.connect(self.on_gradient_btn_clicked)
        
        self.foldTextBtn = CheckableLabel(self.tr("Unfold"), self.tr("Fold"), False)
        self.sourceBtn = TextCheckerLabel(self.tr("Source"))
        self.transBtn = TextCheckerLabel(self.tr("Translation"))
        
        # Add gradient controls
        self.gradientGroup = QGroupBox(self.tr("Gradient"), parent=self)
        gradientLayout = QVBoxLayout(self.gradientGroup)
        gradientLayout.setContentsMargins(10, 10, 10, 10)
        gradientLayout.setSpacing(5)
        
        # Enable gradient checkbox
        self.gradientEnableBox = QCheckBox(self.tr("Enable Gradient"), parent=self.gradientGroup)
        self.gradientEnableBox.stateChanged.connect(lambda state: self.on_param_changed('gradient_enabled', state == Qt.CheckState.Checked.value))
        gradientLayout.addWidget(self.gradientEnableBox)

        # Start color picker
        startColorLayout = QHBoxLayout()
        startColorLayout.addWidget(QLabel(self.tr("Start Color:"), parent=self.gradientGroup))
        self.gradientStartColorPicker = ColorPickerLabel(parent=self.gradientGroup)
        self.gradientStartColorPicker.colorChanged.connect(self.onGradientStartColorChanged)
        self.gradientStartColorPicker.changingColor.connect(self.changingColor)
        startColorLayout.addWidget(self.gradientStartColorPicker)
        gradientLayout.addLayout(startColorLayout)

        # End color picker
        endColorLayout = QHBoxLayout()
        endColorLayout.addWidget(QLabel(self.tr("End Color:"), parent=self.gradientGroup))
        self.gradientEndColorPicker = ColorPickerLabel(parent=self.gradientGroup)
        self.gradientEndColorPicker.colorChanged.connect(self.onGradientEndColorChanged)
        self.gradientEndColorPicker.changingColor.connect(self.changingColor)
        endColorLayout.addWidget(self.gradientEndColorPicker)
        gradientLayout.addLayout(endColorLayout)

        # Angle slider
        angleLayout = QHBoxLayout()
        angleLayout.addWidget(QLabel(self.tr("Angle:"), parent=self.gradientGroup))
        self.gradientAngleSlider = QSlider(Qt.Orientation.Horizontal, parent=self.gradientGroup)
        self.gradientAngleSlider.setRange(0, 359)
        self.gradientAngleSlider.valueChanged.connect(self.onGradientAngleChanged)
        angleLayout.addWidget(self.gradientAngleSlider)
        self.gradientAngleLabel = QLabel("", parent=self.gradientGroup)
        self.gradientAngleLabel.setMinimumWidth(30)
        angleLayout.addWidget(self.gradientAngleLabel)
        gradientLayout.addLayout(angleLayout)

        # Add gradient size control
        sizeLayout = QHBoxLayout()
        sizeLayout.addWidget(QLabel(self.tr("Size:"), parent=self.gradientGroup))
        self.gradientSizeSlider = QSlider(Qt.Orientation.Horizontal, parent=self.gradientGroup)
        self.gradientSizeSlider.setRange(50, 200)  # 0.5x to 2.0x
        self.gradientSizeSlider.setValue(100)  # Default 1.0x
        self.gradientSizeSlider.valueChanged.connect(self.onGradientSizeChanged)
        sizeLayout.addWidget(self.gradientSizeSlider)
        self.gradientSizeLabel = QLabel("1.0x", parent=self.gradientGroup)
        self.gradientSizeLabel.setMinimumWidth(40)
        sizeLayout.addWidget(self.gradientSizeLabel)
        gradientLayout.addLayout(sizeLayout)
        self.effect_panel = TextEffectPanelDeprecated(update_text_style_label=self.update_text_style_label)
        self.effect_panel.hide()
        
        self.gradient_panel = GradientPreviewPanel(update_text_style_label=self.update_text_style_label)
        self.gradient_panel.gradient_changed.connect(self.on_gradient_changed)
        self.gradient_panel.hide()

        FONTFORMAT_SPACING = 6

@@ -501,7 +454,8 @@ class FontFormatPanel(Widget):
        hl3.setAlignment(Qt.AlignmentFlag.AlignCenter)
        hl3.addLayout(stroke_hlayout)
        hl3.addLayout(lettersp_hlayout)
        hl3.addWidget(self.effectBtn)
        hl3.addWidget(self.shadowBtn)
        hl3.addWidget(self.gradientBtn)
        hl3.setContentsMargins(3, 0, 3, 0)
        hl3.setSpacing(13)
        hl4 = QHBoxLayout()
@@ -520,7 +474,6 @@ class FontFormatPanel(Widget):
        self.vlayout.addLayout(hl2)
        self.vlayout.addLayout(hl3)
        self.vlayout.addLayout(hl4)
        self.vlayout.addWidget(self.gradientGroup)
        self.vlayout.setContentsMargins(7, 0, 7, 0)
        self.vlayout.setSpacing(0)

@@ -605,15 +558,6 @@ class FontFormatPanel(Widget):
        self.formatBtnGroup.italicBtn.setChecked(font_format.italic)
        self.alignBtnGroup.setAlignment(font_format.alignment)
        
        # Initialize gradient controls
        self.gradientEnableBox.setChecked(font_format.gradient_enabled)
        self.gradientStartColorPicker.setPickerColor(font_format.gradient_start_color)
        self.gradientEndColorPicker.setPickerColor(font_format.gradient_end_color)
        self.gradientAngleSlider.setValue(int(font_format.gradient_angle))
        self.gradientAngleLabel.setText(f"{int(font_format.gradient_angle)}°")
        self.gradientSizeSlider.setValue(int(font_format.gradient_size * 100))
        self.gradientSizeLabel.setText(f"{font_format.gradient_size:.1f}x")
        
        self.familybox.blockSignals(False)
        # self.texteffect_panel.set_active_format(font_format)
        self.textadvancedfmt_panel.set_active_format(font_format)
@@ -679,41 +623,25 @@ class FontFormatPanel(Widget):
                self.set_active_format(blk_fmt, multi_size)
                self.textstyle_panel.setTitle(f'TextBlock #{textblk_item.idx}')

    def on_effectbtn_clicked(self):
    def on_shadow_btn_clicked(self):
        self.effect_panel.active_fontfmt = C.active_format
        self.effect_panel.fontfmt = copy.deepcopy(C.active_format)
        self.effect_panel.updatePanels()
        self.effect_panel.show()

    def onGradientAngleChanged(self, value):
        self.gradientAngleLabel.setText(f"{value}°")
        self.on_param_changed('gradient_angle', float(value))

    def onGradientStartColorChanged(self, is_valid=True):
        self.focusOnColorDialog = False
        if is_valid:
            rgb = self.gradientStartColorPicker.rgb()
            self.on_param_changed('gradient_start_color', rgb)

    def onGradientEndColorChanged(self, is_valid=True):
        self.focusOnColorDialog = False
        if is_valid:
            rgb = self.gradientEndColorPicker.rgb()
            self.on_param_changed('gradient_end_color', rgb)

    def onGradientSizeChanged(self, value):
        self.gradientSizeLabel.setText(f"{value/100:.1f}x")
        self.on_param_changed('gradient_size', value/100)

    def updateGradientControls(self, fontfmt: FontFormat):
        self.gradientEnableBox.setChecked(fontfmt.gradient_enabled)
        self.gradientStartColorPicker.setRGB(*fontfmt.gradient_start_color)
        self.gradientEndColorPicker.setRGB(*fontfmt.gradient_end_color)
        self.gradientAngleSlider.setValue(int(fontfmt.gradient_angle))
        self.gradientAngleLabel.setText(f"{int(fontfmt.gradient_angle)}°")
        self.gradientSizeSlider.setValue(int(fontfmt.gradient_size * 100))
        self.gradientSizeLabel.setText(f"{fontfmt.gradient_size:.1f}x")

    def updateFontFormatPanel(self, fontfmt: FontFormat):
        self.updateGradientControls(fontfmt)
        self.update_text_style_label()
    def on_gradient_btn_clicked(self):
        self.gradient_panel.active_fontfmt = C.active_format
        self.gradient_panel.fontfmt = copy.deepcopy(C.active_format)
        self.gradient_panel.updatePanels()
        self.gradient_panel.show()

    def on_gradient_changed(self):
        """Handle gradient changes from the preview panel"""
        start_color = self.gradient_panel.gradient_start_picker.color.getRgb()[:3]
        end_color = self.gradient_panel.gradient_end_picker.color.getRgb()[:3]
        
        self.on_param_changed('gradient_enabled', self.gradient_panel.fontfmt.gradient_enabled)
        self.on_param_changed('gradient_start_color', start_color)
        self.on_param_changed('gradient_end_color', end_color)
        self.on_param_changed('gradient_angle', self.gradient_panel.fontfmt.gradient_angle)
        self.on_param_changed('gradient_size', self.gradient_panel.fontfmt.gradient_size)
+1 −1
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@ import os.path as osp
import json
import sys

ICON_PATH = 'icons/[ICONNAME]'
ICON_PATH = 'icons/icon.icns'

PROGRAM_PATH = osp.abspath(osp.dirname(osp.dirname(__file__)))
LOGGING_PATH = osp.join(PROGRAM_PATH, 'logs')