feat: 자막 배경 여백 · 위치 조정 모드 · CPU 추천 · 마이크 입력
Some checks failed
Windows / verify (push) Has been cancelled
Some checks failed
Windows / verify (push) Has been cancelled
요청 네 가지를 넣는다. 1) 자막 배경 여백(padding) 배경 상자를 창 전체가 아니라 **글자를 감싸는 크기**로 그리도록 바꿨다. 창 전체를 칠하던 동안에는 여백을 키워도 글자만 안으로 밀릴 뿐 배경 크기는 그대로여서, 설정 항목을 만들어도 의미가 없었다. 2) 평소 잠금 + '자막 위치 조정' locked 기본값을 True 로 바꾼다. 게임 중에 자막을 실수로 끌고 다니는 사고를 막는 쪽이 기본이어야 한다. 자막 화면의 버튼으로 조정 모드를 열면 그때만 이동·네 모서리 크기 조절·휠이 열린다. 조정 중에는 클릭 통과를 강제로 끈다 — 켜둔 채로는 마우스가 창을 지나가 잡을 수 없다. 크기를 바꾸면 글자 크기가 높이 비율을 그대로 따라간다. 상자만 커지고 글자가 그대로면 여백만 늘어나 '자막을 키우는' 조작이 되지 않는다. 조정 모드는 저장하지 않는다. 다음 실행은 다시 잠긴 상태로 시작한다. 3) GPU 가 없을 때 CPU 추천 티어 표의 recommended 는 GPU 가 넉넉할 때를 전제한 값이라, 그대로 쓰면 GPU 없는 사람에게 10GB 짜리를 권하게 된다. 환경을 보고 고르는 recommended_tier() 를 만들고 배지도 그 결과로 붙인다. GPU 가 없으면 CTranslate2 계열만 후보에 두고 가장 가벼운 쪽을 권한다 — LLM 계열은 CPU 에서 한 문장에 수십 초가 걸려 실시간 자막이 되지 않는다. '추천대로 맞추기' 버튼과, 실제로는 CPU 로 도는데 화면은 GPU 라고 표시하던 것도 같이 고친다. nvidia-smi 실패 문구가 명령줄을 통째로 토해내던 것도 사람이 읽을 문장으로 바꿨다. 4) 마이크 등 입력 장치 선택 InputDeviceCapture 를 추가하고 홈 화면에 목록 전환 버튼을 둔다. 두 목록을 한 번에 섞지 않는다 — 다 합치면 정작 찾으려던 게임이 라인입력들 사이에 파묻힌다. 버튼 글자는 '지금 상태'가 아니라 '누르면 가는 곳'을 적는다. 고른 쪽은 설정에 기억한다.
This commit is contained in:
149
tests/test_subtitle_box.py
Normal file
149
tests/test_subtitle_box.py
Normal file
@@ -0,0 +1,149 @@
|
||||
"""자막 배경 상자(padding)와 위치 조정 모드."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
pytest.importorskip("PySide6")
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
|
||||
from PySide6.QtCore import QPoint, QRect # noqa: E402
|
||||
from PySide6.QtWidgets import QApplication # noqa: E402
|
||||
|
||||
from livesub.config import OverlayConfig, SubtitleStyle # noqa: E402
|
||||
from livesub.ui.overlay import SubtitleOverlay, _content_box # noqa: E402
|
||||
|
||||
WINDOW = QRect(0, 0, 1000, 400)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def qapp():
|
||||
app = QApplication.instance() or QApplication([])
|
||||
yield app
|
||||
|
||||
|
||||
# --- 배경 상자 -------------------------------------------------------------
|
||||
|
||||
|
||||
def test_배경은_글자를_감싼다():
|
||||
"""창 전체가 아니라 글자 크기에 맞춰야 여백이 곧 배경 크기가 된다."""
|
||||
box = _content_box(WINDOW, text_w=300, text_h=60, pad=18, align="center")
|
||||
assert box.width() == 300 + 18 * 2
|
||||
assert box.height() == 60 + 18 * 2
|
||||
assert box.width() < WINDOW.width()
|
||||
|
||||
|
||||
def test_여백을_키우면_배경이_커진다():
|
||||
small = _content_box(WINDOW, 300, 60, pad=8, align="center")
|
||||
large = _content_box(WINDOW, 300, 60, pad=40, align="center")
|
||||
assert large.width() - small.width() == (40 - 8) * 2
|
||||
assert large.height() - small.height() == (40 - 8) * 2
|
||||
|
||||
|
||||
def test_배경은_창_밖으로_넘지_않는다():
|
||||
box = _content_box(WINDOW, text_w=5000, text_h=5000, pad=40, align="center")
|
||||
assert box.width() == WINDOW.width()
|
||||
assert box.height() == WINDOW.height()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("align", "expected_left"),
|
||||
[("left", 0), ("center", (1000 - 336) // 2), ("right", 1000 - 336)],
|
||||
)
|
||||
def test_정렬에_따라_상자가_붙는_쪽이_달라진다(align, expected_left):
|
||||
box = _content_box(WINDOW, text_w=300, text_h=60, pad=18, align=align)
|
||||
assert box.width() == 336
|
||||
assert abs(box.x() - expected_left) <= 1
|
||||
|
||||
|
||||
def test_기본_여백이_설정에_있다():
|
||||
assert SubtitleStyle().padding > 0
|
||||
|
||||
|
||||
# --- 위치 조정 모드 --------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def overlay(qapp):
|
||||
cfg = OverlayConfig()
|
||||
cfg.visible = False
|
||||
widget = SubtitleOverlay(SubtitleStyle(), cfg)
|
||||
yield widget
|
||||
widget.close()
|
||||
|
||||
|
||||
def test_기본은_잠겨_있어_움직이지_않는다(overlay):
|
||||
"""게임 중에 자막을 실수로 끌고 다니는 사고를 막는 기본값이다."""
|
||||
assert OverlayConfig().locked is True
|
||||
assert overlay.edit_mode is False
|
||||
assert overlay._interactive is False
|
||||
|
||||
|
||||
def test_위치_조정을_켜면_잡을_수_있다(overlay):
|
||||
overlay.set_edit_mode(True)
|
||||
assert overlay.edit_mode is True
|
||||
assert overlay._interactive is True
|
||||
# 클릭 통과가 켜져 있으면 마우스가 창을 그냥 지나가 잡을 수가 없다.
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
assert not overlay.testAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
|
||||
|
||||
overlay.set_edit_mode(False)
|
||||
assert overlay._interactive is False
|
||||
|
||||
|
||||
def test_모서리를_집었는지_알아본다(overlay):
|
||||
overlay.setGeometry(0, 0, 600, 200)
|
||||
assert overlay._corner_at(QPoint(2, 2)) == "topleft"
|
||||
assert overlay._corner_at(QPoint(598, 2)) == "topright"
|
||||
assert overlay._corner_at(QPoint(2, 198)) == "bottomleft"
|
||||
assert overlay._corner_at(QPoint(598, 198)) == "bottomright"
|
||||
# 가운데는 이동용이지 크기 조절이 아니다
|
||||
assert overlay._corner_at(QPoint(300, 100)) == ""
|
||||
|
||||
|
||||
def test_크기를_키우면_글자도_같이_커진다(overlay):
|
||||
overlay.set_edit_mode(True)
|
||||
overlay.setGeometry(100, 100, 600, 200)
|
||||
overlay.style_cfg.font_size = 40
|
||||
|
||||
overlay._resize_corner = "bottomright"
|
||||
overlay._resize_geo = QRect(overlay.geometry())
|
||||
overlay._resize_mouse = QPoint(700, 300)
|
||||
overlay._resize_font = 40
|
||||
overlay._resize_to(QPoint(700, 500)) # 높이 200 -> 400
|
||||
|
||||
assert overlay.height() == 400
|
||||
assert overlay.style_cfg.font_size == 80
|
||||
|
||||
|
||||
def test_반대쪽_모서리는_제자리에_있는다(overlay):
|
||||
"""왼쪽 위를 끌면 오른쪽 아래는 움직이지 않아야 한다."""
|
||||
overlay.set_edit_mode(True)
|
||||
overlay.setGeometry(200, 200, 600, 200)
|
||||
start = QRect(overlay.geometry())
|
||||
|
||||
overlay._resize_corner = "topleft"
|
||||
overlay._resize_geo = start
|
||||
overlay._resize_mouse = QPoint(200, 200)
|
||||
overlay._resize_font = overlay.style_cfg.font_size
|
||||
overlay._resize_to(QPoint(300, 250))
|
||||
|
||||
assert overlay.geometry().right() == start.right()
|
||||
assert overlay.geometry().bottom() == start.bottom()
|
||||
assert overlay.geometry().x() == 300
|
||||
|
||||
|
||||
def test_최소_크기_아래로는_줄지_않는다(overlay):
|
||||
overlay.set_edit_mode(True)
|
||||
overlay.setGeometry(0, 0, 600, 200)
|
||||
overlay._resize_corner = "bottomright"
|
||||
overlay._resize_geo = QRect(overlay.geometry())
|
||||
overlay._resize_mouse = QPoint(600, 200)
|
||||
overlay._resize_font = overlay.style_cfg.font_size
|
||||
overlay._resize_to(QPoint(-5000, -5000))
|
||||
|
||||
assert overlay.width() >= overlay.minimumWidth()
|
||||
assert overlay.height() >= overlay.minimumHeight()
|
||||
Reference in New Issue
Block a user