Resizible Frameless Window visual artefacts
-
Hi there! I was create QWidget class with atribute
setAttribute(Qt::WA_TranslucentBackground);
and flagsetWindowFlags(Qt::FramelessWindowHint);
to make a partly transparent and repeated the functionality of a window with a frame, including resizing the window with the mouse. But there were problems with resizing, when I resize, for example, from the left or top side, I am forced to move the widget and the animation of this movement manifests itself as an undesirable visual effect on the opposite side of the widget, constant shakes are obtained. The repainting after resizing smooths out the shaking a little, but I want to get rid of it completely.
The code of resizing from the left side:void QtFormColorTest::mouseMoveEvent(QMouseEvent* event) { // mGlobalPos - last write of globalPos() const QPoint delta = event->globalPos() - mGlobalPos; resize(width() - delta.x(), height()); move(pos() + QPoint(delta.x(), 0)); mGlobalPos = event->globalPos(); }
With setGeomerty() the effect is the same.
Can you advise me a method to solve this problem without using QML?
How is this implemented for a standard QWidget?
Thanks. -
@Axel-Spoerl Thanks for the answer, other windows do not jittering at all, even the similar code is implemented on QML, I just wanted to make sure that I am doing everything correctly in terms of programming on Qt.
Thank you.After some time, I returned to this problem and the best thing I could think of was to make a special transparent widget on top of the main one to resize the main one without visual artifacts.
For those who are interested, here is the link to github
Perhaps this code will be useful to those who have a similar problem. -
Hi @Finchi ,
could you please share the entire code with us, so I can try to reproduce it?
Which Qt Version are you using?Cheers
Axel -
Hi there! I was create QWidget class with atribute
setAttribute(Qt::WA_TranslucentBackground);
and flagsetWindowFlags(Qt::FramelessWindowHint);
to make a partly transparent and repeated the functionality of a window with a frame, including resizing the window with the mouse. But there were problems with resizing, when I resize, for example, from the left or top side, I am forced to move the widget and the animation of this movement manifests itself as an undesirable visual effect on the opposite side of the widget, constant shakes are obtained. The repainting after resizing smooths out the shaking a little, but I want to get rid of it completely.
The code of resizing from the left side:void QtFormColorTest::mouseMoveEvent(QMouseEvent* event) { // mGlobalPos - last write of globalPos() const QPoint delta = event->globalPos() - mGlobalPos; resize(width() - delta.x(), height()); move(pos() + QPoint(delta.x(), 0)); mGlobalPos = event->globalPos(); }
With setGeomerty() the effect is the same.
Can you advise me a method to solve this problem without using QML?
How is this implemented for a standard QWidget?
Thanks. -
Hi @Finchi ,
could you please share the entire code with us, so I can try to reproduce it?
Which Qt Version are you using?Cheers
Axelqt_form_color_test.h
#pragma once #include <QtWidgets/QMainWindow> #include <QMouseEvent> #include <QPushButton> class QtFormColorTest : public QWidget { Q_OBJECT public: explicit QtFormColorTest(QWidget* parent = nullptr); virtual ~QtFormColorTest(); void setHorisontalResizeMargin(int margin) { mHMargin = margin; } void setVerticalResizeMargin(int margin) { mVMargin = margin; } void setFullResizeMargin(int margin) { mFMargin = margin; } int getHorisontalResizeMargin() { return mHMargin; } int getVerticalResizeMargin() { return mVMargin; } int getFullResizeMargin() { return mFMargin; } protected: void paintEvent(QPaintEvent* event) override; void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void resizeEvent(QResizeEvent* event) override; private: const QPoint mInvalidPoint{ QPoint(-1, -1) }; QPoint mGlobalPos{ QPoint(0, 0) }; QPoint mLocalPos{ QPoint(0, 0) }; bool mIsMaximized{ false }; bool mMouseLeftButtonPressed{ false }; QPushButton* mBtnClose{ nullptr }; QPushButton* mBtnMaximize{ nullptr }; QPushButton* mBtnMinimize{ nullptr }; enum class MoveType { NO_TYPE, NO_WIDGET, WIDGET_MOVING, WIDGET_LEFT_HORISONTAL_RESIZE, WIDGET_RIGHT_HORISONTAL_RESIZE, WIDGET_TOP_VERTICAL_RESIZE, WIDGET_BOTTOM_VERTICAL_RESIZE, WIDGET_FULL_RESIZE_RIGHT_TOP, WIDGET_FULL_RESIZE_LEFT_BOTTOM, WIDGET_FULL_RESIZE_LEFT_TOP, WIDGET_FULL_RESIZE_RIGHT_BOTTOM }; MoveType mMoveType{ MoveType::WIDGET_MOVING }; int mHMargin{ 5 }; int mVMargin{ 5 }; int mFMargin{ 15 }; };
qt_form_color_test.cpp
#include "qt_form_color_test.h" #include <QLayout> #include <QPainter> #include <QPaintEvent> #include <QTextEdit> #include <QDebug> #include <QPainterPath> #include <QEvent> #include <math.h> #include <QApplication> QtFormColorTest::QtFormColorTest(QWidget* parent) : QWidget(parent) { this->setAttribute(Qt::WA_TranslucentBackground); this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); this->setStyleSheet( R"( border-radius: 10px; padding: 5px; )"); setMouseTracking(true); setCursor(Qt::OpenHandCursor); resize(640, 480); this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); mBtnClose = new QPushButton("✕", this); mBtnMaximize = new QPushButton("🗖", this); mBtnMinimize = new QPushButton("🗕", this); mBtnClose->setCursor(Qt::CursorShape::ArrowCursor); mBtnMaximize->setCursor(Qt::CursorShape::ArrowCursor); mBtnMinimize->setCursor(Qt::CursorShape::ArrowCursor); mBtnClose->setMaximumSize(mBtnClose->height(), mBtnClose->height()); mBtnMaximize->setMaximumSize(mBtnMaximize->height(), mBtnMaximize->height()); mBtnMinimize->setMaximumSize(mBtnMinimize->height(), mBtnMinimize->height()); mBtnClose->setStyleSheet( R"( QPushButton:!hover { background-color: rgba(90, 90, 90, 1); color: white; border-width: 1px; border-radius: 10px; border-color: rgba(90, 90, 90, 1); font: bold; font-family: Times New Roman; font-size: 14; padding: 5px; } QPushButton:hover { background-color: red; color: white; border-width: 1px; border-radius: 10px; border-color: rgba(90, 90, 90, 1); font: bold; font-family: Times New Roman; font-size: 14; padding: 5px; } )"); mBtnMaximize->setStyleSheet( R"( QPushButton:!hover { background-color: rgba(90, 90, 90, 1); color: white; border-width: 1px; border-radius: 10px; border-color: rgba(90, 90, 90, 1); font: bold; font-family: Times New Roman; font-size: 14; padding: 5px; } QPushButton:hover { background-color: rgba(60, 60, 60, 1); color: white; border-width: 1px; border-radius: 10px; border-color: rgba(90, 90, 90, 1); font: bold; font-family: Times New Roman; font-size: 14; padding: 5px; } )"); mBtnMinimize->setStyleSheet(mBtnMaximize->styleSheet()); QTextEdit* text_edit = new QTextEdit(this); text_edit->setFont(QFont("Times New Roman", 14)); text_edit->setStyleSheet( R"( background-color: rgba(60, 60, 60, 1); color: white; )"); QVBoxLayout* main_vbox = new QVBoxLayout(this); QHBoxLayout* hbox_system_btns = new QHBoxLayout; hbox_system_btns->addStretch(1); hbox_system_btns->addWidget(mBtnMinimize); hbox_system_btns->addWidget(mBtnMaximize); hbox_system_btns->addWidget(mBtnClose); main_vbox->addLayout(hbox_system_btns); main_vbox->addStretch(1); main_vbox->addWidget(text_edit); setLayout(main_vbox); auto maximize = [=]() -> void { if (!mIsMaximized) { this->showMaximized(); mIsMaximized = true; } else { this->showNormal(); mIsMaximized = false; } }; connect(mBtnClose, &QPushButton::clicked, qApp, QApplication::quit); connect(mBtnMaximize, &QPushButton::clicked, this, maximize); connect(mBtnMinimize, &QPushButton::clicked, this, &QMainWindow::showMinimized); auto closePressed = [=]() -> void { mBtnClose->setStyleSheet( R"( background-color: red; color: white; border-width: 1px; border-radius: 10px; border-color: rgba(60, 60, 60, 1); font: bold; font-family: Times New Roman; font-size: 14; padding: 5px; )"); }; } QtFormColorTest::~QtFormColorTest() {} void QtFormColorTest::paintEvent(QPaintEvent* event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); QPainterPath rounded_rect; rounded_rect.addRoundedRect(this->rect(), 10, 10); painter.fillPath(rounded_rect, QColor(0, 0, 0, 60)); painter.end(); } void QtFormColorTest::mousePressEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton && event->modifiers() == Qt::NoModifier) { mMouseLeftButtonPressed = true; mGlobalPos = event->globalPos(); if (mMoveType == MoveType::WIDGET_MOVING) setCursor(Qt::CursorShape::ClosedHandCursor); return; } QWidget::mousePressEvent(event); } void QtFormColorTest::mouseMoveEvent(QMouseEvent* event) { if (!mMouseLeftButtonPressed || mMoveType == MoveType::NO_TYPE) { // Loock for the arrow types to move or resize widget mLocalPos = QPoint(this->cursor().pos().x() - this->x(), this->cursor().pos().y() - this->y()); if ((mMoveType != MoveType::NO_WIDGET || mMoveType == MoveType::NO_TYPE) && (mLocalPos.x() < 0 || mLocalPos.x() > width() || mLocalPos.y() < 0 || mLocalPos.y() > height())) { setCursor(Qt::CursorShape::ArrowCursor); mMoveType = MoveType::NO_WIDGET; //qDebug() << "no widget"; } else if ((mMoveType != MoveType::WIDGET_FULL_RESIZE_LEFT_TOP || mMoveType == MoveType::NO_TYPE) && (mLocalPos.x() <= mFMargin && mLocalPos.y() <= mFMargin) && (mLocalPos.x() > 0 && mLocalPos.y() > 0)) { setCursor(Qt::CursorShape::SizeFDiagCursor); mMoveType = MoveType::WIDGET_FULL_RESIZE_LEFT_TOP; //qDebug() << "full resize left top"; } else if ((mMoveType != MoveType::WIDGET_FULL_RESIZE_RIGHT_BOTTOM || mMoveType == MoveType::NO_TYPE) && (mLocalPos.x() >= width() - mFMargin && mLocalPos.y() >= height() - mFMargin) && (mLocalPos.x() < width() && mLocalPos.y() < height())) { setCursor(Qt::CursorShape::SizeFDiagCursor); mMoveType = MoveType::WIDGET_FULL_RESIZE_RIGHT_BOTTOM; //qDebug() << "full resize right bottom"; } else if ((mMoveType != MoveType::WIDGET_FULL_RESIZE_LEFT_BOTTOM || mMoveType == MoveType::NO_TYPE) && (mLocalPos.x() <= mFMargin && mLocalPos.y() >= height() - mFMargin) && (mLocalPos.x() > 0 && mLocalPos.y() < height())) { setCursor(Qt::CursorShape::SizeBDiagCursor); mMoveType = MoveType::WIDGET_FULL_RESIZE_LEFT_BOTTOM; //qDebug() << "full resize left bottom"; } else if ((mMoveType != MoveType::WIDGET_FULL_RESIZE_RIGHT_TOP || mMoveType == MoveType::NO_TYPE) && (mLocalPos.x() >= width() - mFMargin && mLocalPos.y() <= mFMargin) && (mLocalPos.x() < width() && mLocalPos.y() > 0)) { setCursor(Qt::CursorShape::SizeBDiagCursor); mMoveType = MoveType::WIDGET_FULL_RESIZE_RIGHT_TOP; //qDebug() << "full resize right top"; } else if ((mMoveType != MoveType::WIDGET_LEFT_HORISONTAL_RESIZE || mMoveType == MoveType::NO_TYPE) && mLocalPos.x() <= mHMargin && mLocalPos.x() > 0) { setCursor(Qt::CursorShape::SizeHorCursor); mMoveType = MoveType::WIDGET_LEFT_HORISONTAL_RESIZE; //qDebug() << "horisontal left"; } else if ((mMoveType != MoveType::WIDGET_RIGHT_HORISONTAL_RESIZE || mMoveType == MoveType::NO_TYPE) && mLocalPos.x() >= width() - mHMargin && mLocalPos.x() < width()) { setCursor(Qt::CursorShape::SizeHorCursor); mMoveType = MoveType::WIDGET_RIGHT_HORISONTAL_RESIZE; //qDebug() << "horisontal right"; } else if ((mMoveType != MoveType::WIDGET_TOP_VERTICAL_RESIZE || mMoveType == MoveType::NO_TYPE) && mLocalPos.y() <= mVMargin && mLocalPos.y() > 0) { setCursor(Qt::CursorShape::SizeVerCursor); mMoveType = MoveType::WIDGET_TOP_VERTICAL_RESIZE; //qDebug() << "vertical top"; } else if ((mMoveType != MoveType::WIDGET_BOTTOM_VERTICAL_RESIZE || mMoveType == MoveType::NO_TYPE) && mLocalPos.y() >= height() - mVMargin && mLocalPos.y() < height()) { setCursor(Qt::CursorShape::SizeVerCursor); mMoveType = MoveType::WIDGET_BOTTOM_VERTICAL_RESIZE; //qDebug() << "vertical bottom"; } else if ((mMoveType != MoveType::WIDGET_MOVING || mMoveType == MoveType::NO_TYPE) && mLocalPos.x() > std::max(mHMargin, mFMargin) && mLocalPos.x() < width() - std::max(mHMargin, mFMargin) && mLocalPos.y() > std::max(mVMargin, mFMargin) && mLocalPos.y() < height() - std::max(mVMargin, mFMargin)) { setCursor(Qt::OpenHandCursor); mMoveType = MoveType::WIDGET_MOVING; //qDebug() << "moving"; } } else { if (mGlobalPos == mInvalidPoint) return QWidget::mouseMoveEvent(event); const QPoint delta = event->globalPos() - mGlobalPos; switch (mMoveType) { case MoveType::NO_TYPE: return; case MoveType::NO_WIDGET: return; case MoveType::WIDGET_MOVING: move(pos() + delta); mGlobalPos = event->globalPos(); break; case MoveType::WIDGET_LEFT_HORISONTAL_RESIZE: resize(width() - delta.x(), height()); move(pos() + QPoint(delta.x(), 0)); mGlobalPos = event->globalPos(); break; case MoveType::WIDGET_RIGHT_HORISONTAL_RESIZE: resize(width() + delta.x(), height()); mGlobalPos = event->globalPos(); break; case MoveType::WIDGET_TOP_VERTICAL_RESIZE: resize(width(), height() - delta.y()); move(pos() + QPoint(0, delta.y())); mGlobalPos = event->globalPos(); break; case MoveType::WIDGET_BOTTOM_VERTICAL_RESIZE: resize(width(), height()+delta.y()); mGlobalPos = event->globalPos(); break; case MoveType::WIDGET_FULL_RESIZE_RIGHT_TOP: resize(width()+delta.x(), height() - delta.y()); move(pos() + QPoint(0, delta.y())); mGlobalPos = event->globalPos(); break; case MoveType::WIDGET_FULL_RESIZE_LEFT_BOTTOM: resize(width() - delta.x(), height() + delta.y()); move(pos() + QPoint(delta.x(), 0)); mGlobalPos = event->globalPos(); break; case MoveType::WIDGET_FULL_RESIZE_LEFT_TOP: resize(width() - delta.x(), height() - delta.y()); move(pos() + QPoint(delta.x(), delta.y())); mGlobalPos = event->globalPos(); break; case MoveType::WIDGET_FULL_RESIZE_RIGHT_BOTTOM: resize(width() + delta.x(), height() + delta.y()); mGlobalPos = event->globalPos(); break; default: return; } } } void QtFormColorTest::mouseReleaseEvent(QMouseEvent* event) { mGlobalPos = mInvalidPoint; if(mMouseLeftButtonPressed && mMoveType==MoveType::WIDGET_MOVING) setCursor(Qt::OpenHandCursor); mMouseLeftButtonPressed = false; QWidget::mouseReleaseEvent(event); } void QtFormColorTest::resizeEvent(QResizeEvent* event) { repaint(); }
OS: MS Windows 10 x64, IDE: MSVS 2022, Qt6.4.1 with qmake compiller
Thank you. -
I can observe gentle jittering on Windows 11 and Linux/X11 with Qt 6.4
What smoothens it out almost entirely for me is to completely drop theresizeEvent
override.
The benchmark is how other windows on the desktop jitter when being resized.
That behavior depends on multiple factors, including CPU/GPU power and usage, screen size and memory.
Difficult to pin that to a problem in the Qt code base.
Sorry for replying late and without a solution... -
I can observe gentle jittering on Windows 11 and Linux/X11 with Qt 6.4
What smoothens it out almost entirely for me is to completely drop theresizeEvent
override.
The benchmark is how other windows on the desktop jitter when being resized.
That behavior depends on multiple factors, including CPU/GPU power and usage, screen size and memory.
Difficult to pin that to a problem in the Qt code base.
Sorry for replying late and without a solution...@Axel-Spoerl Thanks for the answer, other windows do not jittering at all, even the similar code is implemented on QML, I just wanted to make sure that I am doing everything correctly in terms of programming on Qt.
Thank you. -
@Axel-Spoerl Thanks for the answer, other windows do not jittering at all, even the similar code is implemented on QML, I just wanted to make sure that I am doing everything correctly in terms of programming on Qt.
Thank you.After some time, I returned to this problem and the best thing I could think of was to make a special transparent widget on top of the main one to resize the main one without visual artifacts.
For those who are interested, here is the link to github
Perhaps this code will be useful to those who have a similar problem. -
F Finchi has marked this topic as solved on