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
相关产品推荐
相关产品推荐

