【发布时间】:2020-06-06 08:32:08
【问题描述】:
对于我的一个应用程序,我想在 Windows 操作系统(如 Firefox、Avast、Microsoft Word 等)下定制一个窗口。所以我从 Win32 API 重新实现了一些消息处理 (QWidget::nativeEvent ()),以保留 AeroSnap 等的功能。
虽然它运行良好,但当我的自定义窗口从一个屏幕转移到另一个屏幕(我有两个屏幕)时,会出现视觉故障(如下所示)。故障出现后,调整窗口大小,纠正错误。而且,经过一些调试,我发现Qt QWidget::geometry()与出现bug时Win32 API GetWindowRect()返回的几何图形不一样。
我的两台显示器都是高清的(1920x1080,所以不是导致错误的分辨率差异,而是 DPI 差异?)。当窗口在两个屏幕之间移动时似乎会出现故障(当窗口将窗口从一个屏幕转移到另一个屏幕时?)。
没有故障的窗口屏幕截图
有故障的窗口的屏幕截图
QWidget::geometry() 最初报告的几何图形是 QRect(640,280 800x600),QWidget::frameGeometry() 是 QRect(640,280 800x600),Win32 GetWindowRect() 是 QRect(640,280 800x600)。因此,相同的几何形状。但是,在两个监视器之间移动窗口后,QWidget::geometry() 报告的几何图形变为QGeometry(1541,322 784x561)。 QWidget::frameGeometry() 或 GetWindowRect() 报告的几何形状保持不变。当然,在那之后,当窗口被调整大小时,几何图形被重新正确报告,并且绘画问题消失了。结论,当窗口在两个监视器之间移动时,Qt 似乎假设“出现”帧。
BaseFramelessWindow 类实现可以在here 中找到。这是一个 Qt QMainWindow 子类,重新实现了一些 Win32 API 原生事件 (QWidget::nativeEvent())。
那么有人会如何避免这个bug呢?这是一个Qt错误吗?我尝试了很多东西,但没有任何东西真正奏效。而且我在互联网上找不到任何关于这个问题的提及(也许我看起来很糟糕?)。
最小示例代码:
// main.cpp
#include "MainWindow.h"
#include <QtWidgets/qapplication.h>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
// MainWindow.h
#pragma once
#include <QtWidgets/qmainwindow.h>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = Q_NULLPTR);
protected:
void paintEvent(QPaintEvent* event) override;
bool nativeEvent(const QByteArray& eventType, void* message, long* result) override;
};
// MainWindow.cpp
#include "MainWindow.h"
#include <QtGui/qpainter.h>
#include <Windows.h>
#include <Windowsx.h>
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
{
::SetWindowLongPtr((HWND)winId(), GWL_STYLE, WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX);
}
void MainWindow::paintEvent(QPaintEvent* event)
{
QPainter painter(this);
// Using GetWindowRect() instead of rect() seems to be a valid
// workaround for the painting issue. However many other things
// do not work cause of the bad geometry reported by Qt.
RECT winrect;
GetWindowRect(reinterpret_cast<HWND>(winId()), &winrect);
QRect rect = QRect(0, 0, winrect.right - winrect.left, winrect.bottom - winrect.top);
// Background
painter.fillRect(rect, Qt::red);
// Border
painter.setBrush(Qt::NoBrush);
painter.setPen(QPen(Qt::blue, 1));
painter.drawRect(rect.adjusted(0, 0, -1, -1));
// Title bar
painter.fillRect(QRect(1, 1, rect.width() - 2, 19), Qt::yellow);
}
bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, long* result)
{
MSG* msg = reinterpret_cast<MSG*>(message);
switch (msg->message)
{
case WM_NCCALCSIZE:
*result = 0;
return true;
case WM_NCHITTEST: {
*result = 0;
RECT winrect;
GetWindowRect(reinterpret_cast<HWND>(winId()), &winrect);
// Code allowing to resize the window with the mouse is omitted.
long x = GET_X_LPARAM(msg->lParam);
long y = GET_Y_LPARAM(msg->lParam);
if (x > winrect.left&& x < winrect.right && y > winrect.top&& y < winrect.top + 20) {
// To allow moving the window.
*result = HTCAPTION;
return true;
}
repaint();
return false;
}
default:
break;
}
return false;
}
【问题讨论】: