Loading launch.py +2 −0 Original line number Diff line number Diff line Loading @@ -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() Loading ui/fontformat_commands.py +45 −1 Original line number Diff line number Diff line Loading @@ -158,3 +158,47 @@ def ffmt_change_line_spacing_type(param_name: str, values: float, act_ffmt: Font restore_cursor = not is_global for blkitem, value in zip(blkitems, values): blkitem.setLineSpacingType(value, restore_cursor=restore_cursor) @font_formating(push_undostack=True) def handle_gradient_enabled(param_name: str, values: List[bool], act_ffmt: FontFormat, is_global: bool, blkitems: List[TextBlkItem], **kwargs): set_kwargs = global_default_set_kwargs if is_global else local_default_set_kwargs for blkitem, value in zip(blkitems, values): blkitem.fontformat.gradient_enabled = value blkitem.update() @font_formating(push_undostack=True) def handle_gradient_start_color(param_name: str, values: List[List], act_ffmt: FontFormat, is_global: bool, blkitems: List[TextBlkItem], **kwargs): set_kwargs = global_default_set_kwargs if is_global else local_default_set_kwargs for blkitem, value in zip(blkitems, values): blkitem.fontformat.gradient_start_color = value blkitem.update() @font_formating(push_undostack=True) def handle_gradient_end_color(param_name: str, values: List[List], act_ffmt: FontFormat, is_global: bool, blkitems: List[TextBlkItem], **kwargs): set_kwargs = global_default_set_kwargs if is_global else local_default_set_kwargs for blkitem, value in zip(blkitems, values): blkitem.fontformat.gradient_end_color = value blkitem.update() @font_formating(push_undostack=True) def handle_gradient_angle(param_name: str, values: List[float], act_ffmt: FontFormat, is_global: bool, blkitems: List[TextBlkItem], **kwargs): set_kwargs = global_default_set_kwargs if is_global else local_default_set_kwargs for blkitem, value in zip(blkitems, values): blkitem.fontformat.gradient_angle = value blkitem.update() @font_formating(push_undostack=True) def handle_gradient_size(param_name: str, values: List[float], act_ffmt: FontFormat, is_global: bool, blkitems: List[TextBlkItem], **kwargs): set_kwargs = global_default_set_kwargs if is_global else local_default_set_kwargs for blkitem, value in zip(blkitems, values): blkitem.fontformat.gradient_size = value blkitem.update() # Add gradient handlers to the handle_ffmt_change dictionary handle_ffmt_change = { 'gradient_enabled': handle_gradient_enabled, 'gradient_start_color': handle_gradient_start_color, 'gradient_end_color': handle_gradient_end_color, 'gradient_angle': handle_gradient_angle, 'gradient_size': handle_gradient_size } No newline at end of file ui/funcmaps.py +16 −0 Original line number Diff line number Diff line Loading @@ -2,11 +2,27 @@ from utils.io_utils import build_funcmap from utils.fontformat import FontFormat from utils.config import pcfg from utils.textblock_mask import canny_flood, connected_canny_flood, existing_mask from ui.fontformat_commands import ( handle_gradient_enabled, handle_gradient_start_color, handle_gradient_end_color, handle_gradient_angle, handle_gradient_size ) # Build base function map handle_ffmt_change = build_funcmap('ui.fontformat_commands', list(FontFormat.params().keys()), 'ffmt_change_', verbose=False) # Add gradient handlers handle_ffmt_change.update({ 'gradient_enabled': handle_gradient_enabled, 'gradient_start_color': handle_gradient_start_color, 'gradient_end_color': handle_gradient_end_color, 'gradient_angle': handle_gradient_angle, 'gradient_size': handle_gradient_size }) def get_maskseg_method(): return [canny_flood, connected_canny_flood, existing_mask][pcfg.drawpanel.rectool_method] No newline at end of file ui/text_graphical_effect.py +178 −3 Original line number Diff line number Diff line Loading @@ -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]: Loading Loading @@ -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)) ui/text_panel.py +46 −9 Original line number Diff line number Diff line Loading @@ -2,7 +2,7 @@ import copy import sys from typing import List from qtpy.QtWidgets import QLineEdit, QSizePolicy, QHBoxLayout, QVBoxLayout, QFrame, QFontComboBox, QApplication, QPushButton, QLabel from qtpy.QtWidgets import QLineEdit, QSizePolicy, QHBoxLayout, QVBoxLayout, QFrame, QFontComboBox, QApplication, QPushButton, QLabel, QGroupBox, QCheckBox, QSlider from qtpy.QtCore import Signal, Qt from qtpy.QtGui import QFocusEvent, QMouseEvent, QTextCursor, QKeyEvent Loading @@ -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 Loading Loading @@ -411,15 +411,22 @@ 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")) 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 vl0 = QVBoxLayout() Loading Loading @@ -447,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() Loading Loading @@ -549,6 +557,7 @@ class FontFormatPanel(Widget): self.formatBtnGroup.underlineBtn.setChecked(font_format.underline) self.formatBtnGroup.italicBtn.setChecked(font_format.italic) self.alignBtnGroup.setAlignment(font_format.alignment) self.familybox.blockSignals(False) # self.texteffect_panel.set_active_format(font_format) self.textadvancedfmt_panel.set_active_format(font_format) Loading Loading @@ -588,6 +597,10 @@ class FontFormatPanel(Widget): if focus_p == self or focus_p.parentWidget() == self: focus_on_fmtoptions = True if not focus_on_fmtoptions: # Store the current text block's format before switching to global if self.textblk_item is not None: # Save all format properties including gradient state self.textblk_item.fontformat = copy.deepcopy(C.active_format) self.textblk_item = None self.set_active_format(self.global_format, multi_select) if multi_select: Loading @@ -598,13 +611,37 @@ class FontFormatPanel(Widget): else: if not self.restoring_textblk: blk_fmt = textblk_item.get_fontformat() # Preserve gradient properties from the text block's format if hasattr(textblk_item.fontformat, 'gradient_enabled'): blk_fmt.gradient_enabled = textblk_item.fontformat.gradient_enabled blk_fmt.gradient_start_color = textblk_item.fontformat.gradient_start_color blk_fmt.gradient_end_color = textblk_item.fontformat.gradient_end_color blk_fmt.gradient_angle = textblk_item.fontformat.gradient_angle blk_fmt.gradient_size = textblk_item.fontformat.gradient_size self.textblk_item = textblk_item multi_size = not textblk_item.isEditing() and textblk_item.isMultiFontSize() 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 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) Loading
launch.py +2 −0 Original line number Diff line number Diff line Loading @@ -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() Loading
ui/fontformat_commands.py +45 −1 Original line number Diff line number Diff line Loading @@ -158,3 +158,47 @@ def ffmt_change_line_spacing_type(param_name: str, values: float, act_ffmt: Font restore_cursor = not is_global for blkitem, value in zip(blkitems, values): blkitem.setLineSpacingType(value, restore_cursor=restore_cursor) @font_formating(push_undostack=True) def handle_gradient_enabled(param_name: str, values: List[bool], act_ffmt: FontFormat, is_global: bool, blkitems: List[TextBlkItem], **kwargs): set_kwargs = global_default_set_kwargs if is_global else local_default_set_kwargs for blkitem, value in zip(blkitems, values): blkitem.fontformat.gradient_enabled = value blkitem.update() @font_formating(push_undostack=True) def handle_gradient_start_color(param_name: str, values: List[List], act_ffmt: FontFormat, is_global: bool, blkitems: List[TextBlkItem], **kwargs): set_kwargs = global_default_set_kwargs if is_global else local_default_set_kwargs for blkitem, value in zip(blkitems, values): blkitem.fontformat.gradient_start_color = value blkitem.update() @font_formating(push_undostack=True) def handle_gradient_end_color(param_name: str, values: List[List], act_ffmt: FontFormat, is_global: bool, blkitems: List[TextBlkItem], **kwargs): set_kwargs = global_default_set_kwargs if is_global else local_default_set_kwargs for blkitem, value in zip(blkitems, values): blkitem.fontformat.gradient_end_color = value blkitem.update() @font_formating(push_undostack=True) def handle_gradient_angle(param_name: str, values: List[float], act_ffmt: FontFormat, is_global: bool, blkitems: List[TextBlkItem], **kwargs): set_kwargs = global_default_set_kwargs if is_global else local_default_set_kwargs for blkitem, value in zip(blkitems, values): blkitem.fontformat.gradient_angle = value blkitem.update() @font_formating(push_undostack=True) def handle_gradient_size(param_name: str, values: List[float], act_ffmt: FontFormat, is_global: bool, blkitems: List[TextBlkItem], **kwargs): set_kwargs = global_default_set_kwargs if is_global else local_default_set_kwargs for blkitem, value in zip(blkitems, values): blkitem.fontformat.gradient_size = value blkitem.update() # Add gradient handlers to the handle_ffmt_change dictionary handle_ffmt_change = { 'gradient_enabled': handle_gradient_enabled, 'gradient_start_color': handle_gradient_start_color, 'gradient_end_color': handle_gradient_end_color, 'gradient_angle': handle_gradient_angle, 'gradient_size': handle_gradient_size } No newline at end of file
ui/funcmaps.py +16 −0 Original line number Diff line number Diff line Loading @@ -2,11 +2,27 @@ from utils.io_utils import build_funcmap from utils.fontformat import FontFormat from utils.config import pcfg from utils.textblock_mask import canny_flood, connected_canny_flood, existing_mask from ui.fontformat_commands import ( handle_gradient_enabled, handle_gradient_start_color, handle_gradient_end_color, handle_gradient_angle, handle_gradient_size ) # Build base function map handle_ffmt_change = build_funcmap('ui.fontformat_commands', list(FontFormat.params().keys()), 'ffmt_change_', verbose=False) # Add gradient handlers handle_ffmt_change.update({ 'gradient_enabled': handle_gradient_enabled, 'gradient_start_color': handle_gradient_start_color, 'gradient_end_color': handle_gradient_end_color, 'gradient_angle': handle_gradient_angle, 'gradient_size': handle_gradient_size }) def get_maskseg_method(): return [canny_flood, connected_canny_flood, existing_mask][pcfg.drawpanel.rectool_method] No newline at end of file
ui/text_graphical_effect.py +178 −3 Original line number Diff line number Diff line Loading @@ -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]: Loading Loading @@ -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))
ui/text_panel.py +46 −9 Original line number Diff line number Diff line Loading @@ -2,7 +2,7 @@ import copy import sys from typing import List from qtpy.QtWidgets import QLineEdit, QSizePolicy, QHBoxLayout, QVBoxLayout, QFrame, QFontComboBox, QApplication, QPushButton, QLabel from qtpy.QtWidgets import QLineEdit, QSizePolicy, QHBoxLayout, QVBoxLayout, QFrame, QFontComboBox, QApplication, QPushButton, QLabel, QGroupBox, QCheckBox, QSlider from qtpy.QtCore import Signal, Qt from qtpy.QtGui import QFocusEvent, QMouseEvent, QTextCursor, QKeyEvent Loading @@ -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 Loading Loading @@ -411,15 +411,22 @@ 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")) 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 vl0 = QVBoxLayout() Loading Loading @@ -447,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() Loading Loading @@ -549,6 +557,7 @@ class FontFormatPanel(Widget): self.formatBtnGroup.underlineBtn.setChecked(font_format.underline) self.formatBtnGroup.italicBtn.setChecked(font_format.italic) self.alignBtnGroup.setAlignment(font_format.alignment) self.familybox.blockSignals(False) # self.texteffect_panel.set_active_format(font_format) self.textadvancedfmt_panel.set_active_format(font_format) Loading Loading @@ -588,6 +597,10 @@ class FontFormatPanel(Widget): if focus_p == self or focus_p.parentWidget() == self: focus_on_fmtoptions = True if not focus_on_fmtoptions: # Store the current text block's format before switching to global if self.textblk_item is not None: # Save all format properties including gradient state self.textblk_item.fontformat = copy.deepcopy(C.active_format) self.textblk_item = None self.set_active_format(self.global_format, multi_select) if multi_select: Loading @@ -598,13 +611,37 @@ class FontFormatPanel(Widget): else: if not self.restoring_textblk: blk_fmt = textblk_item.get_fontformat() # Preserve gradient properties from the text block's format if hasattr(textblk_item.fontformat, 'gradient_enabled'): blk_fmt.gradient_enabled = textblk_item.fontformat.gradient_enabled blk_fmt.gradient_start_color = textblk_item.fontformat.gradient_start_color blk_fmt.gradient_end_color = textblk_item.fontformat.gradient_end_color blk_fmt.gradient_angle = textblk_item.fontformat.gradient_angle blk_fmt.gradient_size = textblk_item.fontformat.gradient_size self.textblk_item = textblk_item multi_size = not textblk_item.isEditing() and textblk_item.isMultiFontSize() 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 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)