Qt跨屏窗口的一个Bug及解决方案

如果我们希望一个窗口覆盖用户的整个桌面,此时就要考虑用户有多个屏幕的场景(此窗口要横跨多个屏幕),由于每个屏幕的分辨率和缩放比例可能是不同的,Qt底层在为此窗口设置缩放比例(DevicePixelRatio)时出了问题。

复现环境

  • 屏幕A:最大可用分辨率:3840*2160

  • 屏幕A:当前设置分辨率:2048*1080

  • 屏幕A:缩放比例:100%

  • 屏幕B:最大可用分辨率:2560*1440

  • 屏幕B:当前设置分辨率:2560*1440

  • 屏幕B:缩放比例:125%

注:其他条件不变的情况下:只要屏幕A的当前设置分辨率比B小,均会出错;其他条件不变的情况下:只要屏幕A的当缩放比例与B不同,亦均会出错。

复现步骤

1、设置窗口跨屏的代码

cpp 复制代码
window->setFlags(window->flags() | Qt::FramelessWindowHint);
auto hwnd = (HWND)window->winId();
auto x = GetSystemMetrics(SM_XVIRTUALSCREEN);
auto y = GetSystemMetrics(SM_YVIRTUALSCREEN);
auto w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
auto h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
SetWindowPos(hwnd,HWND_TOP,   
    x, y,       
    w, h,       
    SWP_NOZORDER );
PostMessage(hwnd, WM_DISPLAYCHANGE, 0, 0);

2、为用户桌面拍照(把这个照片显示在窗口中,照片布满整个窗口,就能只管的看出此Bug)

cpp 复制代码
HDC hScreen = GetDC(NULL);
HDC hDC = CreateCompatibleDC(hScreen);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, w, h);
DeleteObject(SelectObject(hDC, hBitmap));
BOOL bRet = BitBlt(hDC, 0, 0, w, h, hScreen, x, y, SRCCOPY);
img = QImage(w, h, QImage::Format_ARGB32);
BITMAPINFO bmi = { sizeof(BITMAPINFOHEADER), (long)w, 0 - (long)h, 1, 32, BI_RGB, (DWORD)w * 4 * h, 0, 0, 0, 0 };
GetDIBits(hDC, hBitmap, 0, h, img.bits(), &bmi, DIB_RGB_COLORS);
DeleteDC(hDC);
DeleteObject(hBitmap);
ReleaseDC(NULL, hScreen);

注:这段代码中img就是拍照后得到的QImage对象,如何把图像渲染到窗口中的代码就不写了。

这是出错时的样子:

(如你所见,左边屏幕的内容已经侵入右边屏幕去了)

这是正常时的样子:

问题影响范围

这个问题自 Qt5.x.x 到前天刚发布的 Qt6.8.2 一直存在。

无论是 Qt Widgets 窗口还是 Qt Quick 窗口,都有这个问题。

解决方案(思路)

如果是 Qt Widgets窗口 ,那么你就直接用系统API来创建窗口:

cpp 复制代码
hwnd = CreateWindowEx(exStyle,
L"ScreenCapture", L"ScreenCapture",
style,x, y, w, h, 
NULL, NULL, hinstance, 
NULL);

然后把 QImage 渲染到这个原生窗口中:

cpp 复制代码
if (img.isNull()) return;
HDC hdc = GetDC(hwnd);
auto compDC = CreateCompatibleDC(NULL);
auto bitmap = CreateCompatibleBitmap(hdc, w, h);
DeleteObject(SelectObject(compDC, bitmap));

BITMAPINFO info = { sizeof(BITMAPINFOHEADER), w, 0 - h, 1, 32, BI_RGB, w * 4 * h, 0, 0, 0, 0 };
SetDIBits(hdc, bitmap, 0, h, img.bits(), &info, DIB_RGB_COLORS);
BLENDFUNCTION blend = { .BlendOp{AC_SRC_OVER}, .SourceConstantAlpha{255}, .AlphaFormat{AC_SRC_ALPHA} };
POINT pSrc = { 0, 0 };
SIZE sizeWnd = { w, h };
UpdateLayeredWindow(hwnd, hdc, NULL, &sizeWnd, compDC, &pSrc, NULL, &blend, ULW_ALPHA);
ReleaseDC(hwnd, hdc);

DeleteDC(compDC);
DeleteObject(bitmap);

然后所有的绘图操作都在这个QImage上进行。

如果是 Qt Quick 窗口,那么就要用 QQuickRenderControl 来把QML内容渲染到原生窗口中了,代码过于复杂,这里就不贴了,详情请参考:https://doc.qt.io/qt-6/qquickrendercontrol.html

总之:就是不要让Qt帮我创建窗口。

相关推荐
哟哟耶耶17 小时前
bug-Ant中a-select的placeholder不生效(绑定默认值为undefined)
bug
shanks6617 小时前
【Bug】causal_conv1d库的安装
bug
晓风伴月17 小时前
Trae AI 辅助修复uniapp 微信小程序的Bug
uni-app·bug·trae
loop lee17 小时前
【BUG】类文件具有错误的版本 61.0, 应为 52.0,请删除该文件或确保该文件位于正确的类路径子目录中。
bug
kunkun1013 天前
关于软件测试中的bug
python·bug·压力测试
用键盘当武器的秋刀鱼4 天前
springboot-bug
java·spring boot·bug
星辰&与海5 天前
报错 watcgdog: BUG; soft lockup -CPU#0 stuck for 26s! [swapper/0:1]
bug
无人等人5 天前
CyberRT(apollo) 定时器模块简述及bug分析
bug
fengdongnan5 天前
bug小记
bug
天才测试猿5 天前
解决Selenium元素拖拽不生效Bug
linux·自动化测试·软件测试·python·selenium·测试工具·bug