QT中用GDI+绘制鼠标移动时的线段,当屏幕显示设置缩放比例为100%,直线段的起始位置和鼠标按下和移动相同,但缩放比例部位100%时,绘出的线段和鼠标位置有差异,是什么原因?

zxphxh 2024-07-25 11:34:15

QT中用GDI+绘制鼠标移动时的线段,当屏幕显示设置缩放比例为100%,直线段的起始位置和鼠标按下和移动相同,但缩放比例部位100%时,绘出的线段和鼠标位置有差异,且缩放比例越大,位置差别也越大。是什么原因?用VS2022和Gdi+绘图就没有此现象。

...全文
584 5 打赏 收藏 转发到动态 举报
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复

在QT中使用GDI+绘制鼠标移动时的线段,遇到屏幕缩放比例不为100%时绘出的线段和鼠标位置有差异的问题,主要原因涉及屏幕缩放比例(DPI缩放)和坐标映射。

一、原因分析

  1. 鼠标事件坐标与绘图坐标体系差异
    • 在QT中,鼠标事件的坐标是基于逻辑坐标系的。当系统缩放比例改变时,这些逻辑坐标与GDI+绘图基于的物理像素坐标之间就可能出现不一致。例如,当缩放比例为150%时,QT中的逻辑坐标会按此比例缩放,而GDI+默认使用物理像素坐标,这就导致了坐标的差异。
    • VS2022中的GDI+绘图直接基于屏幕的物理像素坐标,没有类似QT中的逻辑坐标调整机制,所以不会出现这种位置偏移现象。

      配图

  2. QT的高DPI支持影响
    • QT默认支持高DPI缩放,像QGuiApplication::highDpiScaleFactorRoundingPolicy和devicePixelRatio等属性会控制这种缩放。当缩放比例变化时,QT的逻辑坐标会相应调整,但GDI+没有自动适配这种调整。
  3. 设备上下文(DC)相关差异
    • QT在处理设备上下文和GDI+绘图的结合方式下,设备上下文会受到QT显示系统设置(如缩放)的影响。其映射模式或者坐标转换可能导致GDI+绘制的线段起点和终点坐标被错误转换。

二、解决方案及相关书籍推荐

配图

  1. 《C++ GUI Programming with Qt 4》
    • 作者:Jasmin Blanchette、Mark Summerfield。
    • 内容特点
      • 这本书全面涵盖了Qt 4的相关知识,包括QT的核心概念、信号与槽机制、布局管理等。对于理解QT的事件处理机制(如鼠标事件)有详细的讲解。书中有很多示例代码,可以帮助读者深入理解如何在实际项目中处理QT的各种功能。
      • 优点:内容详细、全面,是学习QT的经典书籍。示例丰富,适合初学者逐步深入学习QT编程。

        配图

      • 缺点:由于是针对Qt 4版本,部分新特性(如一些新的高DPI处理方式在后续版本中的改进)可能未涉及。
  2. 《GDI+ Programming Guide》
    • 作者:Mahmoud Elkoush。
    • 内容特点
      • 专门针对GDI+编程的指南,详细介绍了GDI+的各种绘图功能,包括图形绘制、图像处理等。对于理解GDI+的坐标系统、绘图模式设置等内容非常有帮助。
      • 优点:专注于GDI+,能深入地学习GDI+的各种特性。对GDI+的原理讲解透彻。
      • 缺点:缺乏与QT结合使用的具体案例讲解,需要在掌握QT的基础上阅读才能更好地应用于QT中的GDI+绘图问题。
  3. 《Mastering Qt 5》
    • 作者:Guillaume Lazar、Robin Penea。
    • 内容特点
      • 涵盖了Qt 5的新特性和改进之处,包括对高DPI支持的更深入讲解。书中有很多关于如何在不同平台和场景下优化QT应用程序的策略。
      • 优点:紧跟Qt的发展,对Qt 5的新特性把握准确。有助于读者了解最新的QT技术,特别是在处理屏幕缩放等问题上有很好的参考价值。
      • 缺点:对于只熟悉旧版本QT的读者,可能需要一定的过渡学习。
推荐书籍图书特点
《C++ GUI Programming with Qt 4》作者:Jasmin Blanchette、Mark Summerfield,全面涵盖Qt 4知识,有大量示例代码,适合初学者深入学习QT编程,但未涉及部分新特性
《GDI+ Programming Guide》作者:Mahmoud Elkoush,专门针对GDI+编程,详细讲解其绘图功能和原理,但缺乏与QT结合的案例
《Mastering Qt 5》作者:Guillaume Lazar、Robin Penea,涵盖Qt 5新特性,对高DPI支持讲解深入,适合了解最新QT技术,但对旧版本QT读者可能有学习门槛

已隐藏部分内容,更多查看原文

  • 打赏
  • 举报
回复

坐标转换问题
在 QT 中,当屏幕缩放比例不是 100% 时,鼠标坐标和绘图坐标之间的转换可能出现了问题。QT 可能会对窗口坐标进行缩放调整,而 GDI + 绘制时如果没有正确考虑这种缩放因素,就会导致绘制位置与鼠标位置不匹配。
当缩放比例改变时,QT 的窗口坐标系可能会根据缩放因子进行相应的变换,例如,在高缩放比例下,实际的物理像素和逻辑像素之间的映射关系发生了变化。而在 VS2022 中可能不存在这种自动的缩放调整或者其坐标转换机制与 QT 不同。
设备上下文(DC)相关差异
QT 在处理设备上下文和 GDI + 绘图的结合方式可能与 VS2022 有所不同。在 QT 环境下,设备上下文可能会受到 QT 的显示系统设置(如缩放)的影响。
当缩放比例改变时,QT 设备上下文的映射模式或者坐标转换可能导致 GDI + 绘制的线段起点和终点坐标被错误地转换。而在 VS2022 中,可能直接使用了 GDI + 的原始坐标系统,没有受到类似 QT 中额外的显示缩放因素的干扰。
事件处理和坐标获取差异
QT 的鼠标事件处理机制可能在获取鼠标位置时,返回的是经过 QT 内部缩放处理后的坐标。而在绘制过程中,如果没有按照相同的缩放规则将这些坐标转换为 GDI + 绘图可用的坐标,就会出现偏差。
相比之下,VS2022 中的鼠标事件坐标和 GDI + 绘图坐标之间可能有更直接的对应关系,没有经过像 QT 这样复杂的缩放调整,所以不会出现位置差异的现象。

我喜欢就喜欢 2024-12-12
  • 打赏
  • 举报
回复

在使用 Qt 中结合 GDI+ 绘制鼠标移动时的线段时,出现这种现象的主要原因是 屏幕缩放比例(DPI 缩放) 和 坐标映射 问题。屏幕缩放比例影响了应用程序的逻辑坐标和实际显示坐标之间的映射关系。如果处理不当,就会导致鼠标事件的坐标与绘图输出之间存在偏差,缩放比例越大,偏差也就越明显。

下面分析问题产生的具体原因以及解决方法:

原因分析
鼠标事件的坐标未考虑 DPI 缩放 在 Qt 中,鼠标事件的坐标(如 QMouseEvent::pos() 或 QMouseEvent::globalPos())是基于逻辑坐标系的。当系统的缩放比例不是 100% 时,这些坐标与 GDI+ 绘图的物理坐标(屏幕像素坐标)之间可能不一致。GDI+ 默认使用物理像素坐标,这导致了坐标差异。

Qt 默认支持高 DPI 缩放 Qt 的高 DPI 支持(由 QGuiApplication::highDpiScaleFactorRoundingPolicy 和 devicePixelRatio 控制)会根据系统缩放比例自动调整坐标系。例如,当缩放比例为 150% 时,Qt 中的逻辑坐标可能会自动缩放到 DPI 缩放后的比例,而 GDI+ 没有自动适配。

VS2022 中的绘图与 GDI+ VS2022 中的 GDI+ 绘图直接基于屏幕的物理像素坐标,而不涉及 Qt 的逻辑坐标调整,因此不会产生这种坐标偏移现象。

解决方案
获取正确的鼠标坐标 将鼠标事件的逻辑坐标转换为物理像素坐标,确保传递给 GDI+ 的是正确的坐标。可以通过以下方法获取物理坐标:
QPoint logicalPos = event->pos(); // Qt 提供的逻辑坐标
QPointF physicalPos = logicalPos * devicePixelRatioF(); // 转换为物理像素坐标
使用 QWidget::devicePixelRatioF() 或 QScreen::devicePixelRatio() 获取设备的缩放系数。

适配 GDI+ 坐标系统 调整 GDI+ 的绘图模式,使其匹配 Qt 的逻辑坐标。例如,设置 GDI+ 的变换矩阵:
Graphics graphics(hdc);
float scaleFactor = devicePixelRatioF();
graphics.ScaleTransform(scaleFactor, scaleFactor);
这样,GDI+ 的坐标系统会与 Qt 的逻辑坐标保持一致。

禁用 Qt 的高 DPI 支持(仅作为测试) 如果需要排查问题,可以暂时禁用 Qt 的高 DPI 支持:

QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
注意:禁用高 DPI 支持可能导致程序在高分辨率屏幕上显示不清晰。

校准绘图区域的坐标系统 如果使用 QPaintDevice 的 HDC,确保在绘图前设置正确的 DPI 缩放。例如:
HDC hdc = getDCForWidget(myWidget);
SetMapMode(hdc, MM_ANISOTROPIC);
SetWindowExtEx(hdc, logicalWidth, logicalHeight, nullptr);
SetViewportExtEx(hdc, physicalWidth, physicalHeight, nullptr);
示例代码
以下是修正代码的简单示例

void MyWidget::mouseMoveEvent(QMouseEvent *event) {
// 获取逻辑坐标
QPoint logicalPos = event->pos();

// 转换为物理坐标
QPointF physicalPos = logicalPos * devicePixelRatioF();

// 使用 GDI+ 绘图
HDC hdc = GetDC(reinterpret_cast<HWND>(winId()));
Graphics graphics(hdc);
Pen pen(Color(255, 0, 0), 2);
graphics.DrawLine(&pen, Gdiplus::PointF(startPos.x(), startPos.y()), 
                  Gdiplus::PointF(physicalPos.x(), physicalPos.y()));
ReleaseDC(reinterpret_cast<HWND>(winId()), hdc);

}
总结
问题的根源在于 Qt 的逻辑坐标和 GDI+ 的物理像素坐标未对齐。通过正确获取 DPI 缩放系数并在两者之间进行转换,可以解决鼠标位置与绘图结果之间的偏差问题。

深东编程 2024-07-28
  • 打赏
  • 举报
回复

应该是缩放代码出了问题 微软系统显卡输出比例不同大小也不同。比如说100%是10901280 150%就变成了1020720

zxphxh 2024-07-25
  • 打赏
  • 举报
回复

// 这是代码。
// 在屏幕缩放比例为100%时,正常,大于100%时,位置有差异。不知道为何?
void MyWidget::mousePressEvent(QMouseEvent *event)
{
// 鼠标左键按下
if(event->button() == Qt::LeftButton)
{
startpos = event->pos();
endpos = startpos;
}
}

// 鼠标移动事件
void MyWidget::mouseMoveEvent(QMouseEvent *event)
{
if(event->buttons() & Qt::LeftButton)
{
endpos = event->pos();
this->update();
}
}

// 重绘事件
void MyWidget::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);

HWND hwnd = (HWND)this->winId();
HDC hdc   = GetDC(hwnd);
 

int nWidth = this->geometry().width();
int nHeight = this->geometry().height();

HDC memDC = CreateCompatibleDC(hdc);
HGDIOBJ memBmp = CreateCompatibleBitmap(hdc,nWidth, nHeight);
HGDIOBJ oldBmp = SelectObject(memDC, memBmp);
PatBlt(memDc, 0, 0, nWidth, nHeight, WHITENESS);//背景白色
DrawLine(memDC); 
BitBlt(hdc, 0, 0, nWidth, nHeight, memDC, 0, 0, SRCCOPY); 
SelectObject(memDC, oldBmp); 
DeleteObject(memBmp);
DeleteDC(memDC);
ReleaseDC(hwnd, hdc); 

}

void MyWidget::DrawLine(HDC hdc)
{
Gdiplus::Graphics graphics(hdc);
graphics.SetPageUnit(Gdiplus::UnitPixel);
graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
Gdiplus::Pen pen(Gdiplus::Color(255, 255, 0, 0), 1);
graphics.DrawLine(&pen, startpos.x(), startpos.y(), endpos.x(), endpos.y());
}

16,759

社区成员

发帖
与我相关
我的任务
社区描述
Qt 是一个跨平台应用程序框架。通过使用 Qt,您可以一次性开发应用程序和用户界面,然后将其部署到多个桌面和嵌入式操作系统,而无需重复编写源代码。
社区管理员
  • Qt
  • 亭台六七座
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧