You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PySide6高DPI屏幕下TreeView行线条绘制缩放异常问题

TreeView高DPI屏幕下绘制线条宽度异常问题解决

问题描述

我编写了一段代码,用于在TreeView特定行的上边框绘制线条,代码如下:

class TextValidatorDelegate(QStyledItemDelegate):
   
    """a delegate to validate user input"""

    def __init__(self, parent = None):
        super().__init__(parent) 
        self.validator = QRegularExpressionValidator(QRegularExpression(R'[a-zA-Z0-9_]*'), self)

    #issue with hdpi screen
    def paint(self, painter, option, index):
        super().paint(painter, option, index)
        if index.data(customDataRole.WellRow):
            rect = option.rect
            painter.setPen(QPen())
            indent = rect.left() - 50 # -50 to draw line over the expand/collapse decorations of parent items
            painter.drawLine(indent, rect.top(), rect.right(), rect.top())
...

在高DPI屏幕上,当窗口缩放比例设置为非100%时,绘制的线条初始宽度为2px而非预期的1px;此外,当项目被hover或滚动时,线条宽度会在0px、1px、2px之间切换。

我尝试了多种解决方案但均无效:

  • 设置QApplication.setAttribute(Qt.AA_DisableHighDpiScaling)
  • 使用样式表:item{border : 1px solid;}
  • 设置环境变量os.environ["QT_SCALE_FACTOR"] = "1"
  • 使用配置文件

目前仅能通过Windows系统设置禁用缩放解决,想知道是否有其他绘制线条的方法可避免Windows对其进行缩放?或者能否通过代码阻止应用窗口被系统缩放?

解决方案

1. 基于设备像素比修正绘制逻辑

Qt在高DPI模式下会自动缩放逻辑坐标,直接使用像素值会导致绘制元素缩放异常。你需要通过**设备像素比(DPR)**调整画笔宽度和坐标,确保物理像素始终为1px:

def paint(self, painter, option, index):
    super().paint(painter, option, index)
    if index.data(customDataRole.WellRow):
        dpr = painter.device().devicePixelRatio()
        rect = option.rect
        # 设置画笔宽度为1/DPR,保证物理宽度为1px
        pen = QPen()
        pen.setWidthF(1 / dpr)
        painter.setPen(pen)
        # 转换缩进值为物理像素
        indent = rect.left() - 50 * dpr
        # 修正Y坐标,避免浮点精度问题导致的宽度波动
        top_y = rect.top() + 0.5 / dpr
        # 绘制线条时使用物理像素坐标
        painter.drawLine(indent, top_y, rect.right() * dpr, top_y)

这种方式让线条在任何缩放比例下都保持1px物理宽度,同时避免因坐标对齐偏差导致的宽度切换问题。

2. 通过Windows API强制禁用系统缩放

如果要彻底阻止Windows对应用窗口进行缩放,可以在创建QApplication实例前调用Windows系统API设置DPI感知模式:

import ctypes
# 设置进程为系统DPI感知,窗口将以原始像素尺寸显示,不被系统缩放
ctypes.windll.shcore.SetProcessDpiAwareness(1)
# 若需要适配多显示器不同DPI,可设置为每显示器DPI感知:
# ctypes.windll.shcore.SetProcessDpiAwareness(2)

注意:此API必须在QApplication()初始化前执行,否则无法生效。这种方式直接绕过系统缩放,从根源解决高DPI下的绘制问题,但窗口尺寸会固定为逻辑像素值,在高DPI屏幕上可能显示较小。

3. 利用Qt样式系统的像素转换

借助Qt样式的fromLogicalPixels方法,自动将逻辑像素转换为物理像素,适配高DPI环境:

def paint(self, painter, option, index):
    super().paint(painter, option, index)
    if index.data(customDataRole.WellRow):
        style = self.parent().style() if self.parent() else QApplication.style()
        rect = option.rect
        # 将逻辑像素转换为物理像素
        fixed_indent = style.fromLogicalPixels(50)
        pen_width = style.fromLogicalPixels(1)
        # 设置画笔并绘制线条
        pen = QPen()
        pen.setWidth(pen_width)
        painter.setPen(pen)
        painter.drawLine(rect.left() - fixed_indent, rect.top(), rect.right(), rect.top())

这种方式依赖Qt原生的高DPI适配逻辑,兼容性更好,适合跨平台场景。

内容的提问来源于stack exchange,提问作者Eric Bayard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 16:25:59