mirror of
https://github.com/YaoFANGUK/video-subtitle-remover.git
synced 2026-05-02 22:47:30 +08:00
选区部分全部采用浮点值, 以便更好的适配不同尺寸不同分辨率
This commit is contained in:
@@ -39,8 +39,8 @@ class Config(QConfig):
|
||||
windowH = ConfigItem("Window", "Height", 1200)
|
||||
|
||||
# 使用一个配置项存储所有选区
|
||||
# 默认值为一个选区,格式为:"x,y,w,h;x,y,w,h;...",分号分隔不同选区
|
||||
subtitleSelectionAreas = ConfigItem("Main", "SubtitleSelectionAreas", "0.15,0.88,0.70,0.11")
|
||||
# 默认值为一个选区,格式为:"ymin,ymax,xmin,xmax;ymin,ymax,xmin,xmax;...",分号分隔不同选区
|
||||
subtitleSelectionAreas = ConfigItem("Main", "SubtitleSelectionAreas", "0.88,0.99,0.15,0.85")
|
||||
|
||||
"""
|
||||
MODE可选算法类型
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import cv2
|
||||
from PySide6.QtWidgets import QWidget, QVBoxLayout, QMenu
|
||||
from PySide6.QtCore import Qt, Signal, QRect, QRectF, QObject, QEvent
|
||||
from PySide6.QtGui import QAction, QShortcut
|
||||
from PySide6 import QtCore, QtWidgets, QtGui
|
||||
from PySide6.QtGui import QAction, QShortcut, QCursor
|
||||
from PySide6 import QtCore, QtWidgets, QtGui
|
||||
from qfluentwidgets import qconfig, CardWidget, HollowHandleStyle
|
||||
|
||||
from backend.config import config, tr
|
||||
@@ -20,8 +20,8 @@ class VideoDisplayComponent(QWidget):
|
||||
|
||||
# 初始化变量
|
||||
self.is_drawing = False
|
||||
self.selection_rect = QRect() # 当前正在绘制或调整的选区
|
||||
self.selection_rects = [] # 存储多个选区
|
||||
self.selection_rect = (0, 0, 0, 0) # 当前正在绘制或调整的选区 (ymin, ymax, xmin, xmax)
|
||||
self.selection_rects = [] # 存储多个选区,每个元素为 (ymin, ymax, xmin, xmax)
|
||||
self.active_selection_index = -1 # 当前活动选区的索引
|
||||
self.drag_start_pos = None
|
||||
self.resize_edge = None
|
||||
@@ -272,6 +272,13 @@ class VideoDisplayComponent(QWidget):
|
||||
|
||||
# 绘制所有选区
|
||||
if draw_selection:
|
||||
# 计算缩放比例
|
||||
display_size = self.video_display.size()
|
||||
pixmap_size = self.current_pixmap.size()
|
||||
scale_x = pixmap_size.width() / display_size.width()
|
||||
scale_y = pixmap_size.height() / display_size.height()
|
||||
video_display_width = self.video_display.width()
|
||||
video_display_height = self.video_display.height()
|
||||
for i, rect in enumerate(self.selection_rects):
|
||||
# 设置选择框样式
|
||||
if i == self.active_selection_index:
|
||||
@@ -283,15 +290,34 @@ class VideoDisplayComponent(QWidget):
|
||||
pen.setWidth(2)
|
||||
painter.setPen(pen)
|
||||
|
||||
# 将比例坐标转换为像素坐标
|
||||
ymin, ymax, xmin, xmax = rect
|
||||
pixel_rect = QRect(
|
||||
int(xmin * scale_x * video_display_width),
|
||||
int(ymin * scale_y * video_display_height),
|
||||
int((xmax - xmin) * scale_x * video_display_width),
|
||||
int((ymax - ymin) * scale_y * video_display_height)
|
||||
)
|
||||
|
||||
# 绘制选择框
|
||||
painter.drawRect(rect)
|
||||
painter.drawRect(pixel_rect)
|
||||
|
||||
# 如果正在绘制新选区,也绘制它
|
||||
if self.is_drawing and self.selection_rect.isValid():
|
||||
if self.is_drawing and any(self.selection_rect):
|
||||
pen = QtGui.QPen(QtGui.QColor(0, 255, 0)) # 绿色
|
||||
pen.setWidth(2)
|
||||
painter.setPen(pen)
|
||||
painter.drawRect(self.selection_rect)
|
||||
|
||||
# 将比例坐标转换为像素坐标
|
||||
ymin, ymax, xmin, xmax = self.selection_rect
|
||||
pixel_rect = QRect(
|
||||
int(xmin * scale_x * video_display_width),
|
||||
int(ymin * scale_y * video_display_height),
|
||||
int((xmax - xmin) * scale_x * video_display_width),
|
||||
int((ymax - ymin) * scale_y * video_display_height)
|
||||
)
|
||||
|
||||
painter.drawRect(pixel_rect)
|
||||
|
||||
# 绘制AB分区标记
|
||||
total_frames = self.video_slider.maximum()
|
||||
@@ -346,87 +372,104 @@ class VideoDisplayComponent(QWidget):
|
||||
"""鼠标按下事件处理"""
|
||||
if not self.enable_mouse_events:
|
||||
return
|
||||
|
||||
# 处理右键点击,显示上下文菜单
|
||||
|
||||
# 右键点击显示上下文菜单
|
||||
if event.button() == Qt.RightButton:
|
||||
self.context_menu.exec_(event.globalPos())
|
||||
return
|
||||
|
||||
video_display_width = self.video_display.width()
|
||||
video_display_height = self.video_display.height()
|
||||
|
||||
pos = event.pos()
|
||||
|
||||
# 检查是否按下了Ctrl键
|
||||
is_ctrl_pressed = event.modifiers() & Qt.ControlModifier
|
||||
|
||||
# 检测双击,重置所有选区
|
||||
if event.type() == QtCore.QEvent.MouseButtonDblClick:
|
||||
self.selection_rects = []
|
||||
# 开始绘制新选区
|
||||
if event.modifiers() & Qt.ControlModifier:
|
||||
self.is_drawing = True
|
||||
pos = event.pos()
|
||||
|
||||
# 转换为比例坐标
|
||||
y_ratio = (pos.y() - self.border_top) / video_display_height if video_display_height > 0 else 0
|
||||
x_ratio = (pos.x() - self.border_left) / video_display_width if video_display_width > 0 else 0
|
||||
|
||||
# 初始化选区为单点
|
||||
self.selection_rect = (y_ratio, y_ratio, x_ratio, x_ratio)
|
||||
self.drag_start_pos = (y_ratio, x_ratio) # 保存起始点的比例坐标
|
||||
self.resize_edge = None
|
||||
self.active_selection_index = -1
|
||||
self.update_preview_with_rect()
|
||||
self.selections_changed.emit(self.selection_rects)
|
||||
return
|
||||
|
||||
# 如果按下Ctrl键,开始绘制新选区
|
||||
if is_ctrl_pressed:
|
||||
self.is_drawing = True
|
||||
self.selection_rect = QRect(pos, pos)
|
||||
self.drag_start_pos = pos
|
||||
self.resize_edge = None
|
||||
self.active_selection_index = -1 # 不选中任何已有选区
|
||||
# 双击重置所有选区
|
||||
if event.type() == QEvent.MouseButtonDblClick:
|
||||
self.clear_selections()
|
||||
return
|
||||
|
||||
# 检查是否点击了已有选区
|
||||
pos = event.pos()
|
||||
y_ratio = (pos.y() - self.border_top) / video_display_height if video_display_height > 0 else 0
|
||||
x_ratio = (pos.x() - self.border_left) / video_display_width if video_display_width > 0 else 0
|
||||
|
||||
clicked_index = -1
|
||||
for i, rect in enumerate(self.selection_rects):
|
||||
# 将比例坐标转换为像素坐标用于检测
|
||||
ymin, ymax, xmin, xmax = rect
|
||||
pixel_rect = QRect(
|
||||
int(xmin * video_display_width) + self.border_left,
|
||||
int(ymin * video_display_height) + self.border_top,
|
||||
int((xmax - xmin) * video_display_width),
|
||||
int((ymax - ymin) * video_display_height)
|
||||
)
|
||||
|
||||
# 检查是否在选区边缘(用于调整大小)
|
||||
if self.is_on_rect_edge(pos, rect):
|
||||
if self.is_on_rect_edge(pos, pixel_rect):
|
||||
clicked_index = i
|
||||
self.active_selection_index = i
|
||||
self.resize_edge = self.get_resize_edge(pos, rect)
|
||||
self.drag_start_pos = pos
|
||||
self.resize_edge = self.get_resize_edge(pos, pixel_rect)
|
||||
self.drag_start_pos = (y_ratio, x_ratio)
|
||||
self.update_preview_with_rect()
|
||||
return
|
||||
# 检查是否在选区内部(用于移动)
|
||||
elif rect.contains(pos):
|
||||
elif pixel_rect.contains(pos):
|
||||
clicked_index = i
|
||||
self.active_selection_index = i
|
||||
self.resize_edge = "move"
|
||||
self.drag_start_pos = pos
|
||||
self.drag_start_pos = (y_ratio, x_ratio)
|
||||
self.update_preview_with_rect()
|
||||
return
|
||||
|
||||
# 如果没有点击任何选区,开始绘制新选区
|
||||
if clicked_index == -1:
|
||||
self.is_drawing = True
|
||||
self.selection_rect = QRect(pos, pos)
|
||||
self.drag_start_pos = pos
|
||||
self.selection_rect = (y_ratio, y_ratio, x_ratio, x_ratio)
|
||||
self.drag_start_pos = (y_ratio, x_ratio)
|
||||
self.resize_edge = None
|
||||
self.active_selection_index = -1
|
||||
|
||||
def is_on_rect_edge(self, pos, rect):
|
||||
"""检查点是否在矩形边缘"""
|
||||
def is_on_rect_edge(self, pos, pixel_rect):
|
||||
"""检查点是否在矩形边缘
|
||||
注意:这里的pixel_rect是已经转换为像素坐标的QRect对象
|
||||
"""
|
||||
# 右下角
|
||||
if abs(pos.x() - rect.right()) <= self.edge_size and abs(pos.y() - rect.bottom()) <= self.edge_size:
|
||||
if abs(pos.x() - pixel_rect.right()) <= self.edge_size and abs(pos.y() - pixel_rect.bottom()) <= self.edge_size:
|
||||
return True
|
||||
# 右上角
|
||||
elif abs(pos.x() - rect.right()) <= self.edge_size and abs(pos.y() - rect.top()) <= self.edge_size:
|
||||
elif abs(pos.x() - pixel_rect.right()) <= self.edge_size and abs(pos.y() - pixel_rect.top()) <= self.edge_size:
|
||||
return True
|
||||
# 左下角
|
||||
elif abs(pos.x() - rect.left()) <= self.edge_size and abs(pos.y() - rect.bottom()) <= self.edge_size:
|
||||
elif abs(pos.x() - pixel_rect.left()) <= self.edge_size and abs(pos.y() - pixel_rect.bottom()) <= self.edge_size:
|
||||
return True
|
||||
# 左上角
|
||||
elif abs(pos.x() - rect.left()) <= self.edge_size and abs(pos.y() - rect.top()) <= self.edge_size:
|
||||
elif abs(pos.x() - pixel_rect.left()) <= self.edge_size and abs(pos.y() - pixel_rect.top()) <= self.edge_size:
|
||||
return True
|
||||
# 左边缘
|
||||
elif abs(pos.x() - rect.left()) <= self.edge_size and rect.top() <= pos.y() <= rect.bottom():
|
||||
elif abs(pos.x() - pixel_rect.left()) <= self.edge_size and pixel_rect.top() <= pos.y() <= pixel_rect.bottom():
|
||||
return True
|
||||
# 右边缘
|
||||
elif abs(pos.x() - rect.right()) <= self.edge_size and rect.top() <= pos.y() <= rect.bottom():
|
||||
elif abs(pos.x() - pixel_rect.right()) <= self.edge_size and pixel_rect.top() <= pos.y() <= pixel_rect.bottom():
|
||||
return True
|
||||
# 上边缘
|
||||
elif abs(pos.y() - rect.top()) <= self.edge_size and rect.left() <= pos.x() <= rect.right():
|
||||
elif abs(pos.y() - pixel_rect.top()) <= self.edge_size and pixel_rect.left() <= pos.x() <= pixel_rect.right():
|
||||
return True
|
||||
# 下边缘
|
||||
elif abs(pos.y() - rect.bottom()) <= self.edge_size and rect.left() <= pos.x() <= rect.right():
|
||||
elif abs(pos.y() - pixel_rect.bottom()) <= self.edge_size and pixel_rect.left() <= pos.x() <= pixel_rect.right():
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -462,82 +505,69 @@ class VideoDisplayComponent(QWidget):
|
||||
"""鼠标移动事件处理"""
|
||||
if not self.enable_mouse_events:
|
||||
return
|
||||
|
||||
video_display_width = self.video_display.width()
|
||||
video_display_height = self.video_display.height()
|
||||
|
||||
pos = event.pos()
|
||||
y_ratio = (pos.y() - self.border_top) / video_display_height if video_display_height > 0 else 0
|
||||
x_ratio = (pos.x() - self.border_left) / video_display_width if video_display_width > 0 else 0
|
||||
|
||||
# 限制比例值在0-1范围内
|
||||
y_ratio = max(0, min(1, y_ratio))
|
||||
x_ratio = max(0, min(1, x_ratio))
|
||||
|
||||
# 根据不同的操作模式处理鼠标移动
|
||||
if self.is_drawing: # 绘制新选择框
|
||||
# 更新选择框的右下角
|
||||
current_rect = QRect(self.drag_start_pos, pos).normalized()
|
||||
self.selection_rect = current_rect
|
||||
# 更新选择框的右下角,保留原始拖动方向
|
||||
start_y, _, start_x, _ = self.selection_rect
|
||||
self.selection_rect = (start_y, y_ratio, start_x, x_ratio)
|
||||
self.update_preview_with_rect()
|
||||
elif self.resize_edge and self.active_selection_index >= 0: # 调整选择框大小或位置
|
||||
ymin, ymax, xmin, xmax = self.selection_rects[self.active_selection_index]
|
||||
start_y, start_x = self.drag_start_pos
|
||||
|
||||
if self.resize_edge == "move":
|
||||
# 移动整个选择框
|
||||
dx = pos.x() - self.drag_start_pos.x()
|
||||
dy = pos.y() - self.drag_start_pos.y()
|
||||
dy = y_ratio - start_y
|
||||
dx = x_ratio - start_x
|
||||
|
||||
# 保存原始选择框尺寸
|
||||
original_width = self.selection_rects[self.active_selection_index].width()
|
||||
original_height = self.selection_rects[self.active_selection_index].height()
|
||||
# 计算新位置,确保不超出边界
|
||||
new_ymin = max(0, min(1 - (ymax - ymin), ymin + dy))
|
||||
new_ymax = min(1, max(new_ymin + (ymax - ymin), new_ymin))
|
||||
new_xmin = max(0, min(1 - (xmax - xmin), xmin + dx))
|
||||
new_xmax = min(1, max(new_xmin + (xmax - xmin), new_xmin))
|
||||
|
||||
# 计算新位置
|
||||
new_rect = self.selection_rects[self.active_selection_index].translated(dx, dy)
|
||||
|
||||
# 获取视频显示区域
|
||||
display_rect = self.video_display.rect()
|
||||
|
||||
# 检查是否超出边界,如果超出则调整位置但保持尺寸
|
||||
if new_rect.left() < 0:
|
||||
new_rect.moveLeft(0)
|
||||
if new_rect.top() < 0:
|
||||
new_rect.moveTop(0)
|
||||
if new_rect.right() > display_rect.width():
|
||||
new_rect.moveRight(display_rect.width())
|
||||
if new_rect.bottom() > display_rect.height():
|
||||
new_rect.moveBottom(display_rect.height())
|
||||
|
||||
# 确保尺寸不变
|
||||
if new_rect.width() != original_width or new_rect.height() != original_height:
|
||||
# 如果尺寸变了,恢复原始尺寸
|
||||
if new_rect.left() == 0:
|
||||
new_rect.setWidth(original_width)
|
||||
if new_rect.top() == 0:
|
||||
new_rect.setHeight(original_height)
|
||||
if new_rect.right() == display_rect.width():
|
||||
new_rect.setLeft(new_rect.right() - original_width)
|
||||
if new_rect.bottom() == display_rect.height():
|
||||
new_rect.setTop(new_rect.bottom() - original_height)
|
||||
|
||||
self.selection_rects[self.active_selection_index] = new_rect
|
||||
self.drag_start_pos = pos
|
||||
self.selection_rects[self.active_selection_index] = (new_ymin, new_ymax, new_xmin, new_xmax)
|
||||
self.drag_start_pos = (y_ratio, x_ratio)
|
||||
else:
|
||||
# 调整选择框大小
|
||||
rect = self.selection_rects[self.active_selection_index]
|
||||
if "left" in self.resize_edge:
|
||||
rect.setLeft(pos.x())
|
||||
xmin = min(xmax - 0.01, x_ratio)
|
||||
if "right" in self.resize_edge:
|
||||
rect.setRight(pos.x())
|
||||
xmax = max(xmin + 0.01, x_ratio)
|
||||
if "top" in self.resize_edge:
|
||||
rect.setTop(pos.y())
|
||||
ymin = min(ymax - 0.01, y_ratio)
|
||||
if "bottom" in self.resize_edge:
|
||||
rect.setBottom(pos.y())
|
||||
ymax = max(ymin + 0.01, y_ratio)
|
||||
|
||||
# 确保选择框在视频显示区域内
|
||||
display_rect = self.video_display.rect()
|
||||
if rect.left() < 0:
|
||||
rect.setLeft(0)
|
||||
if rect.top() < 0:
|
||||
rect.setTop(0)
|
||||
if rect.right() > display_rect.width():
|
||||
rect.setRight(display_rect.width())
|
||||
if rect.bottom() > display_rect.height():
|
||||
rect.setBottom(display_rect.height())
|
||||
# 确保选择框在有效范围内
|
||||
xmin = max(0, min(xmin, 1))
|
||||
xmax = max(0, min(xmax, 1))
|
||||
ymin = max(0, min(ymin, 1))
|
||||
ymax = max(0, min(ymax, 1))
|
||||
|
||||
self.selection_rects[self.active_selection_index] = rect
|
||||
|
||||
# 确保xmin < xmax, ymin < ymax
|
||||
if xmin > xmax:
|
||||
xmin, xmax = xmax, xmin
|
||||
if ymin > ymax:
|
||||
ymin, ymax = ymax, ymin
|
||||
|
||||
self.selection_rects[self.active_selection_index] = (ymin, ymax, xmin, xmax)
|
||||
|
||||
self.update_preview_with_rect()
|
||||
else:
|
||||
# 更新鼠标指针形状
|
||||
# 更新鼠标光标形状
|
||||
self.update_cursor_shape(pos)
|
||||
|
||||
def selection_mouse_release(self, event):
|
||||
@@ -547,11 +577,26 @@ class VideoDisplayComponent(QWidget):
|
||||
|
||||
# 结束绘制或调整
|
||||
if self.is_drawing:
|
||||
# 标准化选择框(确保宽度和高度为正)
|
||||
self.selection_rect = self.selection_rect.normalized()
|
||||
# 标准化选择框(确保ymin < ymax, xmin < xmax)
|
||||
ymin, ymax, xmin, xmax = self.selection_rect
|
||||
if ymin > ymax:
|
||||
ymin, ymax = ymax, ymin
|
||||
if xmin > xmax:
|
||||
xmin, xmax = xmax, xmin
|
||||
|
||||
# 更新标准化后的选区
|
||||
self.selection_rect = (ymin, ymax, xmin, xmax)
|
||||
|
||||
# 如果选择框有效(不是点击),添加到选区列表
|
||||
if self.selection_rect.width() > 5 and self.selection_rect.height() > 5:
|
||||
# 使用比例值计算宽度和高度
|
||||
width_ratio = abs(xmax - xmin)
|
||||
height_ratio = abs(ymax - ymin)
|
||||
|
||||
# 转换为像素大小进行判断
|
||||
pixel_width = width_ratio * self.video_display.width()
|
||||
pixel_height = height_ratio * self.video_display.height()
|
||||
|
||||
if pixel_width > 5 and pixel_height > 5:
|
||||
self.selection_rects.append(self.selection_rect)
|
||||
self.active_selection_index = len(self.selection_rects) - 1
|
||||
|
||||
@@ -559,10 +604,17 @@ class VideoDisplayComponent(QWidget):
|
||||
self.selections_changed.emit(self.selection_rects)
|
||||
|
||||
self.is_drawing = False
|
||||
self.selection_rect = QRect()
|
||||
self.selection_rect = (0, 0, 0, 0) # 重置为空选区
|
||||
elif self.resize_edge and self.active_selection_index >= 0:
|
||||
# 标准化选择框
|
||||
self.selection_rects[self.active_selection_index] = self.selection_rects[self.active_selection_index].normalized()
|
||||
ymin, ymax, xmin, xmax = self.selection_rects[self.active_selection_index]
|
||||
if ymin > ymax:
|
||||
ymin, ymax = ymax, ymin
|
||||
if xmin > xmax:
|
||||
xmin, xmax = xmax, xmin
|
||||
|
||||
# 更新标准化后的选区
|
||||
self.selection_rects[self.active_selection_index] = (ymin, ymax, xmin, xmax)
|
||||
|
||||
# 发送选择框变化信号
|
||||
self.selections_changed.emit(self.selection_rects)
|
||||
@@ -571,53 +623,71 @@ class VideoDisplayComponent(QWidget):
|
||||
|
||||
def update_cursor_shape(self, pos):
|
||||
"""根据鼠标位置更新光标形状"""
|
||||
# 首先检查是否有活动选区
|
||||
video_display_height = self.video_display.height()
|
||||
video_display_width = self.video_display.width()
|
||||
|
||||
# 如果有活动选区,优先检查活动选区
|
||||
if self.active_selection_index >= 0 and self.active_selection_index < len(self.selection_rects):
|
||||
rect = self.selection_rects[self.active_selection_index]
|
||||
# 获取活动选区
|
||||
ymin, ymax, xmin, xmax = self.selection_rects[self.active_selection_index]
|
||||
|
||||
# 确保坐标规范化
|
||||
if xmin > xmax:
|
||||
xmin, xmax = xmax, xmin
|
||||
if ymin > ymax:
|
||||
ymin, ymax = ymax, ymin
|
||||
|
||||
# 将比例坐标转换为像素坐标
|
||||
pixel_rect = QRect(
|
||||
round(xmin * video_display_width) + self.border_left,
|
||||
round(ymin * video_display_height) + self.border_top,
|
||||
round((xmax - xmin) * video_display_width),
|
||||
round((ymax - ymin) * video_display_height)
|
||||
)
|
||||
|
||||
# 检查鼠标是否在选择框边缘
|
||||
if (abs(pos.x() - rect.left()) <= self.edge_size and
|
||||
rect.top() <= pos.y() <= rect.bottom()):
|
||||
self.video_display.setCursor(Qt.SizeHorCursor)
|
||||
return
|
||||
elif (abs(pos.x() - rect.right()) <= self.edge_size and
|
||||
rect.top() <= pos.y() <= rect.bottom()):
|
||||
self.video_display.setCursor(Qt.SizeHorCursor)
|
||||
return
|
||||
elif (abs(pos.y() - rect.top()) <= self.edge_size and
|
||||
rect.left() <= pos.x() <= rect.right()):
|
||||
self.video_display.setCursor(Qt.SizeVerCursor)
|
||||
return
|
||||
elif (abs(pos.y() - rect.bottom()) <= self.edge_size and
|
||||
rect.left() <= pos.x() <= rect.right()):
|
||||
self.video_display.setCursor(Qt.SizeVerCursor)
|
||||
return
|
||||
elif (abs(pos.x() - rect.left()) <= self.edge_size and
|
||||
abs(pos.y() - rect.top()) <= self.edge_size):
|
||||
self.video_display.setCursor(Qt.SizeFDiagCursor)
|
||||
return
|
||||
elif (abs(pos.x() - rect.right()) <= self.edge_size and
|
||||
abs(pos.y() - rect.top()) <= self.edge_size):
|
||||
self.video_display.setCursor(Qt.SizeBDiagCursor)
|
||||
return
|
||||
elif (abs(pos.x() - rect.left()) <= self.edge_size and
|
||||
abs(pos.y() - rect.bottom()) <= self.edge_size):
|
||||
self.video_display.setCursor(Qt.SizeBDiagCursor)
|
||||
return
|
||||
elif (abs(pos.x() - rect.right()) <= self.edge_size and
|
||||
abs(pos.y() - rect.bottom()) <= self.edge_size):
|
||||
self.video_display.setCursor(Qt.SizeFDiagCursor)
|
||||
return
|
||||
elif rect.contains(pos):
|
||||
if self.is_on_rect_edge(pos, pixel_rect):
|
||||
# 根据边缘类型设置光标
|
||||
edge_type = self.get_resize_edge(pos, pixel_rect)
|
||||
if edge_type == "left" or edge_type == "right":
|
||||
self.video_display.setCursor(Qt.SizeHorCursor)
|
||||
return
|
||||
elif edge_type == "top" or edge_type == "bottom":
|
||||
self.video_display.setCursor(Qt.SizeVerCursor)
|
||||
return
|
||||
elif edge_type == "topleft" or edge_type == "bottomright":
|
||||
self.video_display.setCursor(Qt.SizeFDiagCursor)
|
||||
return
|
||||
elif edge_type == "topright" or edge_type == "bottomleft":
|
||||
self.video_display.setCursor(Qt.SizeBDiagCursor)
|
||||
return
|
||||
elif pixel_rect.contains(pos):
|
||||
self.video_display.setCursor(Qt.SizeAllCursor)
|
||||
return
|
||||
|
||||
# 如果没有活动选区或鼠标不在活动选区上,检查所有其他选区
|
||||
for rect in self.selection_rects:
|
||||
# 获取选区坐标
|
||||
ymin, ymax, xmin, xmax = rect
|
||||
|
||||
# 确保坐标规范化
|
||||
if xmin > xmax:
|
||||
xmin, xmax = xmax, xmin
|
||||
if ymin > ymax:
|
||||
ymin, ymax = ymax, ymin
|
||||
|
||||
# 将比例坐标转换为像素坐标
|
||||
pixel_rect = QRect(
|
||||
round(xmin * video_display_width) + self.border_left,
|
||||
round(ymin * video_display_height) + self.border_top,
|
||||
round((xmax - xmin) * video_display_width),
|
||||
round((ymax - ymin) * video_display_height)
|
||||
)
|
||||
|
||||
# 检查鼠标是否在选择框边缘
|
||||
if self.is_on_rect_edge(pos, rect):
|
||||
if self.is_on_rect_edge(pos, pixel_rect):
|
||||
# 根据边缘类型设置光标
|
||||
edge_type = self.get_resize_edge(pos, rect)
|
||||
edge_type = self.get_resize_edge(pos, pixel_rect)
|
||||
if edge_type == "left" or edge_type == "right":
|
||||
self.video_display.setCursor(Qt.SizeHorCursor)
|
||||
return
|
||||
@@ -631,7 +701,7 @@ class VideoDisplayComponent(QWidget):
|
||||
self.video_display.setCursor(Qt.SizeBDiagCursor)
|
||||
return
|
||||
# 检查鼠标是否在选择框内部
|
||||
elif rect.contains(pos):
|
||||
elif pixel_rect.contains(pos):
|
||||
self.video_display.setCursor(Qt.SizeAllCursor)
|
||||
return
|
||||
|
||||
@@ -664,11 +734,6 @@ class VideoDisplayComponent(QWidget):
|
||||
|
||||
def load_selections_from_config(self):
|
||||
"""从配置中加载选择框的相对位置和大小"""
|
||||
print("Loading selections from config...")
|
||||
# 检查是否有有效的视频尺寸
|
||||
if not self.scaled_width or not self.scaled_height:
|
||||
return False
|
||||
|
||||
# 从配置中读取选择框的相对位置和大小
|
||||
areas_str = config.subtitleSelectionAreas.value
|
||||
|
||||
@@ -678,31 +743,15 @@ class VideoDisplayComponent(QWidget):
|
||||
|
||||
# 清空现有选区
|
||||
self.selection_rects = []
|
||||
self.selection_ratios = []
|
||||
|
||||
# 解析所有选区
|
||||
for area_str in areas_str.split(';'):
|
||||
parts = area_str.split(',')
|
||||
if len(parts) != 4:
|
||||
continue
|
||||
|
||||
# 解析配置字符串
|
||||
areas = areas_str.split(";")
|
||||
for area in areas:
|
||||
try:
|
||||
x_ratio = float(parts[0])
|
||||
y_ratio = float(parts[1])
|
||||
w_ratio = float(parts[2])
|
||||
h_ratio = float(parts[3])
|
||||
|
||||
# 检查配置值是否在有效范围内
|
||||
if w_ratio <= 0.01 or h_ratio <= 0.005:
|
||||
continue
|
||||
|
||||
# 计算实际像素坐标
|
||||
x = int(x_ratio * self.scaled_width) + self.border_left
|
||||
y = int(y_ratio * self.scaled_height) + self.border_top
|
||||
w = int(w_ratio * self.scaled_width)
|
||||
h = int(h_ratio * self.scaled_height)
|
||||
|
||||
# 创建选择框
|
||||
self.selection_rects.append(QRect(x, y, w, h))
|
||||
parts = area.split(",")
|
||||
ymin, ymax, xmin, xmax = map(float, parts)
|
||||
self.selection_rects.append((ymin, ymax, xmin, xmax))
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
@@ -721,26 +770,27 @@ class VideoDisplayComponent(QWidget):
|
||||
def preview_coordinates_to_video_coordinates(self, preview_selection_rects):
|
||||
"""获取选择框在原始视频中的坐标"""
|
||||
selection_rects = []
|
||||
video_display_height = self.video_display.height()
|
||||
video_display_width = self.video_display.width()
|
||||
for rect in preview_selection_rects:
|
||||
if not rect.isValid() or not self.scaled_width or not self.scaled_height:
|
||||
continue
|
||||
ymin, ymax, xmin, xmax = rect
|
||||
|
||||
# 调整选择框坐标,考虑黑边偏移
|
||||
x_adjusted = max(0, rect.x() - self.border_left)
|
||||
y_adjusted = max(0, rect.y() - self.border_top)
|
||||
x_adjusted = max(0, xmin - self.border_left)
|
||||
y_adjusted = max(0, ymin - self.border_top)
|
||||
|
||||
# 如果选择框超出了实际视频区域,需要调整宽度和高度
|
||||
w_adjusted = min(rect.width(), self.scaled_width - x_adjusted)
|
||||
h_adjusted = min(rect.height(), self.scaled_height - y_adjusted)
|
||||
|
||||
w_adjusted = min((xmax - xmin), self.scaled_width - x_adjusted)
|
||||
h_adjusted = min((ymax - ymin), self.scaled_height - y_adjusted)
|
||||
# 转换为原始视频坐标
|
||||
scale_x = self.frame_width / self.scaled_width
|
||||
scale_y = self.frame_height / self.scaled_height
|
||||
|
||||
xmin = int(x_adjusted * scale_x)
|
||||
xmax = int((x_adjusted + w_adjusted) * scale_x)
|
||||
ymin = int(y_adjusted * scale_y)
|
||||
ymax = int((y_adjusted + h_adjusted) * scale_y)
|
||||
scale_x = self.frame_width / (self.scaled_width * video_display_width)
|
||||
scale_y = self.frame_height / (self.scaled_height * video_display_height)
|
||||
|
||||
# 使用round代替int,避免精度丢失
|
||||
xmin = round(x_adjusted * scale_x * video_display_width)
|
||||
xmax = round((x_adjusted + w_adjusted) * scale_x * video_display_width)
|
||||
ymin = round(y_adjusted * scale_y * video_display_height)
|
||||
ymax = round((y_adjusted + h_adjusted) * scale_y * video_display_height)
|
||||
|
||||
# 确保坐标在有效范围内
|
||||
xmin = max(0, min(xmin, self.frame_width))
|
||||
@@ -748,6 +798,12 @@ class VideoDisplayComponent(QWidget):
|
||||
ymin = max(0, min(ymin, self.frame_height))
|
||||
ymax = max(0, min(ymax, self.frame_height))
|
||||
|
||||
# 确保xmin < xmax, ymin < ymax
|
||||
if xmin > xmax:
|
||||
xmin, xmax = xmax, xmin
|
||||
if ymin > ymax:
|
||||
ymin, ymax = ymax, ymin
|
||||
|
||||
selection_rects.append((ymin, ymax, xmin, xmax))
|
||||
return selection_rects
|
||||
|
||||
@@ -759,20 +815,12 @@ class VideoDisplayComponent(QWidget):
|
||||
|
||||
def save_selections_to_config(self):
|
||||
"""保存所有选择框的相对位置和大小"""
|
||||
if not self.scaled_width or not self.scaled_height:
|
||||
return
|
||||
|
||||
areas_str_parts = []
|
||||
|
||||
for rect in self.selection_rects:
|
||||
# 计算相对于实际视频的位置和大小比例
|
||||
x_ratio = round((rect.x() - self.border_left) / self.scaled_width if self.scaled_width > 0 else 0, 4)
|
||||
y_ratio = round((rect.y() - self.border_top) / self.scaled_height if self.scaled_height > 0 else 0, 4)
|
||||
w_ratio = round(rect.width() / self.scaled_width if self.scaled_width > 0 else 0, 4)
|
||||
h_ratio = round(rect.height() / self.scaled_height if self.scaled_height > 0 else 0, 4)
|
||||
|
||||
# 添加到字符串部分
|
||||
areas_str_parts.append(f"{x_ratio},{y_ratio},{w_ratio},{h_ratio}")
|
||||
ymin, ymax, xmin, xmax = rect
|
||||
# 直接使用比例值,四舍五入到4位小数
|
||||
areas_str_parts.append(f"{round(ymin,4)},{round(ymax,4)},{round(xmin,4)},{round(xmax,4)}")
|
||||
|
||||
# 更新配置
|
||||
config.subtitleSelectionAreas.value = ";".join(areas_str_parts)
|
||||
@@ -793,22 +841,29 @@ class VideoDisplayComponent(QWidget):
|
||||
|
||||
def __handle_delete_selection(self):
|
||||
"""处理删除当前选区的逻辑"""
|
||||
if self.active_selection_index >= 0 and self.selection_rects:
|
||||
# 删除当前活跃选区
|
||||
self.selection_rects.pop(self.active_selection_index)
|
||||
|
||||
# 如果还有其他选区,将最后一个选区设为活跃选区
|
||||
if self.selection_rects:
|
||||
self.active_selection_index = len(self.selection_rects) - 1
|
||||
else:
|
||||
self.active_selection_index = -1
|
||||
|
||||
# 更新显示
|
||||
self.update_preview_with_rect()
|
||||
|
||||
# 发送选区变化信号
|
||||
self.selections_changed.emit(self.selection_rects)
|
||||
return True
|
||||
try:
|
||||
if self.active_selection_index >= 0 and self.selection_rects:
|
||||
# 删除当前活跃选区
|
||||
self.selection_rects.pop(self.active_selection_index)
|
||||
|
||||
# 如果还有其他选区,将最后一个选区设为活跃选区
|
||||
if self.selection_rects:
|
||||
self.active_selection_index = len(self.selection_rects) - 1
|
||||
else:
|
||||
self.active_selection_index = -1
|
||||
|
||||
# 更新显示
|
||||
self.update_preview_with_rect()
|
||||
|
||||
# 发送选区变化信号
|
||||
self.selections_changed.emit(self.selection_rects)
|
||||
return True
|
||||
return False
|
||||
finally:
|
||||
# 获取当前鼠标位置
|
||||
global_pos = QCursor.pos()
|
||||
pos = self.video_display.mapFromGlobal(global_pos)
|
||||
self.update_cursor_shape(pos)
|
||||
|
||||
def __handle_mark_for_ab_start(self):
|
||||
"""处理标记AB分区起点的逻辑"""
|
||||
|
||||
@@ -262,15 +262,15 @@ class HomeInterface(QWidget):
|
||||
)
|
||||
|
||||
# 保存边框信息,用于坐标转换
|
||||
self.border_left = left_border
|
||||
self.border_right = right_border
|
||||
self.border_top = top_border
|
||||
self.border_bottom = bottom_border
|
||||
self.border_left = left_border / video_preview_width
|
||||
self.border_right = right_border / video_preview_width
|
||||
self.border_top = top_border / video_preview_height
|
||||
self.border_bottom = bottom_border / video_preview_height
|
||||
self.original_width = width
|
||||
self.original_height = height
|
||||
self.is_vertical = width < height
|
||||
self.scaled_width = new_width
|
||||
self.scaled_height = new_height
|
||||
self.scaled_width = new_width / video_preview_width
|
||||
self.scaled_height = new_height / video_preview_height
|
||||
|
||||
return padded
|
||||
|
||||
|
||||
Reference in New Issue
Block a user