对于我的一个应用程序,我想要在Windows操作系统下定制一个窗口(如Firefox,Avast,Microsoft Word等)。因此,我重新实现了一些消息处理(
虽然它完美地工作,但当我的自定义窗口从一个屏幕转移到另一个屏幕时(我有两个屏幕),会出现视觉故障(如下图所示)。出现故障后,调整窗口大小可以修复该错误。此外,在进行一些调试后,我注意到Qt的
我的两个显示器都是高清的(1920x1080),因此不是分辨率差异引起的错误,但可能是DPI差异?当窗口在两个屏幕之间移动时(当窗口将窗口从一个屏幕传输到另一个屏幕时?),似乎会出现故障。
没有故障的窗口截图
窗口带有故障的截图
最初由
可以在这里找到BaseFramelessWindow类实现。这是一个Qt QMainWindow子类,重新实现了一些Win32 API本机事件(
因此,有人想知道如何避免这个错误吗?这是一个Qt错误吗?我尝试了很多东西,但没有真正起作用。我在互联网上找不到有关此问题的任何提及(也许我找得不好?)。
最小示例代码:
QWidget::nativeEvent()
)来自Win32 API,以保留AeroSnap和其他功能。虽然它完美地工作,但当我的自定义窗口从一个屏幕转移到另一个屏幕时(我有两个屏幕),会出现视觉故障(如下图所示)。出现故障后,调整窗口大小可以修复该错误。此外,在进行一些调试后,我注意到Qt的
QWidget::geometry()
与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类实现。这是一个Qt QMainWindow子类,重新实现了一些Win32 API本机事件(
QWidget::nativeEvent()
)。因此,有人想知道如何避免这个错误吗?这是一个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;
}
WM_NCCALCSIZE
消息发送到本地事件处理程序时,如果使用SWP_FRAMECHANGED
标志,则将向窗口发送WM_NCCALCSIZE
,因此窗口大小将被重新计算。无论如何,似乎这是一个与Qt相关的问题。 - HojjatJafaryQWindow::screenChanged
信号时调用SetWindowPos
。这也避免了屏幕更改后短暂显示黑色边框的问题。 - Everlight