diff --git a/ui/views/system_tools/screen_capture.py b/ui/views/system_tools/screen_capture.py index 8d8ca07..ea567b9 100644 --- a/ui/views/system_tools/screen_capture.py +++ b/ui/views/system_tools/screen_capture.py @@ -74,13 +74,44 @@ class ScreenCaptureOverlay(QtWidgets.QWidget): self.activateWindow() self.raise_() + @staticmethod + def _scale_rect(rect, logical_size, native_size): + """把 Qt 逻辑坐标选区映射到抓屏位图的原生像素坐标。""" + if (logical_size.width() <= 0 or logical_size.height() <= 0 + or native_size.width() <= 0 or native_size.height() <= 0): + return QtCore.QRect() + + logical_bounds = QtCore.QRect(QtCore.QPoint(), logical_size) + clipped = rect.normalized().intersected(logical_bounds) + if clipped.isEmpty(): + return QtCore.QRect() + + scale_x = native_size.width() / logical_size.width() + scale_y = native_size.height() / logical_size.height() + left = round(clipped.x() * scale_x) + top = round(clipped.y() * scale_y) + right = round((clipped.x() + clipped.width()) * scale_x) + bottom = round((clipped.y() + clipped.height()) * scale_y) + + left = max(0, min(native_size.width(), left)) + top = max(0, min(native_size.height(), top)) + right = max(left, min(native_size.width(), right)) + bottom = max(top, min(native_size.height(), bottom)) + return QtCore.QRect(left, top, right - left, bottom - top) + + def _native_rect(self, rect): + """返回选区在当前抓屏位图中的原生像素矩形。""" + if self._full_pixmap is None or self._full_pixmap.isNull(): + return QtCore.QRect() + return self._scale_rect(rect, self.size(), self._full_pixmap.size()) + def paintEvent(self, event): if not self._full_pixmap: return painter = QtGui.QPainter(self) # 1. 绘制屏幕截图作为背景 - painter.drawPixmap(0, 0, self._full_pixmap) + painter.drawPixmap(self.rect(), self._full_pixmap, self._full_pixmap.rect()) # 2. 半透明遮罩 painter.fillRect(self.rect(), QtGui.QColor(0, 0, 0, 100)) @@ -92,15 +123,17 @@ class ScreenCaptureOverlay(QtWidgets.QWidget): rect = self._current_rect if rect.width() > 0 and rect.height() > 0: + native_rect = self._native_rect(rect) # 3. 选区内重绘原图(去掉遮罩,形成高亮效果) - painter.drawPixmap(rect, self._full_pixmap, rect) + painter.drawPixmap(rect, self._full_pixmap, native_rect) # 4. 蓝色边框 pen = QtGui.QPen(QtGui.QColor(0, 120, 215), 2) painter.setPen(pen) painter.setBrush(QtCore.Qt.BrushStyle.NoBrush) painter.drawRect(rect) # 5. 尺寸标注 - size_text = f"{rect.width()} x {rect.height()}" + # 标签显示最终文件的真实像素,而不是受系统缩放影响的逻辑尺寸。 + size_text = f"{native_rect.width()} x {native_rect.height()} px" font = painter.font() font.setPointSize(9) painter.setFont(font) @@ -177,15 +210,12 @@ class ScreenCaptureOverlay(QtWidgets.QWidget): def _on_confirm(self): """确认截图:裁剪并发射信号""" if self._full_pixmap and self._current_rect.width() > 5 and self._current_rect.height() > 5: - dpr = self._full_pixmap.devicePixelRatio() - phys_rect = QtCore.QRect( - int(self._current_rect.x() * dpr), - int(self._current_rect.y() * dpr), - int(self._current_rect.width() * dpr), - int(self._current_rect.height() * dpr), - ) - captured = self._full_pixmap.toImage().copy(phys_rect) - self.screenshot_captured.emit(captured) + native_rect = self._native_rect(self._current_rect) + if not native_rect.isEmpty(): + captured = self._full_pixmap.toImage().copy(native_rect) + # 截图是普通位图;避免下游再次按桌面 DPR 缩小显示。 + captured.setDevicePixelRatio(1.0) + self.screenshot_captured.emit(captured) self.close() def _on_cancel(self):