I'm aware that have some similar questions about this topic, but I couldn't find one relative to a window being created using Qt.
I tried all suggestions in this answer: How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?
- Modifying
WM_WINDOWPOSCHANGING
flag toSWP_NOCOPYBITS
wp = reinterpret_cast<tagWINDOWPOS*>(msg->lParam);
wp->flags |= SWP_NOCOPYBITS;
- Returning
WVR_VALIDRECTS
inWM_NCCALCSIZE
if (msg->wParam == 1)
return WVR_VALIDRECTS;
- Applying
CS_HREDRAW | CS_VREDRAW
styles to the window
SetWindowLongPtr(HWND(winId()), GWL_STYLE, CS_HREDRAW | CS_VREDRAW | WS_POPUPWINDOW | WS_CAPTION | WS_SIZEBOX | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_CLIPCHILDREN);
- Apply the
exstyle
WS_EX_COMPOSITED
WM_ERASEBKGND
to return 1;
But the flicker persists. What else I could try?
The goal is to create a window without a caption, that can be resized/minimized.
The code below is working the problem is when the window is being resized from left/top it causes flickering.
//.h
#include <QtWidgets/QMainWindow>
#include <Windows.h>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void Frameless()
{
// set flags that will override what Qt does, especially with the Qt::FramelessWindowHint which essentially disables WS_SIZEBOX/WS_THICKFRAME
SetWindowLongPtr(HWND(winId()), GWL_STYLE, WS_POPUPWINDOW | WS_CAPTION | WS_SIZEBOX | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_CLIPCHILDREN);
}
private:
Ui::MainWindowClass ui;
protected:
virtual bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override;
};
//.cpp
#include "MainWindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
Frameless();
return;
}
MainWindow::~MainWindow()
{}
bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr* result)
{
MSG* msg = static_cast<MSG*>(message);
switch (msg->message)
{
case WM_WINDOWPOSCHANGING:
{
tagWINDOWPOS* wp;
wp = reinterpret_cast<tagWINDOWPOS*>(msg->lParam);
wp->flags |= SWP_NOCOPYBITS;
}
break;
case WM_NCCALCSIZE:
{
if (msg->wParam == 1)
return WVR_VALIDRECTS;
// Just return 0 and mark event as handled. This will draw the widget contents
// into the full size of the frame, instead of leaving room for it.
*result = 0;
return true;
}
break;
case WM_NCHITTEST:
{
if (isMaximized())
{
return false;
}
*result = 0;
const LONG borderWidth = 8;
RECT winrect;
GetWindowRect(reinterpret_cast<HWND>(winId()), &winrect);
// must be short to correctly work with multiple monitors (negative coordinates)
short x = msg->lParam & 0x0000FFFF;
short y = (msg->lParam & 0xFFFF0000) >> 16;
bool resizeWidth = minimumWidth() != maximumWidth();
bool resizeHeight = minimumHeight() != maximumHeight();
if (resizeWidth)
{
//left border
if (x >= winrect.left && x < winrect.left borderWidth)
{
*result = HTLEFT;
}
//right border
if (x < winrect.right && x >= winrect.right - borderWidth)
{
*result = HTRIGHT;
}
}
if (resizeHeight)
{
//bottom border
if (y < winrect.bottom && y >= winrect.bottom - borderWidth)
{
*result = HTBOTTOM;
}
//top border
if (y >= winrect.top && y < winrect.top borderWidth)
{
*result = HTTOP;
}
}
if (resizeWidth && resizeHeight)
{
//bottom left corner
if (x >= winrect.left && x < winrect.left borderWidth &&
y < winrect.bottom && y >= winrect.bottom - borderWidth)
{
*result = HTBOTTOMLEFT;
}
//bottom right corner
if (x < winrect.right && x >= winrect.right - borderWidth &&
y < winrect.bottom && y >= winrect.bottom - borderWidth)
{
*result = HTBOTTOMRIGHT;
}
//top left corner
if (x >= winrect.left && x < winrect.left borderWidth &&
y >= winrect.top && y < winrect.top borderWidth)
{
*result = HTTOPLEFT;
}
//top right corner
if (x < winrect.right && x >= winrect.right - borderWidth &&
y >= winrect.top && y < winrect.top borderWidth)
{
*result = HTTOPRIGHT;
}
}
if (*result != 0)
return true;
QWidget *action = QApplication::widgetAt(QCursor::pos());
if (action == this){
*result = HTCAPTION;
return true;
}
return false;
}
break;
default:
return QWidget::nativeEvent(eventType, message, result);
}
return QWidget::nativeEvent(eventType, message, result);
}
EDIT
I was testing the GUI compiling the project as debug
when I changed to release
the flicker reduced a lot but still persists, and now it flickers from the right instead of the left.
Testing with QT 6.3.1 static
debug
:
https://www.youtube.com/watch?v=a5fmXKsKDaY&feature=youtu.be
Testing with QT 6.3.1 static
release
:
https://www.youtube.com/watch?v=OwpxmCsLLRQ
Computer settings, os version: https://i.imgur.com/zdDAP1D.png
I tested on two different machines using win10, and the flicker happens on both.
The same issue happens with the frameless.h
from jdfa
answer, the difference is that my method the GUI resizing is way more 'smoothly'/fast.
CodePudding user response:
I would suggest to avoid this kind of mix of Qt and WinAPI. Not only it is not portable, but it is also produce your issue.
This problem can be avoided by solving original task (frameless resizeble window) in Qt way by using Qt::FramelessWindowHint
. Unfortunately, resizing functionality will be lost together with frame in that case. So you need to reimplement it by overriding mouse event handlers (mousePressEvent
, mouseMoveEvent
). You are basically doing similar thing already, just using WinAPI.
That problem is already solved here: https://gist.github.com/Nico-Duduf/b8c799f1f2a694732abd1238843b1d70
Here is my result with it: https://i.imgur.com/ovbG2hI.mp4
Edit: here is how I tested it:
#include <QApplication>
#include <QtWidgets/QMainWindow>
#include "FrameLess.h"
class MainWindow : public QMainWindow {
public:
MainWindow(QWidget* parent = nullptr)
: QMainWindow(parent)
{
setStyleSheet("border-image:url(\"bg.png\");");
}
};
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MainWindow main;
FrameLess f(&main);
main.show();
return app.exec();
}
CodePudding user response:
I created a project in 6.4 that doesn't have the jittery effect you describe and showed in the video. I tested it on windows 10.
Please create a folder, let's call it TestImage, and save the following files:
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
Qt::WindowFlags flags;
flags = Qt::Window;
//flags |= Qt::FramelessWindowHint;
flags |= Qt::CustomizeWindowHint;
this->setWindowFlags(flags);
}
MainWindow::~MainWindow()
{
delete ui;
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
TestImage.pro
QT = core gui
greaterThan(QT_MAJOR_VERSION, 4): QT = widgets
CONFIG = c 17
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES = QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES = \
main.cpp \
mainwindow.cpp
HEADERS = \
mainwindow.h
FORMS = \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS = target
RESOURCES = \
resources.qrc
earth.jpeg (save the image in the same folder)
resources.qrc
<RCC>
<qresource prefix="/image">
<file>earth.jpeg</file>
</qresource>
</RCC>
mainwindow.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>2068</width>
<height>1599</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget name="centralwidget">
<layout name="gridLayout_2">
<item row="0" column="0">
<layout name="gridLayout">
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<item row="0" column="0">
<widget name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="resources.qrc">:/image/earth.jpeg</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>2068</width>
<height>22</height>
</rect>
</property>
</widget>
<widget name="statusbar"/>
</widget>
<resources>
<include location="resources.qrc"/>
</resources>
<connections/>
</ui>