QT自绘标题和边框

这篇具有很好参考价值的文章主要介绍了QT自绘标题和边框。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在QT中如果想要自绘标题和边框,一般步骤是:

  1) 在创建窗口前设置Qt::FramelessWindowHint标志,设置该标志后会创建一个无标题、无边框的窗口。

  2)在客户区域的顶部创建一个自绘标题栏。

  3)给窗口绘制一个背景作为边框。

  4)如果想要鼠标拖动效果,可以在WM_NCHITTEST消息中返回HTCAPTION,具体方法百度这里不再详述。

  但是这样做会导致一个问题:

    在win7系统上,将窗口移动到屏幕边缘会自动排列(在屏幕顶部,左边,右边都会自动排列)的功能失效。

  如果你的窗口没有这个功能,只有两种可能:

  1)你的窗口不支持"移动到屏幕边缘自动排列"功能。

  2)你从系统关闭了此项功能(控制面板\轻松访问\轻松访问中心\使任务更容易被关注\防止将窗口移动到屏幕边缘时自动排列窗口)。

怎么样才能够既能够自绘标题和边框,又能够使用屏幕自动排列功能:

  有一个windows消息能够帮助我们,响应WM_NCCALCSIZE消息,直接返回true,就可以使客户区域的大小和窗口大小完全一样,这样就没有了标题栏和边框,我们可以按照上面的一般步骤来自绘标题栏和边框,唯一不同的是不需要设置Qt::FramelessWindowHint标志。

  这样做也会有问题:

    窗口显示的不完整,特别是在最大化的时候,非常明显。

  为什么会显示不完整,这个问题困扰我一整天。我新建了一个win32项目,响应WM_NCCALCSIZE消息,窗口显示完整,应该是QT自己处理的问题,最后不断调试QT源码,终于明白问题所在:

  调用堆栈(从下往上看):

QWindowsWindow::frameMarginsDp() 行 1854    C++
QWindowsWindow::frameMargins() 行 188    C++
QWidgetPrivate::updateFrameStrut() 行 11824    C++
QWidget::create(unsigned int window, bool initializeWindow, bool destroyOldWindow) 行 1358    C++

  关键函数:

QMargins QWindowsWindow::frameMarginsDp() const
{
    // Frames are invalidated by style changes (window state, flags).
    // As they are also required for geometry calculations in resize
    // event sequences, introduce a dirty flag mechanism to be able
    // to cache results.
    if (testFlag(FrameDirty)) {
        // Always skip calculating style-dependent margins for windows claimed to be frameless.
        // This allows users to remove the margins by handling WM_NCCALCSIZE with WS_THICKFRAME set
        // to ensure Areo snap still works (QTBUG-40578).
        m_data.frame = window()->flags() & Qt::FramelessWindowHint
            ? QMargins(0, 0, 0, 0)
            : QWindowsGeometryHint::frame(style(), exStyle());
        clearFlag(FrameDirty);
    }
    return m_data.frame + m_data.customMargins;
}

  注释里面清楚说明这是一个BUG(QTBUG-40578),我们虽然已经让客户区域大小和窗口大小完全一样,但是QT还是认为系统有边框,只有当设置了Qt::FramelessWindowHint标志,才会返回QMargins(0, 0, 0, 0)。

现在又回到了原点,且问题相互矛盾,想要自绘标题和边框必须设置Qt::FramelessWindowHint标志,但是设置Qt::FramelessWindowHint标志后"屏幕边缘自动排列"无效。

  首先要搞清楚Qt::FramelessWindowHint标志如何影响窗口,因为它直接导致"屏幕边缘自动排列"无效:

WindowCreationData::fromWindow(const QWindow * w, const QFlags<enum Qt::WindowType> flagsIn, unsigned int creationFlags) 行 519    C++
QWindowsWindowData::create(const QWindow * w, const QWindowsWindowData & parameters, const QString & title) 行 1075    C++
QWindowsIntegration::createWindowData(QWindow * window) 行 316    C++
QWindowsIntegration::createPlatformWindow(QWindow * window) 行 340    C++
QWindowPrivate::create(bool recursive) 行 392    C++
QWindow::create() 行 549    C++
QWidgetPrivate::create_sys(unsigned int window, bool initializeWindow, bool destroyOldWindow) 行 1456    C++
QWidget::create(unsigned int window, bool initializeWindow, bool destroyOldWindow) 行 1321    C++
QWidgetPrivate::createWinId(unsigned int winid) 行 2528    C++
void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flagsIn,
                                    unsigned creationFlags)
{
    if (popup || (type == Qt::ToolTip) || (type == Qt::SplashScreen)) {
        style = WS_POPUP;
    } else if (topLevel && !desktop) {
        if (flags & Qt::FramelessWindowHint)
            style = WS_POPUP;                // no border
        else if (flags & Qt::WindowTitleHint)
            style = WS_OVERLAPPED;
        else
            style = 0;
    } else {
        style = WS_CHILD;
    }

    if (!desktop) {
        if (topLevel) {
            if ((type == Qt::Window || dialog || tool)) {
                if (!(flags & Qt::FramelessWindowHint)) {
                    style |= WS_POPUP;
                    if (flags & Qt::MSWindowsFixedSizeDialogHint) {
                        style |= WS_DLGFRAME;
                    } else {
                        style |= WS_THICKFRAME;
                    }
                    if (flags & Qt::WindowTitleHint)
                        style |= WS_CAPTION; // Contains WS_DLGFRAME
                }
            } else {
                 exStyle |= WS_EX_TOOLWINDOW;
            }
        }
    }
}

  上面一个是调用堆栈(从下往上看),一个是关键函数(函数中不重要的内容已经删除)。从代码中可以看出,设置Qt::FramelessWindowHint标志会改变窗口样式,从而影响创建的窗口,现在基本已经知道,"屏幕边缘自动排列"功能与窗口样式有关。

  新建一个win32窗口程序,不断改变窗口的样式,最后得出结论:只有在窗口拥有WS_MAXIMIZEBOX | WS_THICKFRAME样式时,"屏幕边缘自动排列"功能才有效,最好还要添加WS_CAPTION样式,否则窗口最大化会覆盖任务栏。

原本以为完美结束了,但是不要高兴的太早,经过不断测试,还有几个问题:

  1)在任务栏点击窗口时,不能最小化:

    只要加上Qt::WindowMinimizeButtonHint标志即可解决该问题。

  2)如果有多个显示器,在辅屏上直接显示最大化,窗口显示不完整:

QWindowsWindow::show_sys() 行 1230    C++
QWindowsWindow::setVisible(bool visible) 行 1092    C++
QWindow::setVisible(bool visible) 行 518    C++
QWidgetPrivate::show_sys() 行 7897    C++
QWidgetPrivate::show_helper() 行 7826    C++
QWidget::setVisible(bool visible) 行 8110    C++
QWidget::showMaximized() 行 3154    C++
void QWindowsWindow::show_sys() const
{
    int sm = SW_SHOWNORMAL;
    bool fakedMaximize = false;
    const QWindow *w = window();
    const Qt::WindowFlags flags = w->flags();
    const Qt::WindowType type = w->type();
    if (w->isTopLevel()) {
        const Qt::WindowState state = w->windowState();
        if (state & Qt::WindowMinimized) {
            sm = SW_SHOWMINIMIZED;
            if (!isVisible())
                sm = SW_SHOWMINNOACTIVE;
        } else {
            updateTransientParent();
            if (state & Qt::WindowMaximized) {
                sm = SW_SHOWMAXIMIZED;
                // Windows will not behave correctly when we try to maximize a window which does not
                // have minimize nor maximize buttons in the window frame. Windows would then ignore
                // non-available geometry, and rather maximize the widget to the full screen, minus the
                // window frame (caption). So, we do a trick here, by adding a maximize button before
                // maximizing the widget, and then remove the maximize button afterwards.
                if (flags & Qt::WindowTitleHint &&
                        !(flags & (Qt::WindowMinMaxButtonsHint | Qt::FramelessWindowHint))) {
                    fakedMaximize = TRUE;
                    setStyle(style() | WS_MAXIMIZEBOX);
                }
            } // Qt::WindowMaximized
        } // !Qt::WindowMinimized
    }
    if (type == Qt::Popup || type == Qt::ToolTip || type == Qt::Tool || testShowWithoutActivating(w))
        sm = SW_SHOWNOACTIVATE;

    if (w->windowState() & Qt::WindowMaximized)
        setFlag(WithinMaximize); // QTBUG-8361

    ShowWindow(m_data.hwnd, sm);

    clearFlag(WithinMaximize);

    if (fakedMaximize) {
        setStyle(style() & ~WS_MAXIMIZEBOX);
        SetWindowPos(m_data.hwnd, 0, 0, 0, 0, 0,
                     SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER
                     | SWP_FRAMECHANGED);
    }
}

  还是老样子,上面一个是调用堆栈,一个是关键函数,我们可以看到最后QT调用了ShowWindow函数来显示最大化窗口,但是为什么会显示不完整呢?

  通常遇到一个复杂的问题,我会新建一个简单的项目来做实验。新建一个win32项目,最开始显示就让它最大化,结果显示正常,证明还是QT自己处理的问题,应该是在ShowWindow之后进行其他的处理,导致窗口显示不完整,最后发现是

处理WM_GETMINMAXINFO消息导致的,接下来我们看看QT如何处理WM_GETMINMAXINFO消息。

QWindowsWindow::getSizeHints(tagMINMAXINFO * mmi) 行 2044    C++
QWindowsContext::windowsProc 行 1015    C++
qWindowsWndProc(HWND__ * hwnd, unsigned int message, unsigned int wParam, long lParam) 行 1271    C++
void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const
{
    const QWindowsGeometryHint hint(window(), m_data.customMargins);
    hint.applyToMinMaxInfo(m_data.hwnd, mmi);

    if ((testFlag(WithinMaximize) || (window()->windowState() == Qt::WindowMinimized))
            && (m_data.flags & Qt::FramelessWindowHint)) {
        // This block fixes QTBUG-8361: Frameless windows shouldn't cover the
        // taskbar when maximized
        const QScreen *screen = window()->screen();

        // Documentation of MINMAXINFO states that it will only work for the primary screen
        if (screen && screen == QGuiApplication::primaryScreen()) {
            mmi->ptMaxSize.y = screen->availableGeometry().height();

            // Width, because you can have the taskbar on the sides too.
            mmi->ptMaxSize.x = screen->availableGeometry().width();

            // If you have the taskbar on top, or on the left you don't want it at (0,0):
            mmi->ptMaxPosition.x = screen->availableGeometry().x();
            mmi->ptMaxPosition.y = screen->availableGeometry().y();
        } else if (!screen){
            qWarning() << "window()->screen() returned a null screen";
        }
    }

    qCDebug(lcQpaWindows) << __FUNCTION__ << window() << *mmi;
}

  当程序在辅屏上时,它的screen是辅屏,如果当前screen不等于QGuiApplication::primaryScreen(主屏),则不设置MINMAXINFO结构,但是由于它已经处理了WM_GETMINMAXINFO消息,导致这个消息不会被系统默认的窗口处理函数处理(DefWindowProc),所以才会显示不完整,解决办法是优先响应WM_GETMINMAXINFO消息,让后交给系统默认的窗口处理函数进行处理。

  3)最大化后,窗口内容变小,最明显的就是最小化、最大化、关闭按钮变小了:

  窗口最大化时,系统会在屏幕上面显示所有的客户区域,此时系统会计算边框的大小,然后超出屏幕范围进行显示,例如边框的宽为8高为8,则系统会在(-8,-8,宽度+8,高度+8)的位置显示窗口,给人的感觉窗口的内容变小了,

QT自绘标题和边框

  除去底部的任务栏,程序最大化可显示的最大宽度是1600*860,而窗口的实际位置是(-8,-8,1608,868)。这样我们可以添加一个QWidget作为主显示窗口,然后在程序最大化时,添加一个外边框,让它向内部缩一点。

最后的解决方案是:

  1. 在窗口的构造函数中添加以下代码,改变窗口的样式:

this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint);
// QMainWindow透明显示,当设置主显示窗口的外边距时,防止外边距显示出来。
this->setAttribute(Qt::WA_TranslucentBackground, true);

HWND hwnd = (HWND)this->winId();
DWORD style = ::GetWindowLong(hwnd, GWL_STYLE);
::SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION);

  2. 重载nativeEvent函数,处理WM_NCHITTEST、WM_NCCALCSIZE和WM_GETMINMAXINFO消息

bool CustomWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
    MSG* msg = (MSG*)message;
    switch (msg->message) {

    case WM_NCHITTEST:
    {
        int xPos = GET_X_LPARAM(msg->lParam) - this->frameGeometry().x();
        int yPos = GET_Y_LPARAM(msg->lParam) - this->frameGeometry().y();
        if (m_title->isCaption(xPos, yPos)) {
            *result = HTCAPTION;
            return true;
        }
    }
        break;
case WM_NCCALCSIZE:
        return true;

    case WM_GETMINMAXINFO:
    {
        if (::IsZoomed(msg->hwnd)) {
            // 最大化时会超出屏幕,所以填充边框间距
            RECT frame = { 0, 0, 0, 0 };
            AdjustWindowRectEx(&frame, WS_OVERLAPPEDWINDOW, FALSE, 0);
            frame.left = abs(frame.left);
            frame.top = abs(frame.bottom);
             this->setContentsMargins(frame.left, frame.top, frame.right, frame.bottom);
        }
        else {
            this->setContentsMargins(2, 2, 2, 2);
        }

        *result = ::DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
        return true;
    }
        break;
    }

    return QMainWindow::nativeEvent(eventType, message, result);
}

显示效果:

QT自绘标题和边框文章来源地址https://www.toymoban.com/news/detail-437065.html

到了这里,关于QT自绘标题和边框的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • css画箭头图标放标题前面,旋转,border的单个边框设置

    CSS边框属性_css border dotted_小张biubiu的博客-CSDN博客 你还不知道css的旋转效果怎么实现?来这里看看吧_css旋转效果_我糖呢的博客-CSDN博客

    2024年02月09日
    浏览(39)
  • 小程序开发费用一览表,如果你也想要用低成本制作出自己的小程序,来了解一下!

    近几年来微信小程序的发展的十分的迅速,越来越多的用户对小程序已经非常熟悉,同时很多商家都看到了小程序的商机,其中小程序商城更是受到众多用户和商家的欢迎。 制作小程序商城主要的方式有两种,一种是源码定制开发,一种是直接套用小程序模板了。而其中大家

    2023年04月08日
    浏览(60)
  • 前端vue导出excel(标题加粗+表头自定义样式+表格边框+单元格自定义样式)

    接近过年,被一大堆excel报表烦死的我,遇到要求前端导出excel的后端,差点猝死的我拼命学习中,整理出这篇文章,希望看到这篇文章的你有所收获,也希望能收到大佬们的指点 之前用c#,.net弄过导出word,excel,可以点击查看.NET使用Aspose控件生成Word(可构建自定义表格)、

    2024年04月15日
    浏览(57)
  • element表格tooltip内容换行展示(本人第一次写帖子效果图在最后如果是各位想要的效果请点个赞,写的不好的地方也可以指导一下万分感谢)

    前言 在使用element的table中咱们有可能会遇到后端返回的单个字段有多个内容这个时候想一个单元格展示换行其实很简单直接就是template加个块标签v-for就可以了如果是很多的话这样表格就不美观如下图,这个时候大家会想到是show-overflow-tooltip,但是如果说数据跟我的一样是时

    2024年02月15日
    浏览(39)
  • Qt不能安装自己想要的版本,如Qt 5.15.2

    使用在线安装工具安装Qt5.15.2时,发现没有Qt 5的相关版本,只有Qt 6的版本,这时选择右边的Archive,再点击筛选,这时就会出现之前的Qt版本。

    2024年04月17日
    浏览(32)
  • QT窗口实现无边框

    在窗口的构造函数内添加

    2024年02月06日
    浏览(36)
  • Qt 窗口阴影边框

    环境:Qt 5.15 + VS2019 实现方法参考链接:https://blog.csdn.net/goforwardtostep/article/details/99549750 使用此方法添加窗口阴影,会出现警告信息: 且窗口最大化与还原切换时会出现子控件刷新问题: 实现方法参考链接:https://blog.csdn.net/goforwardtostep/article/details/99549750 需要美工 实现方法

    2024年01月24日
    浏览(30)
  • Qt无边框青绿色主题

    收费产品,学生党、闹眼子党勿扰 收费金额: 500元 最近因项目需要,写了一个炫酷的青绿色、无边框界面,和3DSMax的界面有点类似。 首先看看3DSMax的界面 不知道大家看出来没,这个ui其实很简单,顶部是一个toolbar工具栏,右边是tool box抽屉盒,左边也是工具栏。 我做了一

    2024年02月10日
    浏览(32)
  • Qt C++ 实现无边框窗口

    2024年04月17日
    浏览(30)
  • FramelessWindow实现qt无边框窗口

    FramelessWindow.h文件链接 添加FramelessWindow.h头文件 在你的qt项目中将FramelessWindow.h头文件导入进来,或者使用cmake、qmake将包含该头文件的目录引入。 简单浏览framelessWindow代码 我们发现FramelessWindow是一个继承模板参数的类模板。 并且看一下构造函数 简单实现 1.继承该类模板,如

    2024年02月02日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包