官方网站建设的目标,wordpress 不用ftp,c 做网站 知乎,wordpress要求配置在QT中如果想要自绘标题和边框#xff0c;一般步骤是#xff1a; 1#xff09; 在创建窗口前设置Qt::FramelessWindowHint标志#xff0c;设置该标志后会创建一个无标题、无边框的窗口。 2#xff09;在客户区域的顶部创建一个自绘标题栏。 3#xff09;给窗口绘制一个背…在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;
} 注释里面清楚说明这是一个BUGQTBUG-40578我们虽然已经让客户区域大小和窗口大小完全一样但是QT还是认为系统有边框只有当设置了Qt::FramelessWindowHint标志才会返回QMargins(0, 0, 0, 0)。
现在又回到了原点且问题相互矛盾想要自绘标题和边框必须设置Qt::FramelessWindowHint标志但是设置Qt::FramelessWindowHint标志后屏幕边缘自动排列无效。 首先要搞清楚Qt::FramelessWindowHint标志如何影响窗口因为它直接导致屏幕边缘自动排列无效
WindowCreationData::fromWindow(const QWindow * w, const QFlagsenum 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 borderelse if (flags Qt::WindowTitleHint)style WS_OVERLAPPED;elsestyle 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-8361ShowWindow(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 shouldnt cover the// taskbar when maximizedconst QScreen *screen window()-screen();// Documentation of MINMAXINFO states that it will only work for the primary screenif (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 dont 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的位置显示窗口给人的感觉窗口的内容变小了 除去底部的任务栏程序最大化可显示的最大宽度是1600*860而窗口的实际位置是-8-81608868。这样我们可以添加一个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);
}
显示效果