
QT小部件和QT快速的跨平台窗口自定义框架。支持Windows,Linux和MacOS。
您可以加入我们的Discord频道与我们交流。您可以分享有关在更多平台和应用程序上改善 /实施FramelessHelper功能的发现,想法和想法!
FramelessDialog类。如果QDialog比一般的QWidget首选,则可以使用它。WindowBorderPainter类绘制,并且也已公开展开,因此您将能够更改我们轻松绘制窗口边框的方式。WindowBorder元素。这是一个跨平台的窗口边框装饰器,并且可以在没有FramelessHelper元素的情况下使用。FramelessApplicationWindow元素。这是标准ApplicationWindow元素的简单包装器,只需删除标题栏即可添加窗口边框。find_package查找FramelessHelper。






使用Qsynthesis框架的Vogen编辑器。存储库网址:https://gitee.com/functioner/qvogenclient。
每个平台还有一些其他限制,请参阅下面的平台注释部分。
git clone --recursive https://github.com/wangwenx190/framelesshelper.git # "--recursive" is necessary to clone the submodules.
mkdir build # Please change to your own build directory!
cd build
cmake -DCMAKE_PREFIX_PATH= < YOUR_QT_SDK_DIR_PATH > -DCMAKE_INSTALL_PREFIX= < WHERE_YOU_WANT_TO_INSTALL > -DCMAKE_BUILD_TYPE=Release -GNinja < PATH_TO_THE_REPOSITORY >
cmake --build . --config Release --target all --parallel
cmake --install . --config Release --strip # Don't add "--strip" for MSVC/Clang-CL/Intel-CL toolchains!
# YOUR_QT_SDK_DIR_PATH: the Qt SDK directory, something like "C:/Qt/6.5.1/msvc2019_64" or "/opt/Qt/6.5.1/gcc_64". Please change to your own path!
# WHERE_YOU_WANT_TO_INSTALL: the install directory of FramelessHelper, something like "../install". You can ignore this setting if you don't need to install the CMake package. Please change to your own path!
# PATH_TO_THE_REPOSITORY: the source code directory of FramelessHelper, something like "../framelesshelper". Please change to your own path!您也可以使用Qt6_DIR或Qt5_DIR替换CMAKE_PREFIX_PATH :
cmake -DQt6_DIR=C:/Qt/6.5.1/msvc2019_64/lib/cmake/Qt6 [other parameters ...]
# Or
cmake -DQt5_DIR=C:/Qt/5.15.2/msvc2019_64/lib/cmake/Qt5 [other parameters ...]如果克隆子模块时有任何错误,请尝试在项目目录中运行git submodule update --init --recursive --remote ,该命令将下载和更新所有的子模块。如果再次失败,请尝试多次执行它,直到最终成功。
完成编译和安装后,您将能够使用find_package(FramelessHelper REQUIRED COMPONENTS Core Widgets Quick)命令查找并链接到FramelessHelper库。但是,在此之前,请确保CMAKE通过传递CMAKE_PREFIX_PATH或FramelessHelper_DIR变量来知道在哪里可以找到FramelessHelper。例如: -DCMAKE_PREFIX_PATH=C:/my-cmake-packages;C:/my-toolchain;etc...或-DFramelessHelper_DIR=C:/Projects/FramelessHelper/lib64/cmake/FramelessHelper 。当然,还支持将Framhelper构建为CMAKE项目的子目录。受支持的Framhelper目标名称是FramelessHelper::Core , FramelessHelper::Widgets和FramelessHelper::Quick 。示例代码:
# Find Qt:
find_package (QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
find_package (Qt ${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
# Find FramelessHelper:
find_package (FramelessHelper REQUIRED COMPONENTS Core Widgets)
# Create your target:
add_executable (demo)
# Add your source code:
target_sources (demo PRIVATE main.cpp)
# Link to Qt and FramelessHelper:
target_link_libraries (demo PRIVATE
Qt ${QT_VERSION_MAJOR} ::Widgets
FramelessHelper::Core
FramelessHelper::Widgets
)如果您需要FramelessHelper快速模块的语法突出显示,请设置QML_IMPORT_PATH变量。示例代码:
# This is the path where you want FramelessHelper's Quick plugin (it only contains the QML meta
# info and an optional dummy library, for QtCreator's QML tooling purpose, it's not the Quick
# module) to place. Please change to your own path!
# If you are using add_subdirectory() to include FramelessHelper directly, you can change it to
# "${PROJECT_BINARY_DIR}/imports" instead of the install location.
set (FRAMELESSHELPER_IMPORT_DIR "C:/packages/FramelessHelper/qml" )
list ( APPEND QML_IMPORT_PATH " ${FRAMELESSHELPER_IMPORT_DIR} " )
list ( REMOVE_DUPLICATES QML_IMPORT_PATH)
# Force cache refresh:
set (QML_IMPORT_PATH ${QML_IMPORT_PATH} CACHE STRING "Qt Creator extra QML import paths" FORCE)要自定义Qwidget的窗框,您需要实例化FramelessWidgetsHelper对象,然后将其连接到小部件的顶级小部件,然后将FramelessWidgetsHelper固定在您的所有其余工作中:一旦它已将其连接到顶级式窗口窗口,将自动删除窗口框架。从理论上讲,您可以将多个FramelessWidgetsHelper对象实例化,以相同的小部件,在这种情况下,只有一个对象可以保持功能性,所有其他对象都将成为该对象的包装器。但是,为了确保一切顺利进行,无论如何,您都不应该这样做。实例化FramelessWidgetsHelper对象的最简单方法是调用静态方法FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *) 。如果有的话,它将返回先前实例化对象的句柄,或者如果找不到一个新对象,它将实例化。可以安全地将此方法用于同一小部件,如果存在一个方法,则不会实例化任何新对象。只要顶级小部件相同,您何时和何处将其称为该功能也无关紧要。内部创建的对象将始终属于顶级小部件。获取FramelessWidgetsHelper对象的句柄后,您可以调用void FramelessWidgetsHelper::extendsContentIntoTitleBar()以使其隐藏操作系统提供的默认标题栏。为了确保FramelessWidgetsHelper可以找到正确的顶级小部件,您应该在一个窗口链上调用FramelessWidgetsHelper *FramelessWidgetsHelper::get(QObject *)函数,该小部件具有完整的父链,其root parten的root parten parter是顶级小部件。为了使无框窗口可拖动,您应该自己提供一个自制标题栏窗口小部件,标题栏窗口小部件不需要处于矩形形状,也不需要将其放置在窗口的第一行上。致电void FramelessWidgetsHelper::setTitleBarWidget(QWidget *)让FramelessHelper知道您的标题bar小部件是什么。默认情况下,标题栏区域中的所有小部件都不对任何鼠标和键盘事件负责,因为它们已被FramelessHelper拦截。为了使他们恢复负责任的状态,您应该让它们可见以进行测试。致电void FramelessWidgetsHelper::setHitTestVisible(QWidget* )做到这一点。当然,您可以将其称为“标题栏”内部的小部件,但是它不会产生任何效果。由于QT自身的局限性,您需要确保小部件具有完整的父链,其root父母是顶级小部件。永远不要尝试删除FramelessWidgetsHelper对象,它可能仍在监视和控制您的小部件,QT会自动删除它。无需担心内存泄漏。
还有两个称为FramelessWidget和FramelessMainWindow的类,它们只是FramelessWidgetsHelper的简单包装器,它们仅保存了void FramelessWidgetsHelper::extendsContentIntoTitleBar()函数。您绝对可以使用普通的QWidget 。
首先,在很早的阶段(必须在构造任何Q(Gui|Core)Application程序对象之前,必须在您的main函数中调用void FramelessHelper::Widgets::initialize() :
int main ( int , char **)
{
FramelessHelper::Widgets::initialize ();
// ...
}然后隐藏OS提供的标准标题栏:
MyWidget::MyWidget (QWidget *parent) : QWidget(parent)
{
// You should do this early enough.
FramelessWidgetsHelper::get ( this )-> extendsContentIntoTitleBar ();
// ...
}然后,让FramelessHelper知道应该是标题栏是什么:
void MyWidget::myFunction ()
{
// ...
FramelessWidgetsHelper::get ( this )-> setTitleBarWidget (m_myTitleBarWidget);
// ...
}然后在您的标题栏内部进行一些小部件,以进行测试:
void MyWidget::myFunction2 ()
{
// ...
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someSearchBox);
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someButton);
FramelessWidgetsHelper::get ( this )-> setHitTestVisible (m_someMenuItem);
// ...
} QT小部件应用程序的重要说明:仅当FramelessHelper完成窗口自定义过程,例如更改窗口几何/flags/state时,才有某些功能可用。在这种情况下,您可以连接到FramelessHelper的public void ready()信号,以获取准确的时间点,然后进行休息的初始化过程。
首先,您应该在很早的阶段(必须在构建任何Q(Gui|Core)Application程序对象之前)在main函数中调用void FramelessHelper::Quick::initialize() :
int main ( int , char **)
{
FramelessHelper::Quick::initialize ();
// ...
}然后,您需要通过调用void FramelessHelper::Quick::registerTypes(QQmlEngine *)来注册FramelessHelper提供的自定义类型,然后再加载任何QML文档:
int main ( int , char **)
{
// ...
QQmlApplicationEngine engine;
FramelessHelper::Quick::registerTypes (&engine);
// ...
}现在您可以编写QML文档。您应该从uri org.wangwenx190.FramelessHelper导入FramelessHelper 。如果使用QT5,则应立即指定版本号:
import org.wangwenx190.FramelessHelper 1.0 // You can use "auto" or omit the version number in Qt6.然后,您可以使用QML类型FramelessHelper的附件属性:
Window {
Item {
id : myTitleBar
Item { id : someControl1 }
Item { id : someControl2 }
Item { id : someControl3 }
Component . onCompleted : {
// Don't access FramelessHelper too early, otherwise it may not be able to find the root window!
FramelessHelper . titleBarItem = myTitleBar;
FramelessHelper . setHitTestVisible (someControl1);
FramelessHelper . setHitTestVisible (someControl2);
FramelessHelper . setHitTestVisible (someControl3);
}
}
}与FramelessWidgetsHelper接口相同,QML类型FramelessHelper只能为每个Window实例化一次,无论您在何时何地使用附加的属性。但是,由于FramelessHelper类型的特殊设计,您也可以像普通的QML类型一样使用它:
Window {
Item {
id : myTitleBar
Item { id : someControl1 }
Item { id : someControl2 }
Item { id : someControl3 }
Component . onCompleted : {
framelessHelper . setHitTestVisible (someControl1);
framelessHelper . setHitTestVisible (someControl2);
framelessHelper . setHitTestVisible (someControl3);
}
}
FramelessHelper {
id : framelessHelper
titleBarItem : myTitleBar
}
}从理论上讲,可以为一个Window实例化多个FramelessHelper对象,在这种情况下,只有一个将保持功能性,所有其他对象都会成为其中的包装器,但是不建议这样做并可能导致意外的行为或错误,因此请避免在任何情况下尝试这样做。
如果您发现呼叫后任何FramelessHelper功能没有效果,那么最大的原因是,到调用函数/更改FramelessHelper的属性时,根窗口尚未完成其初始化过程,因此FramelessHelper无法获得其处理方法,因此,直到root intirtiation the Root Initialization。
还有一种名为FramelessWindow的QML类型,它只是FramelessHelper的一个简单包装器,您绝对可以使用Plain Window 。
QT快速应用程序的重要说明:只有在FramelessHelper完成窗口自定义过程(例如更改窗口几何/flags/state)之类的窗口自定义过程时,才有某些功能可用。在这种情况下,您可以连接到FramelessHelper的公共void ready()信号,以获取准确的时间点,然后进行休息的初始化过程:
Window {
FramelessHelper . onReady : {
// do something here ...
}
} Window {
FramelessHelper {
onReady : {
// do something here ...
}
}
}请参阅演示项目以查看更多详细用法:示例
如果在某些非常罕见的情况下(仅在Windows 7上)禁用了DWM组成,则左上角和右上角将以圆形形状出现。如果您重新启用DWM组成,则可以将圆角恢复为正方形。
有一个OpenGL驱动程序错误,它会导致一些无框窗户在您自制标题栏的顶部有一个奇怪的黑色条,并且还使窗户中的控件转移到右下角的角落,以获取某些像素。这是您的图形卡驱动程序的错误,特别是您的OpenGL驱动程序,而不是FramelessHelper。我们的用户提供了一些解决方案,但是其中一些解决方案可能在任何情况下都无法正常工作,您可以从中挑选一种解决方案:
| 解决方案 | 原则 |
|---|---|
| 升级图形驱动程序 | 尝试使用可能随身携带的新驾驶员 |
| 将系统主题更改为“基本”(与“ Windows Aero”相反) | 让Windows使用纯软件渲染 |
| 如果有多个图形卡,请使用另一张卡片 | 尝试使用可能根本没有错误的驱动程序 |
| 将系统升级到至少Windows 11 | Windows 11重新设计了窗口系统,因此无法再触发该错误 |
从窗口中删除WS_THICKFRAME和WS_OVERLAPPED样式,也许同时添加WS_POPUP样式,并且在WM_NCCALCSIZE BLOCK中不要执行任何操作(只需直接返回false或删除/注释整个块)) | 尝试反映QT的FramelessWindowHint的行为 |
使用Qt::FramelessWindowHint而不是进行WM_NCCALCSIZE TRICK | QT的渲染代码路径在这两个解决方案之间完全不同 |
| 强迫QT使用角度后端而不是桌面OpenGL | Angle将将OpenGL指令转化为D3D |
| 迫使QT使用纯软件渲染而不是通过OpenGL渲染 | QT根本不使用OpenGL |
| 迫使QT使用Mesa 3D库,而不是正常的OpenGL | 尝试使用不同的OpenGL实现 |
| 使用Direct3D/Vulkan/Metal而不是OpenGL | 只是不要使用越野车opengl |
如果您很幸运,其中一个可能会为您解决问题。如果没有,您可以尝试一起使用多个解决方案。但是我不能保证问题可以100%解决。
由于Windows 10有许多子版本,因此强烈建议使用最新版本的Windows 10,至少不超过Windows 10 1809 。如果您尝试在一些非常旧的Windows 10版本(例如1507或1607)上使用此框架,则可能存在一些兼容性问题。还支持在Windows 7上使用此框架,但不建议使用此框架。为了获得最稳定的行为和最佳外观,您应该在最新版本的Windows 10或Windows 11上使用它。
为了使SNAP布局按预期工作,您的自制系统按钮还有一些其他规则:
setSystemButton() (可以是任何qwidget或qquickitem ),让FramelessHelper知道哪个是最小化/最大化/关闭按钮。When running on Win10, it seems the top border is missing? But the demo applications still have it? FramelessHelper通过删除窗框的整个顶部(包括顶部边框)来隐藏系统标题栏。没有办法仅删除系统标题栏,但仍然同时保留顶部边框,即使是Microsoft他们自己也无法做到这一点。非Microsoft开发人员的确切原因是未知的,我对挖掘背后的所有魔力没有兴趣。因此,您必须手动绘制一个假装顶级边界仍然存在。您可以通过官方DWM API检索其高度和颜色。请参阅DwmGetWindowAttribute()和DwmGetColorizationColor()的文档。演示应用程序仍然具有顶端边框,因为它们的主窗户全部从FramelessWidget或FramelessMainWindow继承,这将为您内部吸引顶级边框。至于QT快速,QML类型FramelessWindow也将绘制顶部边框。
When running on Wayland, dragging the title bar causes crash?您需要强迫QT在Wayland运行时使用XCB QPA。在实例化任何Q(Gui)Application程序实例之前,请尝试设置环境变量QT_QPA_PLATFORM (case敏感)对xcb (case敏感)。或者只需在您的main函数中调用void FramelessHelper::Widgets/Quick::initialize() ,此功能将为您照顾它。
I can see the black background during window resizing?首先,这是一个QT问题,不是由Framelesshelper引起的。 QT小部件应用程序不可能。这是QT快速应用程序的常见问题。在大多数情况下,它是由D3D11/Vulkan/Metal引起的,因为它们不擅长处理纹理调整操作。如果您真的想解决此问题,则可以尝试将QT的RHI后端更改为OpenGL (请小心图形卡驱动程序的错误)或软件(如果您不关心性能)。请记住,此问题无法从QT之外解决。
Can I preserve the window frame border even on Win7? How does Google Chrome/Microsoft Edge's installer achieve that?简短答案:这是不可能的。完整的解释:当然,我们可以使用在Win10上使用的相同技术来删除窗户的整个顶部并同时保留其他三个框架边界,但是在Win10上,我们可以通过在WM_PAINT处理程序中进行一些黑色魔法,或者在Win7上绘制薄框架边框,但是,在Win7上做这件事是不可能的。我已经在Win7上尝试过它,可悲的是,结果是WM_PAINT技巧在Win7上无法使用,我们也无法绘制一个看起来与原始的边框相似的框架边框(半透明的矩形,与系统的重音颜色和窗户后面的视觉内容混合在一起,也具有一些模糊效果)。但是看来Google Chrome/Microsoft Edge的安装程序已经实现了我们想做的事情,如何?好吧,他们的安装程序是开源的,我已经阅读了它的代码。他们通过重叠两个窗口,底部的一个普通窗口,顶部的另一个无边界窗口覆盖底部窗口的标题栏来实现这一目标。他们在无边界窗口上画出自己的自制标题栏,并使用它模仿标准标题栏的行为。系统提供的原始标题栏仍在那里,但是任何人都看不到它仅仅是因为它被另一个窗口覆盖。我承认在这种情况下,这是一个很好的解决方案,但是对于我们的图书馆来说,这是不合适的,因为代码复杂性会爆炸。
按第一次贡献时间订购(可能不是很准确,对不起)
MIT License
Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.