Для одного из моих приложений я хотел настроить окно под операционную систему Windows (например, Firefox, Avast, Microsoft Word и др. c.). Поэтому я переопределил обработку некоторых сообщений (QWidget::nativeEvent ()
) из Win32 API, чтобы сохранить функциональность AeroSnap и других.
Хотя это прекрасно работает, когда мое пользовательское окно переносится с одного экрана на другой ( У меня два экрана), появляется визуальный сбой (как вы можете видеть ниже). После появления глюка, изменяя размеры окна, исправьте ошибку. Более того, после некоторой отладки я заметил, что Qt QWidget::geometry()
не совпадает с геометрией, возвращаемой Win32 API GetWindowRect()
при появлении ошибки.
Оба моих монитора HD (1920x1080, поэтому не разница в разрешениях, которые вызывают ошибку, но, может быть, разница в DPI?). Кажется, что появляется сбой, когда окно перемещается между двумя экранами (когда windows переносит окно с одного экрана на другой?).
Снимок экрана окна без сбоев
Снимок экрана с глюком
Геометрия, первоначально сообщаемая 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? Я перепробовал много вещей, и ничего не получилось. И я не могу найти упоминания об этой проблеме в inte rnet (может, я плохо выгляжу?).
Минимальный пример кода:
// 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;
}