/
Ruden161
/
player
Обзор
Документация
Войти
/
Ruden161
/
player
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/ui/timecode_widget.py
251 строка
9 KB
Ruden161
Проводник видео, горячие клавиши, YouTube-style панель
01 мар 2026, 19:16
01 мар 2026, 19:16
816ee8b
Код
Авторство
О чём код?
""" Виджет карточки таймкода """ from typing import Optional from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QFrame, QSizePolicy from PyQt6.QtCore import Qt, pyqtSignal from src.video.timecodes import TimecodeEntry from src.utils.config import Config from src.ui.timecode_buttons import LoopButton, SlowMoButton class TimecodeWidget(QFrame): """Виджет карточки таймкода""" # Сигналы clicked = pyqtSignal(TimecodeEntry) double_clicked = pyqtSignal(TimecodeEntry) loop_toggled = pyqtSignal(TimecodeEntry, bool) slowmo_toggled = pyqtSignal(TimecodeEntry, bool) def __init__(self, timecode: TimecodeEntry, parent=None): super().__init__(parent) self.timecode = timecode self.is_expanded = False self.current_description = "" self._init_ui() self._update_content() def _init_ui(self): """Инициализация интерфейса""" self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) self.setMinimumWidth(1) self.main_layout = QVBoxLayout(self) self.main_layout.setContentsMargins(0, 0, 0, 0) self.main_layout.setSpacing(0) # Контейнер заголовка header_container = self._create_header() self.main_layout.addWidget(header_container) # Разделитель self.separator_line = self._create_separator() self.main_layout.addWidget(self.separator_line) # Описание self.description_label = self._create_description() self.main_layout.addWidget(self.description_label) # Стили рамки self._apply_styles() def _create_header(self) -> QWidget: """Создать контейнер заголовка""" header_container = QWidget() header_container.setStyleSheet("background-color: #14141b; border: none; border-radius: 6px 6px 0px 0px;") header_layout = QHBoxLayout(header_container) header_layout.setContentsMargins(12, 10, 12, 10) header_layout.setSpacing(12) # Время self.time_label = QLabel() self.time_label.setStyleSheet(f""" QLabel {{ color: {Config.COLOR_ACCENT}; font-weight: bold; font-family: 'Consolas', 'Monaco', monospace; font-size: 14px; padding: 0px; }} """) self.time_label.setFixedWidth(55) header_layout.addWidget(self.time_label) # Название self.name_label = QLabel() self.name_label.setStyleSheet(f""" QLabel {{ color: {Config.COLOR_TEXT}; font-size: 14px; font-weight: 500; padding: 0px; background-color: transparent; border: none; }} """) self.name_label.setWordWrap(True) self.name_label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) self.name_label.setMinimumWidth(1) header_layout.addWidget(self.name_label) # Кнопка зацикливания self.loop_button = LoopButton() self.loop_button.loop_toggled.connect(self._on_loop_toggled) header_layout.addWidget(self.loop_button) # Кнопка slow-motion self.slowmo_button = SlowMoButton() self.slowmo_button.slowmo_toggled.connect(self._on_slowmo_toggled) header_layout.addWidget(self.slowmo_button) # Кнопка раскрытия self.expand_button = QPushButton("▼") self.expand_button.setFixedSize(28, 28) self.expand_button.setCursor(Qt.CursorShape.PointingHandCursor) self.expand_button.setStyleSheet(f""" QPushButton {{ background-color: transparent; border: none; color: {Config.COLOR_TEXT_DIM}; font-size: 12px; padding: 0px; border-radius: 4px; }} QPushButton:hover {{ background-color: {Config.COLOR_BUTTON_HOVER}; color: {Config.COLOR_ACCENT}; }} QPushButton:pressed {{ background-color: {Config.COLOR_ACCENT}; color: white; }} """) self.expand_button.clicked.connect(self.toggle_expanded) self.expand_button.setVisible(False) header_layout.addWidget(self.expand_button) return header_container def _create_separator(self) -> QFrame: """Создать разделитель""" separator = QFrame() separator.setFrameShape(QFrame.Shape.HLine) separator.setFrameShadow(QFrame.Shadow.Plain) separator.setStyleSheet(f""" QFrame {{ background-color: {Config.COLOR_BORDER}; border: none; max-height: 1px; min-height: 1px; margin: 0px; }} """) separator.setVisible(False) return separator def _create_description(self) -> QLabel: """Создать описание""" description_label = QLabel() description_label.setStyleSheet(f""" QLabel {{ color: {Config.COLOR_TEXT_DIM}; font-size: 13px; padding: 8px 12px; background-color: #1a1b26; border: none; border-radius: 0px 0px 6px 6px; }} """) description_label.setWordWrap(True) description_label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) description_label.setMinimumWidth(1) description_label.setVisible(False) description_label.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse) return description_label def _apply_styles(self): """Применить стили рамки""" self.setStyleSheet(f""" QFrame {{ background-color: {Config.COLOR_PANEL}; border: 1px solid {Config.COLOR_BORDER}; border-radius: 6px; margin: 4px 0px; }} QFrame:hover {{ border: 1px solid {Config.COLOR_ACCENT}; }} """) def _update_content(self): """Обновить содержимое виджета""" total_seconds = int(self.timecode.time) minutes = total_seconds // 60 seconds = total_seconds % 60 time_str = f"{minutes:02d}:{seconds:02d}" self.time_label.setText(time_str) # Разделяем description на название и описание if self.timecode.tags and len(self.timecode.tags) > 0: name = self.timecode.tags[0] description = self.timecode.description or "" else: name = self.timecode.description or "Без названия" description = "" self.current_description = description self.name_label.setText(name) has_description = bool(description.strip()) self.expand_button.setVisible(has_description) if has_description: self.description_label.setText(description) else: self.description_label.setText("") self.set_expanded(self.is_expanded) def toggle_expanded(self): """Переключить состояние раскрытия""" self.set_expanded(not self.is_expanded) def set_expanded(self, expanded: bool): """Установить состояние раскрытия""" self.is_expanded = expanded has_description = bool(self.current_description.strip()) show_description = expanded and has_description self.description_label.setVisible(show_description) self.separator_line.setVisible(show_description) if has_description: self.expand_button.setText("▲" if expanded else "▼") def mousePressEvent(self, event): """Обработка нажатия мыши""" if event.button() == Qt.MouseButton.LeftButton: if not self.expand_button.geometry().contains(event.pos()): self.clicked.emit(self.timecode) super().mousePressEvent(event) def mouseDoubleClickEvent(self, event): """Обработка двойного клика""" if event.button() == Qt.MouseButton.LeftButton: self.double_clicked.emit(self.timecode) super().mouseDoubleClickEvent(event) def _on_loop_toggled(self, is_looping: bool): """Обработчик переключения зацикливания""" self.loop_toggled.emit(self.timecode, is_looping) def _on_slowmo_toggled(self, is_slowmo: bool): """Обработчик переключения slow-motion""" self.slowmo_toggled.emit(self.timecode, is_slowmo) def get_loop_duration(self) -> float: """Получить длительность зацикливания""" return self.loop_button.get_duration() def get_slowmo_speed(self) -> float: """Получить скорость slow-motion""" return self.slowmo_button.get_speed()