11import cv2
2- from PySide6 .QtWidgets import QWidget , QVBoxLayout , QLabel
3- from PySide6 .QtCore import Qt , Signal , QRect , QRectF
2+ from PySide6 .QtWidgets import QWidget , QVBoxLayout , QLabel , QSizePolicy
3+ from PySide6 .QtCore import Qt , Signal , QRect , QRectF , QTimer , QObject , QEvent
44from PySide6 import QtCore , QtWidgets , QtGui
55from qfluentwidgets import qconfig , CardWidget , HollowHandleStyle
66
@@ -22,6 +22,7 @@ def __init__(self, parent=None):
2222 self .drag_start_pos = None
2323 self .resize_edge = None
2424 self .edge_size = 10 # 调整大小的边缘区域
25+ self .enable_mouse_events = True # 控制是否启用鼠标事件
2526
2627 # 获取屏幕大小
2728 screen = QtWidgets .QApplication .primaryScreen ().size ()
@@ -45,7 +46,7 @@ def __init__(self, parent=None):
4546
4647 # 保存选择框的相对位置和大小(相对于实际视频的比例)
4748 self .selection_ratio = None
48-
49+
4950 self .__initWidget ()
5051
5152 def __initWidget (self ):
@@ -68,7 +69,8 @@ def __initWidget(self):
6869 self .black_container .setStyleSheet ("""
6970 #blackContainer {
7071 background-color: black;
71- border-radius: 8px;
72+ border-radius: 10px;
73+ border: 0px solid transparent;
7274 }
7375 """ )
7476 black_layout = QVBoxLayout ()
@@ -80,11 +82,15 @@ def __initWidget(self):
8082 self .video_display = QtWidgets .QLabel ()
8183 self .video_display .setStyleSheet ("""
8284 background-color: black;
83- border-top-left-radius: 8px;
84- border-top-right-radius: 8px;
85+ border-top-left-radius: 10px;
86+ border-top-right-radius: 10px;
87+ border: 0px solid transparent;
8588 """ )
86- self .video_display .setFixedSize (self .video_preview_width , self .video_preview_height )
89+ self .video_display .setMinimumSize (self .video_preview_width , self .video_preview_height )
90+
8791 self .video_display .setMouseTracking (True )
92+ self .video_display .setScaledContents (True )
93+ self .video_display .setAlignment (Qt .AlignCenter )
8894 self .video_display .mousePressEvent = self .selection_mouse_press
8995 self .video_display .mouseMoveEvent = self .selection_mouse_move
9096 self .video_display .mouseReleaseEvent = self .selection_mouse_release
@@ -104,8 +110,30 @@ def __initWidget(self):
104110
105111 # 视频预览区域
106112 self .video_display .setObjectName ('videoDisplay' )
107- black_layout .addWidget (self .video_display , 0 , Qt .AlignCenter )
108-
113+ # black_layout.addWidget(self.video_display, 0, Qt.AlignCenter)
114+ # 创建一个容器来保持比例
115+ ratio_container = QWidget ()
116+ ratio_layout = QVBoxLayout (ratio_container )
117+ ratio_layout .setContentsMargins (0 , 0 , 0 , 0 )
118+ ratio_layout .addWidget (self .video_display )
119+
120+ # 设置固定的宽高比
121+ ratio_container .setFixedHeight (ratio_container .width () * 9 // 16 )
122+ ratio_container .setMinimumWidth (self .video_preview_width )
123+
124+ # 添加到布局
125+ black_layout .addWidget (ratio_container )
126+
127+ # 添加一个事件过滤器来处理大小变化
128+ class RatioEventFilter (QObject ):
129+ def eventFilter (self , obj , event ):
130+ if event .type () == QEvent .Resize :
131+ obj .setFixedHeight (obj .width () * 9 // 16 )
132+ return False
133+
134+ ratio_filter = RatioEventFilter (ratio_container )
135+ ratio_container .installEventFilter (ratio_filter )
136+
109137 # 进度条和滑块容器
110138 control_container = QWidget (self )
111139 control_layout = QVBoxLayout ()
@@ -125,7 +153,13 @@ def __initWidget(self):
125153 self .video_container .setLayout (video_layout )
126154 main_layout .addWidget (self .video_container )
127155
128- def update_video_display (self , frame ):
156+ def update_video_display (self , frame , draw_selection = True ):
157+ """更新视频显示"""
158+ if frame is None :
159+ return
160+
161+ # 调整视频帧大小以适应视频预览区域
162+ frame = cv2 .resize (frame , (self .video_preview_width , self .video_preview_height ))
129163 # 将 OpenCV 帧(BGR 格式)转换为 QImage 并显示在 QLabel 上
130164 rgb_frame = cv2 .cvtColor (frame , cv2 .COLOR_BGR2RGB )
131165 h , w , ch = rgb_frame .shape
@@ -139,6 +173,7 @@ def update_video_display(self, frame):
139173
140174 painter = QtGui .QPainter (rounded_pix )
141175 painter .setRenderHint (QtGui .QPainter .Antialiasing ) # 抗锯齿
176+ painter .setRenderHint (QtGui .QPainter .SmoothPixmapTransform , True )
142177
143178 # 创建圆角路径
144179 path = QtGui .QPainterPath ()
@@ -165,7 +200,7 @@ def update_video_display(self, frame):
165200 self .video_display .setPixmap (rounded_pix )
166201
167202 # 如果有保存的选择框比例,根据新视频尺寸重新计算选择框
168- if self .selection_ratio is not None and self .scaled_width and self .scaled_height :
203+ if draw_selection and self .selection_ratio is not None and self .scaled_width and self .scaled_height :
169204 x_ratio , y_ratio , w_ratio , h_ratio = self .selection_ratio
170205
171206 # 计算新的选择框坐标和大小
@@ -207,12 +242,37 @@ def update_preview_with_rect(self, rect=None):
207242
208243 def selection_mouse_press (self , event ):
209244 """鼠标按下事件处理"""
245+ if not self .enable_mouse_events :
246+ return
210247 pos = event .pos ()
248+
249+ # 检测双击或三击,重置选择框
250+ if event .type () == QtCore .QEvent .MouseButtonDblClick :
251+ self .selection_rect = QRect (pos , pos )
252+ self .resize_edge = None
253+ self .is_drawing = True
254+ self .drag_start_pos = pos
255+ return
211256
212257 # 检查是否在选择框边缘(用于调整大小)
213258 if self .selection_rect .isValid ():
259+ # 右下角
260+ if abs (pos .x () - self .selection_rect .right ()) <= self .edge_size and abs (pos .y () - self .selection_rect .bottom ()) <= self .edge_size :
261+ self .resize_edge = "bottomright"
262+ self .drag_start_pos = pos
263+ return
264+ # 右上角
265+ elif abs (pos .x () - self .selection_rect .right ()) <= self .edge_size and abs (pos .y () - self .selection_rect .top ()) <= self .edge_size :
266+ self .resize_edge = "topright"
267+ self .drag_start_pos = pos
268+ return
269+ # 左下角
270+ elif abs (pos .x () - self .selection_rect .left ()) <= self .edge_size and abs (pos .y () - self .selection_rect .bottom ()) <= self .edge_size :
271+ self .resize_edge = "bottomleft"
272+ self .drag_start_pos = pos
273+ return
214274 # 左边缘
215- if abs (pos .x () - self .selection_rect .left ()) <= self .edge_size and self .selection_rect .top () <= pos .y () <= self .selection_rect .bottom ():
275+ elif abs (pos .x () - self .selection_rect .left ()) <= self .edge_size and self .selection_rect .top () <= pos .y () <= self .selection_rect .bottom ():
216276 self .resize_edge = "left"
217277 self .drag_start_pos = pos
218278 return
@@ -236,21 +296,6 @@ def selection_mouse_press(self, event):
236296 self .resize_edge = "topleft"
237297 self .drag_start_pos = pos
238298 return
239- # 右上角
240- elif abs (pos .x () - self .selection_rect .right ()) <= self .edge_size and abs (pos .y () - self .selection_rect .top ()) <= self .edge_size :
241- self .resize_edge = "topright"
242- self .drag_start_pos = pos
243- return
244- # 左下角
245- elif abs (pos .x () - self .selection_rect .left ()) <= self .edge_size and abs (pos .y () - self .selection_rect .bottom ()) <= self .edge_size :
246- self .resize_edge = "bottomleft"
247- self .drag_start_pos = pos
248- return
249- # 右下角
250- elif abs (pos .x () - self .selection_rect .right ()) <= self .edge_size and abs (pos .y () - self .selection_rect .bottom ()) <= self .edge_size :
251- self .resize_edge = "bottomright"
252- self .drag_start_pos = pos
253- return
254299 # 在选择框内部(用于移动)
255300 elif self .selection_rect .contains (pos ):
256301 self .resize_edge = "move"
@@ -265,6 +310,8 @@ def selection_mouse_press(self, event):
265310
266311 def selection_mouse_move (self , event ):
267312 """鼠标移动事件处理"""
313+ if not self .enable_mouse_events :
314+ return
268315 pos = event .pos ()
269316
270317 # 根据不同的操作模式处理鼠标移动
@@ -276,7 +323,40 @@ def selection_mouse_move(self, event):
276323 # 移动整个选择框
277324 dx = pos .x () - self .drag_start_pos .x ()
278325 dy = pos .y () - self .drag_start_pos .y ()
279- self .selection_rect .translate (dx , dy )
326+
327+ # 保存原始选择框尺寸
328+ original_width = self .selection_rect .width ()
329+ original_height = self .selection_rect .height ()
330+
331+ # 计算新位置
332+ new_rect = self .selection_rect .translated (dx , dy )
333+
334+ # 获取视频显示区域
335+ display_rect = self .video_display .rect ()
336+
337+ # 检查是否超出边界,如果超出则调整位置但保持尺寸
338+ if new_rect .left () < 0 :
339+ new_rect .moveLeft (0 )
340+ if new_rect .top () < 0 :
341+ new_rect .moveTop (0 )
342+ if new_rect .right () > display_rect .width ():
343+ new_rect .moveRight (display_rect .width ())
344+ if new_rect .bottom () > display_rect .height ():
345+ new_rect .moveBottom (display_rect .height ())
346+
347+ # 确保尺寸不变
348+ if new_rect .width () != original_width or new_rect .height () != original_height :
349+ # 如果尺寸变了,恢复原始尺寸
350+ if new_rect .left () == 0 :
351+ new_rect .setWidth (original_width )
352+ if new_rect .top () == 0 :
353+ new_rect .setHeight (original_height )
354+ if new_rect .right () == display_rect .width ():
355+ new_rect .setLeft (new_rect .right () - original_width )
356+ if new_rect .bottom () == display_rect .height ():
357+ new_rect .setTop (new_rect .bottom () - original_height )
358+
359+ self .selection_rect = new_rect
280360 self .drag_start_pos = pos
281361 else :
282362 # 调整选择框大小
@@ -288,25 +368,27 @@ def selection_mouse_move(self, event):
288368 self .selection_rect .setTop (pos .y ())
289369 if "bottom" in self .resize_edge :
290370 self .selection_rect .setBottom (pos .y ())
291-
292- # 确保选择框在视频显示区域内
293- display_rect = self .video_display .rect ()
294- if self .selection_rect .left () < 0 :
295- self .selection_rect .setLeft (0 )
296- if self .selection_rect .top () < 0 :
297- self .selection_rect .setTop (0 )
298- if self .selection_rect .right () > display_rect .width ():
299- self .selection_rect .setRight (display_rect .width ())
300- if self .selection_rect .bottom () > display_rect .height ():
301- self .selection_rect .setBottom (display_rect .height ())
302371
372+ # 确保选择框在视频显示区域内
373+ display_rect = self .video_display .rect ()
374+ if self .selection_rect .left () < 0 :
375+ self .selection_rect .setLeft (0 )
376+ if self .selection_rect .top () < 0 :
377+ self .selection_rect .setTop (0 )
378+ if self .selection_rect .right () > display_rect .width ():
379+ self .selection_rect .setRight (display_rect .width ())
380+ if self .selection_rect .bottom () > display_rect .height ():
381+ self .selection_rect .setBottom (display_rect .height ())
382+
303383 self .update_preview_with_rect ()
304384 else :
305385 # 更新鼠标指针形状
306386 self .update_cursor_shape (pos )
307387
308388 def selection_mouse_release (self , event ):
309389 """鼠标释放事件处理"""
390+ if not self .enable_mouse_events :
391+ return
310392 # 结束绘制或调整
311393 self .is_drawing = False
312394 self .resize_edge = None
@@ -390,7 +472,18 @@ def load_selection_ratio(self):
390472 # 检查配置值是否有效
391473 if x_ratio is None or y_ratio is None or w_ratio is None or h_ratio is None :
392474 return False
393-
475+
476+ # 检查配置值是否在有效范围内
477+ if w_ratio <= 0.01 or h_ratio <= 0.005 :
478+ config .set (config .subtitleSelectionAreaX , config .subtitleSelectionAreaX .defaultValue )
479+ config .set (config .subtitleSelectionAreaY , config .subtitleSelectionAreaY .defaultValue )
480+ config .set (config .subtitleSelectionAreaW , config .subtitleSelectionAreaW .defaultValue )
481+ config .set (config .subtitleSelectionAreaH , config .subtitleSelectionAreaH .defaultValue )
482+ x_ratio = config .subtitleSelectionAreaX .value
483+ y_ratio = config .subtitleSelectionAreaY .value
484+ w_ratio = config .subtitleSelectionAreaW .value
485+ h_ratio = config .subtitleSelectionAreaH .value
486+
394487 # 保存选择框比例
395488 self .selection_ratio = (x_ratio , y_ratio , w_ratio , h_ratio )
396489
@@ -464,4 +557,10 @@ def get_original_coordinates(self):
464557 ymin = max (0 , min (ymin , self .frame_height ))
465558 ymax = max (0 , min (ymax , self .frame_height ))
466559
467- return (ymin , ymax , xmin , xmax )
560+ return (ymin , ymax , xmin , xmax )
561+
562+ def set_dragger_enabled (self , enabled ):
563+ """设置拖动器是否可用"""
564+ self .enable_mouse_events = enabled
565+ self .video_display .setMouseTracking (enabled )
566+ self .video_display .setCursor (Qt .ArrowCursor )
0 commit comments