
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.