diff --git a/docs/source/gallery.md b/docs/source/gallery.md index 6adc26c9ec8d8167481d8feb7d0386a0db0989bc..d686d01baeb4f3f81111812039a867e3419c111a 100644 --- a/docs/source/gallery.md +++ b/docs/source/gallery.md @@ -58,5 +58,5 @@ ### Material -### Acrylic Label +#### Acrylic Label \ No newline at end of file diff --git a/examples/gallery/app/common/config.py b/examples/gallery/app/common/config.py new file mode 100644 index 0000000000000000000000000000000000000000..db56d39e99f15de0b418e2dd667d721d8f76f27e --- /dev/null +++ b/examples/gallery/app/common/config.py @@ -0,0 +1,57 @@ +# coding:utf-8 +from enum import Enum + +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QGuiApplication, QFont +from qfluentwidgets import (qconfig, QConfig, ConfigItem, OptionsConfigItem, BoolValidator, + ColorConfigItem, OptionsValidator, RangeConfigItem, RangeValidator, + FolderListValidator, EnumSerializer, FolderValidator) + + + +class Language(Enum): + """ Language enumeration """ + + CHINESE_SIMPLIFIED = "zh" + CHINESE_TRADITIONAL = "hk" + ENGLISH = "en" + AUTO = "Auto" + + +class Config(QConfig): + """ Config of application """ + + # folders + musicFolders = ConfigItem( + "Folders", "LocalMusic", [], FolderListValidator()) + downloadFolder = ConfigItem( + "Folders", "Download", "app/download", FolderValidator()) + + # main window + minimizeToTray = ConfigItem( + "MainWindow", "MinimizeToTray", True, BoolValidator()) + playBarColor = ColorConfigItem("MainWindow", "PlayBarColor", "#225C7F") + recentPlaysNumber = RangeConfigItem( + "MainWindow", "RecentPlayNumbers", 300, RangeValidator(10, 300)) + dpiScale = OptionsConfigItem( + "MainWindow", "DpiScale", "Auto", OptionsValidator([1, 1.25, 1.5, 1.75, 2, "Auto"]), restart=True) + language = OptionsConfigItem( + "MainWindow", "Language", Language.AUTO, OptionsValidator(Language), EnumSerializer(Language), restart=True) + + # software update + checkUpdateAtStartUp = ConfigItem( + "Update", "CheckUpdateAtStartUp", True, BoolValidator()) + + +YEAR = 2023 +AUTHOR = "zhiyiYo" +VERSION = "v0.4.2" +HELP_URL = "https://pyqt-fluent-widgets.readthedocs.io" +EXAMPLE_URL = "https://github.com/zhiyiYo/PyQt-Fluent-Widgets/tree/master/examples" +DOCUMENT_URL = "https://github.com/zhiyiYo/PyQt-Fluent-Widgets/issues" +FEEDBACK_URL = "https://github.com/zhiyiYo/PyQt-Fluent-Widgets/issues" +RELEASE_URL = "https://github.com/zhiyiYo/PyQt-Fluent-Widgets/releases/latest" + + +cfg = Config() +qconfig.load('app/config/config.json', cfg) \ No newline at end of file diff --git a/examples/gallery/app/common/icon.py b/examples/gallery/app/common/icon.py new file mode 100644 index 0000000000000000000000000000000000000000..c2520e519eb22b874fda38fab0ffe59d0ddad4fc --- /dev/null +++ b/examples/gallery/app/common/icon.py @@ -0,0 +1,25 @@ +# coding: utf-8 +from enum import Enum + +from qfluentwidgets import FluentIconBase, getIconColor, Theme + + +class Icon(FluentIconBase, Enum): + + HOME = "Home" + CHAT = "Chat" + MENU = "Menu" + LAYOUT = "Layout" + GITHUB = "Github" + MESSAGE = "Message" + CHECKBOX = "CheckBox" + DOCUMENT = "Document" + CONSTRACT = "Constract" + + def path(self, theme=Theme.AUTO): + if theme == Theme.AUTO: + c = getIconColor() + else: + c = "white" if theme == Theme.DARK else "black" + + return f"app/resource/images/icons/{self.value}_{c}.svg" diff --git a/examples/gallery/app/common/translator.py b/examples/gallery/app/common/translator.py new file mode 100644 index 0000000000000000000000000000000000000000..63b6e454668c91a4902c1599673a18103159ee48 --- /dev/null +++ b/examples/gallery/app/common/translator.py @@ -0,0 +1,14 @@ +# coding: utf-8 +from PyQt5.QtCore import QObject + + +class Translator(QObject): + + def __init__(self, parent=None): + super().__init__(parent=parent) + self.basicInput = self.tr('Basic input') + self.menus = self.tr('Menus') + self.dialogs = self.tr('Dialogs') + self.material = self.tr('Material') + self.statusInfo = self.tr('Status & info') + self.layout = self.tr('Layout') \ No newline at end of file diff --git a/examples/gallery/app/components/avatar_widget.py b/examples/gallery/app/components/avatar_widget.py new file mode 100644 index 0000000000000000000000000000000000000000..5803a2b9b6435be6b891e6244cbf669b1e25e714 --- /dev/null +++ b/examples/gallery/app/components/avatar_widget.py @@ -0,0 +1,42 @@ +# coding: utf-8 +from PyQt5.QtCore import Qt, QRect +from PyQt5.QtGui import QPainter, QImage, QBrush, QColor, QFont +from qfluentwidgets import NavigationWidget, isDarkTheme + + +class AvatarWidget(NavigationWidget): + """ Avatar widget """ + + def __init__(self, image_path, parent=None): + super().__init__(isSelectable=False, parent=parent) + self.avatar = QImage(image_path).scaled( + 24, 24, Qt.KeepAspectRatio, Qt.SmoothTransformation) + + def paintEvent(self, e): + painter = QPainter(self) + painter.setRenderHints( + QPainter.SmoothPixmapTransform | QPainter.Antialiasing) + + painter.setPen(Qt.NoPen) + + if self.isPressed: + painter.setOpacity(0.7) + + # draw background + if self.isEnter: + c = 255 if isDarkTheme() else 0 + painter.setBrush(QColor(c, c, c, 10)) + painter.drawRoundedRect(self.rect(), 5, 5) + + # draw avatar + painter.setBrush(QBrush(self.avatar)) + painter.translate(8, 6) + painter.drawEllipse(0, 0, 24, 24) + painter.translate(-8, -6) + + if not self.isCompacted: + painter.setPen(Qt.white if isDarkTheme() else Qt.black) + font = QFont('Segoe UI') + font.setPixelSize(14) + painter.setFont(font) + painter.drawText(QRect(44, 0, 255, 36), Qt.AlignVCenter, 'zhiyiYo') \ No newline at end of file diff --git a/examples/gallery/app/resource/audio/ZhiYinJi.wav b/examples/gallery/app/resource/audio/ZhiYinJi.wav new file mode 100644 index 0000000000000000000000000000000000000000..83bf05e3565b738f27ff66b7f500aeaea9b659c4 Binary files /dev/null and b/examples/gallery/app/resource/audio/ZhiYinJi.wav differ diff --git a/examples/gallery/app/resource/i18n/qfluentwidgets_hk.qm b/examples/gallery/app/resource/i18n/qfluentwidgets_hk.qm new file mode 100644 index 0000000000000000000000000000000000000000..63b1ea253be799e7dab26810befe2ae88ca5128e Binary files /dev/null and b/examples/gallery/app/resource/i18n/qfluentwidgets_hk.qm differ diff --git a/examples/gallery/app/resource/i18n/qfluentwidgets_hk.ts b/examples/gallery/app/resource/i18n/qfluentwidgets_hk.ts new file mode 100644 index 0000000000000000000000000000000000000000..6d9c8503fa23109d2324b4eff82635cf3e2d4e86 --- /dev/null +++ b/examples/gallery/app/resource/i18n/qfluentwidgets_hk.ts @@ -0,0 +1,374 @@ + + + + + + ColorDialog + + OK + 確認 + + + Cancel + 取消 + + + Edit Color + 編輯顏色 + + + Red + 紅色 + + + Blue + 藍色 + + + Green + 綠色 + + + + MessageDialog + + OK + 確認 + + + Cancel + 取消 + + + + Dialog + + OK + 確認 + + + Cancel + 取消 + + + + FolderListDialog + + Done + 完成 + + + Choose folder + 選擇文件夾 + + + Are you sure you want to delete the folder? + 是否確認刪除此文件夾? + + + If you delete the + 如果將 + + + folder and remove it from the list, the folder will no longer appear in the + list, but will not be deleted. + 文件夾從列表中移除,則該文件夾不會再出現在列表中,但不會被刪除。 + + + + SwitchSettingCard + + Off + + + + On + + + + + CustomColorSettingCard + + Custom color + 自定義顏色 + + + Default color + 默認顏色 + + + Choose color + 選擇顏色 + + + + ColorPickerButton + + Choose + 選擇 + + + + FolderListSettingCard + + Add folder + 添加文件夾 + + + Choose folder + 選擇文件夾 + + + Are you sure you want to delete the folder? + 是否確認刪除此文件夾? + + + If you delete the + 如果將 + + + folder and remove it from the list, the folder will no longer appear in the + list, but will not be deleted. + 文件夾從列表中移除,則該文件夾不會再出現在列表中,但不會被刪除。 + + + + + + SettingInterface + + Settings + 設置 + + + Music on this PC + 此 PC 上的音樂 + + + Local music library + 本地音樂庫 + + + Choose folder + 選擇文件夾 + + + Download directory + 下載目錄 + + + Personalization + 個性化 + + + Use Acrylic effect + 啟用亞克力效果 + + + Acrylic effect has better visual experience, but it may cause the window to + become stuck + 亞克力效果的視覺體驗更好,但可能導致窗口卡頓 + + + Application theme + 應用主題 + + + Theme color + 主題色 + + + Change the theme color of you application + 調整你的應用主題顏色 + + + Change the appearance of your application + 調整你的應用外觀 + + + Light + 淺色 + + + Dark + 深色 + + + Use system setting + 跟隨系統設置 + + + Interface zoom + 界面縮放 + + + Change the size of widgets and fonts + 調整組件和字體的大小 + + + Language + 語言 + + + Set your preferred language for UI + 選擇界面所使用的語言 + + + Online Music + 在線音樂 + + + Number of online music displayed on each page + 每頁顯示的在線歌曲數量 + + + Online music quality + 在線播放音質 + + + Standard quality + 流暢 + + + High quality + 高品 + + + Super quality + 超品 + + + Lossless quality + 無損 + + + Online MV quality + 在線 MV 畫質 + + + Full HD + 超清 + + + HD + 高清 + + + SD + 標清 + + + LD + 流暢 + + + Desktop Lyric + 桌面歌詞 + + + Choose font + 選擇字體 + + + Font + 字體 + + + Foreground color + 前景色 + + + Background color + 背景色 + + + Stroke color + 描邊色 + + + Stroke size + 描邊大小 + + + Alignment + 對齊方式 + + + Center aligned + 居中對齊 + + + Left aligned + 左對齊 + + + Right aligned + 右對齊 + + + Main Panel + 主面板 + + + Minimize to tray after closing + 關閉後最小化到托盤 + + + PyQt-Fluent-Widgets will continue to run in the background + PyQt-Fluent-Widgets 將在後臺繼續運行 + + + + Software update + 軟件更新 + + + Check for updates when the application starts + 在應用程序啟動時檢查更新 + + + The new version will be more stable and have more features + 新版本將更加穩定並擁有更多功能(建議啟用此選項) + + + About + 關於 + + + Open help page + 打開幫助頁面 + + + Discover new features and learn useful tips about PyQt-Fluent-Widgets + 發現新功能並了解有關 PyQt-Fluent-Widgets 的使用技巧 + + + Provide feedback + 提供反饋 + + + Help us improve PyQt-Fluent-Widgets by providing feedback + 通過提供反饋幫助我們改進 PyQt-Fluent-Widgets + + + Check update + 檢查更新 + + + Copyright + 版權所有 + + + Version + 當前版本 + + + Configuration updated successfully + 配置更新成功 + + + Configuration takes effect after restart + 配置在重啟軟件後生效 + + + \ No newline at end of file diff --git a/examples/gallery/app/resource/i18n/qfluentwidgets_zh.qm b/examples/gallery/app/resource/i18n/qfluentwidgets_zh.qm new file mode 100644 index 0000000000000000000000000000000000000000..657157dbe3fb65c76378341f7b113dc3d507bfc4 Binary files /dev/null and b/examples/gallery/app/resource/i18n/qfluentwidgets_zh.qm differ diff --git a/examples/gallery/app/resource/i18n/qfluentwidgets_zh.ts b/examples/gallery/app/resource/i18n/qfluentwidgets_zh.ts new file mode 100644 index 0000000000000000000000000000000000000000..0a26e898bccc59de1782ea655d8dd496ee4fb3b6 --- /dev/null +++ b/examples/gallery/app/resource/i18n/qfluentwidgets_zh.ts @@ -0,0 +1,371 @@ + + + + + + ColorDialog + + OK + 确认 + + + Cancel + 取消 + + + Edit Color + 编辑颜色 + + + Red + 红色 + + + Blue + 蓝色 + + + Green + 绿色 + + + + MessageDialog + + OK + 确认 + + + Cancel + 取消 + + + + Dialog + + OK + 确认 + + + Cancel + 取消 + + + + FolderListDialog + + Done + 完成 + + + Choose folder + 选择文件夹 + + + Are you sure you want to delete the folder? + 是否确认删除此文件夹? + + + If you delete the + 如果将 + + + folder and remove it from the list, the folder will no longer appear in the list, but will not be deleted. + 文件夹从列表中移除,则该文件夹不会再出现在列表中,但不会被删除。 + + + + SwitchSettingCard + + Off + + + + On + + + + + CustomColorSettingCard + + Custom color + 自定义颜色 + + + Default color + 默认颜色 + + + Choose color + 选择颜色 + + + + ColorPickerButton + + Choose + 选择 + + + + FolderListSettingCard + + Add folder + 添加文件夹 + + + Choose folder + 选择文件夹 + + + Are you sure you want to delete the folder? + 是否确认删除此文件夹? + + + If you delete the + 如果将 + + + folder and remove it from the list, the folder will no longer appear in the list, but will not be deleted. + 文件夹从列表中移除,则该文件夹不会再出现在列表中,但不会被删除。 + + + + + + SettingInterface + + Settings + 设置 + + + Music on this PC + 此 PC 上的音乐 + + + Local music library + 本地音乐库 + + + Choose folder + 选择文件夹 + + + Download directory + 下载目录 + + + Personalization + 个性化 + + + Use Acrylic effect + 启用亚克力效果 + + + Acrylic effect has better visual experience, but it may cause the window to become stuck + 亚克力效果的视觉体验更好,但可能导致窗口卡顿 + + + Application theme + 应用主题 + + + Theme color + 主题色 + + + Change the theme color of you application + 调整你的应用主题颜色 + + + Change the appearance of your application + 调整你的应用外观 + + + Light + 浅色 + + + Dark + 深色 + + + Use system setting + 跟随系统设置 + + + Interface zoom + 界面缩放 + + + Change the size of widgets and fonts + 调整组件和字体的大小 + + + Language + 语言 + + + Set your preferred language for UI + 选择界面所使用的语言 + + + Online Music + 在线音乐 + + + Number of online music displayed on each page + 每页显示的在线歌曲数量 + + + Online music quality + 在线播放音质 + + + Standard quality + 流畅 + + + High quality + 高品 + + + Super quality + 超品 + + + Lossless quality + 无损 + + + Online MV quality + 在线 MV 画质 + + + Full HD + 超清 + + + HD + 高清 + + + SD + 标清 + + + LD + 流畅 + + + Desktop Lyric + 桌面歌词 + + + Choose font + 选择字体 + + + Font + 字体 + + + Foreground color + 前景色 + + + Background color + 背景色 + + + Stroke color + 描边色 + + + Stroke size + 描边大小 + + + Alignment + 对齐方式 + + + Center aligned + 居中对齐 + + + Left aligned + 左对齐 + + + Right aligned + 右对齐 + + + Main Panel + 主面板 + + + Minimize to tray after closing + 关闭后最小化到托盘 + + + PyQt-Fluent-Widgets will continue to run in the background + PyQt-Fluent-Widgets 将在后台继续运行 + + + + Software update + 软件更新 + + + Check for updates when the application starts + 在应用程序启动时检查更新 + + + The new version will be more stable and have more features + 新版本将更加稳定并拥有更多功能(建议启用此选项) + + + About + 关于 + + + Open help page + 打开帮助页面 + + + Discover new features and learn useful tips about PyQt-Fluent-Widgets + 发现新功能并了解有关 PyQt-Fluent-Widgets 的使用技巧 + + + Provide feedback + 提供反馈 + + + Help us improve PyQt-Fluent-Widgets by providing feedback + 通过提供反馈帮助我们改进 PyQt-Fluent-Widgets + + + Check update + 检查更新 + + + Copyright + 版权所有 + + + Version + 当前版本 + + + Configuration updated successfully + 配置更新成功 + + + Configuration takes effect after restart + 配置在重启软件后生效 + + + \ No newline at end of file diff --git a/examples/gallery/app/resource/images/header.png b/examples/gallery/app/resource/images/header.png new file mode 100644 index 0000000000000000000000000000000000000000..d260a7c86ef5d3d552af34124f7f812a115c2443 Binary files /dev/null and b/examples/gallery/app/resource/images/header.png differ diff --git a/examples/gallery/app/resource/images/icons/Chat_black.svg b/examples/gallery/app/resource/images/icons/Chat_black.svg new file mode 100644 index 0000000000000000000000000000000000000000..976a59f7605b397f00679beca7f1e9512391af7f --- /dev/null +++ b/examples/gallery/app/resource/images/icons/Chat_black.svg @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/examples/gallery/app/resource/images/icons/Chat_white.svg b/examples/gallery/app/resource/images/icons/Chat_white.svg new file mode 100644 index 0000000000000000000000000000000000000000..87a8afe5ac40e462149686fba9464bb7efe53b43 --- /dev/null +++ b/examples/gallery/app/resource/images/icons/Chat_white.svg @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/examples/gallery/app/resource/images/icons/CheckBox_black.svg b/examples/gallery/app/resource/images/icons/CheckBox_black.svg new file mode 100644 index 0000000000000000000000000000000000000000..36cd567e09fd9b57de0c1d0e29cfcddd0ef57cac --- /dev/null +++ b/examples/gallery/app/resource/images/icons/CheckBox_black.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/examples/gallery/app/resource/images/icons/CheckBox_white.svg b/examples/gallery/app/resource/images/icons/CheckBox_white.svg new file mode 100644 index 0000000000000000000000000000000000000000..93ec5943090355656e4a6d5d839e2d75144c68ae --- /dev/null +++ b/examples/gallery/app/resource/images/icons/CheckBox_white.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/examples/gallery/app/resource/images/icons/Constract_black.svg b/examples/gallery/app/resource/images/icons/Constract_black.svg new file mode 100644 index 0000000000000000000000000000000000000000..999670948581051e159841cdd99d93234876148d --- /dev/null +++ b/examples/gallery/app/resource/images/icons/Constract_black.svg @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/examples/gallery/app/resource/images/icons/Constract_white.svg b/examples/gallery/app/resource/images/icons/Constract_white.svg new file mode 100644 index 0000000000000000000000000000000000000000..c0f9f6d1e4163415be318509b12e6baac0f8d521 --- /dev/null +++ b/examples/gallery/app/resource/images/icons/Constract_white.svg @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/examples/gallery/app/resource/images/icons/Document_black.svg b/examples/gallery/app/resource/images/icons/Document_black.svg new file mode 100644 index 0000000000000000000000000000000000000000..650b89a5eebf0644f9cdf7bca092128f251a2406 --- /dev/null +++ b/examples/gallery/app/resource/images/icons/Document_black.svg @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/examples/gallery/app/resource/images/icons/Document_white.svg b/examples/gallery/app/resource/images/icons/Document_white.svg new file mode 100644 index 0000000000000000000000000000000000000000..b7dddeb71b6358d602d046d74d5e018134bdeb8e --- /dev/null +++ b/examples/gallery/app/resource/images/icons/Document_white.svg @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/examples/gallery/app/resource/images/icons/Home_black.svg b/examples/gallery/app/resource/images/icons/Home_black.svg new file mode 100644 index 0000000000000000000000000000000000000000..2e2426e66dc7602d5c72e2f4f09bf74a0d47fbe3 --- /dev/null +++ b/examples/gallery/app/resource/images/icons/Home_black.svg @@ -0,0 +1,6 @@ + +. + + \ No newline at end of file diff --git a/examples/gallery/app/resource/images/icons/Home_white.svg b/examples/gallery/app/resource/images/icons/Home_white.svg new file mode 100644 index 0000000000000000000000000000000000000000..29b0d9557f91dcc21fe871b688efdfb196499c6e --- /dev/null +++ b/examples/gallery/app/resource/images/icons/Home_white.svg @@ -0,0 +1,6 @@ + +. + + \ No newline at end of file diff --git a/examples/gallery/app/resource/images/icons/Layout_black.svg b/examples/gallery/app/resource/images/icons/Layout_black.svg new file mode 100644 index 0000000000000000000000000000000000000000..afc2c881c28e2429f7d1a18679d11870f4f7f864 --- /dev/null +++ b/examples/gallery/app/resource/images/icons/Layout_black.svg @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/examples/gallery/app/resource/images/icons/Layout_white.svg b/examples/gallery/app/resource/images/icons/Layout_white.svg new file mode 100644 index 0000000000000000000000000000000000000000..05b0de97c5d0d2325ed8651f30b50ecc938bdd09 --- /dev/null +++ b/examples/gallery/app/resource/images/icons/Layout_white.svg @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/examples/gallery/app/resource/images/icons/Menu_black.svg b/examples/gallery/app/resource/images/icons/Menu_black.svg new file mode 100644 index 0000000000000000000000000000000000000000..e590822a07a604db06cb64e9371bf076e6b631ee --- /dev/null +++ b/examples/gallery/app/resource/images/icons/Menu_black.svg @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/examples/gallery/app/resource/images/icons/Menu_white.svg b/examples/gallery/app/resource/images/icons/Menu_white.svg new file mode 100644 index 0000000000000000000000000000000000000000..4fd70ed2f82e6f8e5bc74e245703dfda5f1cfaf9 --- /dev/null +++ b/examples/gallery/app/resource/images/icons/Menu_white.svg @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/examples/gallery/app/resource/images/icons/Message_black.svg b/examples/gallery/app/resource/images/icons/Message_black.svg new file mode 100644 index 0000000000000000000000000000000000000000..f5f13eb3c10a3ccf5bafdff683c1d5c303dcfba1 --- /dev/null +++ b/examples/gallery/app/resource/images/icons/Message_black.svg @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/examples/gallery/app/resource/images/icons/Message_white.svg b/examples/gallery/app/resource/images/icons/Message_white.svg new file mode 100644 index 0000000000000000000000000000000000000000..49958fb1bc40e2bdf11bda69c453673070872cac --- /dev/null +++ b/examples/gallery/app/resource/images/icons/Message_white.svg @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/examples/gallery/app/resource/images/icons/github_black.svg b/examples/gallery/app/resource/images/icons/github_black.svg new file mode 100644 index 0000000000000000000000000000000000000000..37fa923df33faeccc4b55228046b5b079a82926d --- /dev/null +++ b/examples/gallery/app/resource/images/icons/github_black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/gallery/app/resource/images/icons/github_white.svg b/examples/gallery/app/resource/images/icons/github_white.svg new file mode 100644 index 0000000000000000000000000000000000000000..d5e64918546d9dd87c9742239deae2397e634343 --- /dev/null +++ b/examples/gallery/app/resource/images/icons/github_white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/gallery/app/resource/images/kunkun.png b/examples/gallery/app/resource/images/kunkun.png new file mode 100644 index 0000000000000000000000000000000000000000..00a4cfd28a2b8879e249456540fc2a0eb574d23f Binary files /dev/null and b/examples/gallery/app/resource/images/kunkun.png differ diff --git a/examples/gallery/app/resource/images/logo.png b/examples/gallery/app/resource/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0bae40eedbdba654e5c5248c923ca700287205a8 Binary files /dev/null and b/examples/gallery/app/resource/images/logo.png differ diff --git a/examples/gallery/app/resource/images/shoko.png b/examples/gallery/app/resource/images/shoko.png new file mode 100644 index 0000000000000000000000000000000000000000..7d8894bb8779c83df005c97fb4659dce2eb82484 Binary files /dev/null and b/examples/gallery/app/resource/images/shoko.png differ diff --git a/examples/gallery/app/resource/qss/dark/gallery_interface.qss b/examples/gallery/app/resource/qss/dark/gallery_interface.qss new file mode 100644 index 0000000000000000000000000000000000000000..0cf908daec9d13107278a330374794579d12e091 --- /dev/null +++ b/examples/gallery/app/resource/qss/dark/gallery_interface.qss @@ -0,0 +1,80 @@ +GalleryInterface, +ToolBar, +#view { + background-color: transparent; +} + +QScrollArea { + border: none; +} + +ToolBar > #titleLabel { + font: 28px 'Segoe UI SemiBold', 'Microsoft YaHei'; + font-weight: bold; + background-color: transparent; + color: white; +} + +ToolBar > #subtitleLabel { + font: 12px 'Segoe UI', 'Microsoft YaHei'; + background-color: transparent; + color: white; +} + +ExampleCard { + background-color: transparent; +} + +ExampleCard>#card { + border: 1px solid rgb(32, 32, 32); + border-radius: 10px; + background-color: rgb(36, 36, 36); +} + +ExampleCard>#titleLabel { + font: 14px 'Segoe UI SemiBold', 'Microsoft YaHei'; + background-color: transparent; + color: white; +} + +#sourceWidget { + background-color: rgb(51, 51, 51); + border-top: 1px solid rgb(36, 36, 36); + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; +} + +#sourcePathLabel { + color: white; + font: 14px 'Segoe UI', 'Microsoft YaHei'; +} + +QScrollBar { + background: transparent; + width: 4px; + margin-top: 32px; + margin-bottom: 0; + padding-right: 2px; +} + +/*隐藏上箭头*/ +QScrollBar::sub-line { + background: transparent; +} + +/*隐藏下箭头*/ +QScrollBar::add-line { + background: transparent; +} + +QScrollBar::handle { + background: rgb(122, 122, 122); + border: 2px solid rgb(128, 128, 128); + border-radius: 1px; + min-height: 32px; +} + +QScrollBar::add-page:vertical, +QScrollBar::sub-page:vertical { + background: none; +} \ No newline at end of file diff --git a/examples/gallery/app/resource/qss/dark/main_window.qss b/examples/gallery/app/resource/qss/dark/main_window.qss new file mode 100644 index 0000000000000000000000000000000000000000..ed9bac3b7511ed36ee4a3d5406990f6f04f26975 --- /dev/null +++ b/examples/gallery/app/resource/qss/dark/main_window.qss @@ -0,0 +1,53 @@ +StackedWidget { + border: 1px solid rgb(29, 29, 29); + border-right: none; + border-bottom: none; + border-top-left-radius: 10px; + background-color: rgb(39, 39, 39); +} + + +MainWindow { + background-color: rgb(32, 32, 32); +} + +CustomTitleBar { + background-color: transparent; +} + +CustomTitleBar > QLabel, +Widget > QLabel { + color: white; +} + + +CustomTitleBar>QLabel#titleLabel { + background: transparent; + font: 13px 'Segoe UI'; + padding: 0 4px +} + +MinimizeButton { + qproperty-normalColor: white; + qproperty-normalBackgroundColor: transparent; + qproperty-hoverColor: white; + qproperty-hoverBackgroundColor: rgba(255, 255, 255, 26); + qproperty-pressedColor: white; + qproperty-pressedBackgroundColor: rgba(255, 255, 255, 51) +} + + +MaximizeButton { + qproperty-normalColor: white; + qproperty-normalBackgroundColor: transparent; + qproperty-hoverColor: white; + qproperty-hoverBackgroundColor: rgba(255, 255, 255, 26); + qproperty-pressedColor: white; + qproperty-pressedBackgroundColor: rgba(255, 255, 255, 51) +} + +CloseButton { + qproperty-normalColor: white; + qproperty-normalBackgroundColor: transparent; +} + diff --git a/examples/gallery/app/resource/qss/dark/setting_interface.qss b/examples/gallery/app/resource/qss/dark/setting_interface.qss new file mode 100644 index 0000000000000000000000000000000000000000..df9b41fffd9b5181e0acc5d5e4f0d4e21793d906 --- /dev/null +++ b/examples/gallery/app/resource/qss/dark/setting_interface.qss @@ -0,0 +1,48 @@ +SettingInterface, #scrollWidget { + background-color: transparent; +} + +QScrollArea { + border: none; + background-color: transparent; +} + + +/* 标签 */ +QLabel#settingLabel { + font: 33px 'Microsoft YaHei Light'; + background-color: transparent; + color: white; +} + + +/* 滚动条 */ +QScrollBar { + background: transparent; + width: 4px; + margin-top: 32px; + margin-bottom: 0; + padding-right: 2px; +} + +/*隐藏上箭头*/ +QScrollBar::sub-line { + background: transparent; +} + +/*隐藏下箭头*/ +QScrollBar::add-line { + background: transparent; +} + +QScrollBar::handle { + background: rgb(122, 122, 122); + border: 2px solid rgb(128, 128, 128); + border-radius: 1px; + min-height: 32px; +} + +QScrollBar::add-page:vertical, +QScrollBar::sub-page:vertical { + background: none; +} \ No newline at end of file diff --git a/examples/gallery/app/resource/qss/light/gallery_interface.qss b/examples/gallery/app/resource/qss/light/gallery_interface.qss new file mode 100644 index 0000000000000000000000000000000000000000..55df1ddaae990d61ac92fdfb361671a9773d1054 --- /dev/null +++ b/examples/gallery/app/resource/qss/light/gallery_interface.qss @@ -0,0 +1,77 @@ +GalleryInterface, ToolBar, #view { + background-color: transparent; +} + +QScrollArea { + border: none; +} + +ToolBar > #titleLabel { + font: 28px 'Segoe UI SemiBold', 'Microsoft YaHei'; + background-color: transparent; + color: black; +} + +ToolBar > #subtitleLabel { + font: 12px 'Segoe UI', 'Microsoft YaHei'; + background-color: transparent; + color: rgb(95, 95, 95); +} + +ExampleCard { + background-color: transparent; +} + +ExampleCard > #card { + border: 1px solid rgb(234, 234, 234); + border-radius: 10px; + background-color: rgb(243, 243, 243); +} + +ExampleCard > #titleLabel { + font: 14px 'Segoe UI SemiBold', 'Microsoft YaHei'; + background-color: transparent; + color: black; +} + +#sourceWidget { + background-color: rgb(253, 253, 253); + border-top: 1px solid rgb(234, 234, 234); + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; +} + +#sourcePathLabel { + color: black; + font: 14px 'Segoe UI', 'Microsoft YaHei'; +} + +QScrollBar { + background: transparent; + width: 4px; + margin-top: 32px; + margin-bottom: 0; + padding-right: 2px; +} + +/*隐藏上箭头*/ +QScrollBar::sub-line { + background: transparent; +} + +/*隐藏下箭头*/ +QScrollBar::add-line { + background: transparent; +} + +QScrollBar::handle { + background: rgb(122, 122, 122); + border: 2px solid rgb(128, 128, 128); + border-radius: 1px; + min-height: 32px; +} + +QScrollBar::add-page:vertical, +QScrollBar::sub-page:vertical { + background: none; +} \ No newline at end of file diff --git a/examples/gallery/app/resource/qss/light/main_window.qss b/examples/gallery/app/resource/qss/light/main_window.qss new file mode 100644 index 0000000000000000000000000000000000000000..758578d7b1d580a2949c5902405bbe934b02b1c5 --- /dev/null +++ b/examples/gallery/app/resource/qss/light/main_window.qss @@ -0,0 +1,42 @@ +StackedWidget { + border: 1px solid rgb(229, 229, 229); + border-right: none; + border-bottom: none; + border-top-left-radius: 10px; + background-color: rgb(249, 249, 249); +} + +MainWindow { + background-color: rgb(243, 243, 243); +} + +CustomTitleBar > QLabel#titleLabel { + color: black; + background: transparent; + font: 13px 'Segoe UI'; + padding: 0 4px +} + +MinimizeButton { + qproperty-normalColor: black; + qproperty-normalBackgroundColor: transparent; + qproperty-hoverColor: black; + qproperty-hoverBackgroundColor: rgba(0, 0, 0, 26); + qproperty-pressedColor: black; + qproperty-pressedBackgroundColor: rgba(0, 0, 0, 51) +} + + +MaximizeButton { + qproperty-normalColor: black; + qproperty-normalBackgroundColor: transparent; + qproperty-hoverColor: black; + qproperty-hoverBackgroundColor: rgba(0, 0, 0, 26); + qproperty-pressedColor: black; + qproperty-pressedBackgroundColor: rgba(0, 0, 0, 51) +} + +CloseButton { + qproperty-normalColor: black; + qproperty-normalBackgroundColor: transparent; +} \ No newline at end of file diff --git a/examples/gallery/app/resource/qss/light/setting_interface.qss b/examples/gallery/app/resource/qss/light/setting_interface.qss new file mode 100644 index 0000000000000000000000000000000000000000..ce639874c2a40be58fc6cf1bfbf9b918477f0057 --- /dev/null +++ b/examples/gallery/app/resource/qss/light/setting_interface.qss @@ -0,0 +1,47 @@ +SettingInterface, #scrollWidget { + background-color: transparent; +} + +QScrollArea { + background-color: transparent; + border: none; +} + + +/* 标签 */ +QLabel#settingLabel { + font: 33px 'Microsoft YaHei Light'; + background-color: transparent; +} + + +/* 滚动条 */ +QScrollBar { + background: transparent; + width: 4px; + margin-top: 32px; + margin-bottom: 0; + padding-right: 2px; +} + +/*隐藏上箭头*/ +QScrollBar::sub-line { + background: transparent; +} + +/*隐藏下箭头*/ +QScrollBar::add-line { + background: transparent; +} + +QScrollBar::handle { + background: rgb(122, 122, 122); + border: 2px solid rgb(128, 128, 128); + border-radius: 1px; + min-height: 32px; +} + +QScrollBar::add-page:vertical, +QScrollBar::sub-page:vertical { + background: none; +} \ No newline at end of file diff --git a/examples/gallery/app/view/basic_input_interface.py b/examples/gallery/app/view/basic_input_interface.py new file mode 100644 index 0000000000000000000000000000000000000000..bcf9d9c94e7fb96817088b40b53fe3c63d0f2199 --- /dev/null +++ b/examples/gallery/app/view/basic_input_interface.py @@ -0,0 +1,103 @@ +# coding:utf-8 +from PyQt5.QtCore import Qt, QSize +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QButtonGroup +from PyQt5.QtMultimedia import QSound +from qfluentwidgets import (PushButton, ToolButton, PrimaryPushButton, HyperlinkButton, + ComboBox, RadioButton, CheckBox, Slider) + +from .gallery_interface import GalleryInterface +from ..common.translator import Translator + + +class BasicInputInterface(GalleryInterface): + """ Basic input interface """ + + def __init__(self, parent=None): + translator = Translator() + super().__init__( + title=translator.basicInput, + subtitle='qfluentwidgets.components.widgets', + parent=parent + ) + + self.addExampleCard( + self.tr('A simple button with text content'), + PushButton('Standard push button'), + 'https://github.com/zhiyiYo/PyQt-Fluent-Widgets/blob/master/examples/button/demo.py' + ) + + button = ToolButton('app/resource/images/kunkun.png') + button.setIconSize(QSize(40, 40)) + button.clicked.connect(lambda: QSound.play( + 'app/resource/audio/ZhiYinJi.wav')) + button.resize(70, 70) + self.addExampleCard( + self.tr('A button with graphical content'), + button, + 'https://github.com/zhiyiYo/PyQt-Fluent-Widgets/blob/master/examples/button/demo.py' + ) + + self.addExampleCard( + self.tr('Accent style applied to button'), + PrimaryPushButton('Accent style button'), + 'https://github.com/zhiyiYo/PyQt-Fluent-Widgets/blob/master/examples/button/demo.py' + ) + + self.addExampleCard( + self.tr('A hyperlink button that navigates to a URI'), + HyperlinkButton('http://github.com', 'GitHub home page'), + 'https://github.com/zhiyiYo/PyQt-Fluent-Widgets/blob/master/examples/button/demo.py' + ) + + self.addExampleCard( + self.tr('A 2-state CheckBox'), + CheckBox('Two-state CheckBox'), + 'https://github.com/zhiyiYo/PyQt-Fluent-Widgets/blob/master/examples/check_box/demo.py' + ) + + checkBox = CheckBox('Three-state CheckBox') + checkBox.setTristate(True) + self.addExampleCard( + self.tr('A 3-state CheckBox'), + checkBox, + 'https://github.com/zhiyiYo/PyQt-Fluent-Widgets/blob/master/examples/check_box/demo.py' + ) + + comboBox = ComboBox() + comboBox.addItems(['shoko 🥰', '西宫硝子 😊', '一级棒卡哇伊的硝子酱 😘']) + comboBox.setCurrentIndex(0) + comboBox.setMinimumWidth(210) + self.addExampleCard( + self.tr('A ComboBox with items'), + comboBox, + 'https://github.com/zhiyiYo/PyQt-Fluent-Widgets/blob/master/examples/combo_box/demo.py' + ) + + radioWidget = QWidget() + radioLayout = QVBoxLayout(radioWidget) + radioLayout.setContentsMargins(2, 0, 0, 0) + radioLayout.setSpacing(15) + radioButton1 = RadioButton('Star Platinum', radioWidget) + radioButton2 = RadioButton('Crazy Diamond', radioWidget) + radioButton3 = RadioButton('Soft and Wet', radioWidget) + buttonGroup = QButtonGroup(radioWidget) + buttonGroup.addButton(radioButton1) + buttonGroup.addButton(radioButton2) + buttonGroup.addButton(radioButton3) + radioLayout.addWidget(radioButton1) + radioLayout.addWidget(radioButton2) + radioLayout.addWidget(radioButton3) + self.addExampleCard( + self.tr('A group of RadioButton controls in a button group'), + radioWidget, + 'https://github.com/zhiyiYo/PyQt-Fluent-Widgets/blob/master/examples/radio_button/demo.py' + ) + + slider = Slider(Qt.Horizontal) + slider.setRange(0, 100) + slider.setFixedWidth(200) + self.addExampleCard( + self.tr('A simple horizontal slider'), + slider, + 'https://github.com/zhiyiYo/PyQt-Fluent-Widgets/blob/master/examples/slider/demo.py' + ) diff --git a/examples/gallery/app/view/gallery_interface.py b/examples/gallery/app/view/gallery_interface.py new file mode 100644 index 0000000000000000000000000000000000000000..8174f304df7b07953f4d84e5d58bc0fc21f2fedb --- /dev/null +++ b/examples/gallery/app/view/gallery_interface.py @@ -0,0 +1,171 @@ +# coding:utf-8 +from PyQt5.QtCore import Qt, pyqtSignal, QUrl, QEvent +from PyQt5.QtGui import QDesktopServices +from PyQt5.QtWidgets import QWidget, QLabel, QVBoxLayout, QHBoxLayout, QFrame + +from qfluentwidgets import (ScrollArea, PushButton, ToolButton, FluentIcon, + isDarkTheme, IconWidget, Theme) +from ..common.icon import Icon +from ..common.config import cfg, FEEDBACK_URL, DOCUMENT_URL, EXAMPLE_URL + + +class ToolBar(QWidget): + """ Tool bar """ + + def __init__(self, title, subtitle, parent=None): + super().__init__(parent=parent) + self.titleLabel = QLabel(title, self) + self.subtitleLabel = QLabel(subtitle, self) + + self.documentButton = PushButton( + self.tr('Documentation'), self, Icon.DOCUMENT) + self.sourceButton = PushButton(self.tr('Source'), self, Icon.GITHUB) + self.themeButton = ToolButton(Icon.CONSTRACT, self) + self.feedbackButton = ToolButton(FluentIcon.FEEDBACK, self) + + self.vBoxLayout = QVBoxLayout(self) + self.buttonLayout = QHBoxLayout() + + self.__initWidget() + + def __initWidget(self): + self.setFixedHeight(138) + self.vBoxLayout.setSpacing(0) + self.vBoxLayout.setContentsMargins(36, 22, 36, 12) + self.vBoxLayout.addWidget(self.titleLabel) + self.vBoxLayout.addSpacing(4) + self.vBoxLayout.addWidget(self.subtitleLabel) + self.vBoxLayout.addSpacing(4) + self.vBoxLayout.addLayout(self.buttonLayout, 1) + self.vBoxLayout.setAlignment(Qt.AlignTop) + + self.buttonLayout.setSpacing(4) + self.buttonLayout.setContentsMargins(0, 0, 0, 0) + self.buttonLayout.addWidget(self.documentButton, 0, Qt.AlignLeft) + self.buttonLayout.addWidget(self.sourceButton, 0, Qt.AlignLeft) + self.buttonLayout.addStretch(1) + self.buttonLayout.addWidget(self.themeButton, 0, Qt.AlignRight) + self.buttonLayout.addWidget(self.feedbackButton, 0, Qt.AlignRight) + self.buttonLayout.setAlignment(Qt.AlignVCenter | Qt.AlignLeft) + + self.titleLabel.setObjectName('titleLabel') + self.subtitleLabel.setObjectName('subtitleLabel') + + self.themeButton.clicked.connect(self.toggleTheme) + self.documentButton.clicked.connect( + lambda: QDesktopServices.openUrl(QUrl(DOCUMENT_URL))) + self.sourceButton.clicked.connect( + lambda: QDesktopServices.openUrl(QUrl(EXAMPLE_URL))) + self.feedbackButton.clicked.connect( + lambda: QDesktopServices.openUrl(QUrl(FEEDBACK_URL))) + + def toggleTheme(self): + theme = Theme.LIGHT if isDarkTheme() else Theme.DARK + cfg.set(cfg.themeMode, theme) + + +class ExampleCard(QWidget): + """ Example card """ + + def __init__(self, title, widget: QWidget, sourcePath, parent=None): + super().__init__(parent=parent) + self.widget = widget + self.titleLabel = QLabel(title, self) + self.card = QFrame(self) + + self.sourceWidget = QFrame(self.card) + self.sourcePath = sourcePath + self.sourcePathLabel = QLabel(self.tr('Source code'), self.sourceWidget) + self.linkIcon = IconWidget(FluentIcon.LINK, self.sourceWidget) + + self.vBoxLayout = QVBoxLayout(self) + self.cardLayout = QVBoxLayout(self.card) + self.topLayout = QHBoxLayout() + self.bottomLayout = QHBoxLayout(self.sourceWidget) + + self.__initWidget() + + def __initWidget(self): + self.linkIcon.setFixedSize(16, 16) + self.__initLayout() + + self.sourceWidget.setCursor(Qt.PointingHandCursor) + self.sourceWidget.installEventFilter(self) + + self.titleLabel.setObjectName('titleLabel') + self.card.setObjectName('card') + self.sourcePathLabel.setObjectName('sourcePathLabel') + self.sourceWidget.setObjectName('sourceWidget') + + def __initLayout(self): + self.vBoxLayout.setSizeConstraint(QVBoxLayout.SetMinimumSize) + self.cardLayout.setSizeConstraint(QVBoxLayout.SetMinimumSize) + self.topLayout.setSizeConstraint(QHBoxLayout.SetMinimumSize) + + self.vBoxLayout.setSpacing(12) + self.vBoxLayout.setContentsMargins(0, 0, 0, 0) + self.topLayout.setContentsMargins(12, 12, 12, 12) + self.bottomLayout.setContentsMargins(18, 18, 18, 18) + self.cardLayout.setContentsMargins(0, 0, 0, 0) + + self.vBoxLayout.addWidget(self.titleLabel, 0, Qt.AlignTop) + self.vBoxLayout.addWidget(self.card, 0, Qt.AlignTop) + self.vBoxLayout.setAlignment(Qt.AlignTop) + + self.cardLayout.setSpacing(0) + self.cardLayout.setAlignment(Qt.AlignTop) + self.cardLayout.addLayout(self.topLayout, 0) + self.cardLayout.addWidget(self.sourceWidget, 0, Qt.AlignBottom) + + self.widget.setParent(self.card) + self.topLayout.addWidget(self.widget) + self.topLayout.addStretch(1) + self.widget.show() + + self.bottomLayout.addWidget(self.sourcePathLabel, 0, Qt.AlignLeft) + self.bottomLayout.addStretch(1) + self.bottomLayout.addWidget(self.linkIcon, 0, Qt.AlignRight) + self.bottomLayout.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + + def eventFilter(self, obj, e): + if obj is self.sourceWidget: + if e.type() == QEvent.MouseButtonRelease: + QDesktopServices.openUrl(QUrl(self.sourcePath)) + + return super().eventFilter(obj, e) + + +class GalleryInterface(ScrollArea): + """ Gallery interface """ + + def __init__(self, title: str, subtitle: str, parent=None): + super().__init__(parent=parent) + self.view = QWidget(self) + self.toolBar = ToolBar(title, subtitle, self) + self.vBoxLayout = QVBoxLayout(self.view) + + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.setViewportMargins(0, self.toolBar.height(), 0, 0) + self.setWidget(self.view) + self.setWidgetResizable(True) + + self.vBoxLayout.setSpacing(30) + self.vBoxLayout.setAlignment(Qt.AlignTop) + self.vBoxLayout.setContentsMargins(36, 20, 36, 36) + + self.__setQss() + cfg.themeChanged.connect(self.__setQss) + + def addExampleCard(self, title, widget, sourcePath: str): + card = ExampleCard(title, widget, sourcePath, self.view) + self.vBoxLayout.addWidget(card, 0, Qt.AlignTop) + + def resizeEvent(self, e): + super().resizeEvent(e) + self.toolBar.resize(self.width(), self.toolBar.height()) + + def __setQss(self): + self.view.setObjectName('view') + theme = 'dark' if isDarkTheme() else 'light' + with open(f'app/resource/qss/{theme}/gallery_interface.qss', encoding='utf-8') as f: + self.setStyleSheet(f.read()) diff --git a/examples/gallery/app/view/main_window.py b/examples/gallery/app/view/main_window.py new file mode 100644 index 0000000000000000000000000000000000000000..d4307ece675b07ea18bd3c37dfd32a9dac828cec --- /dev/null +++ b/examples/gallery/app/view/main_window.py @@ -0,0 +1,196 @@ +# coding: utf-8 +from PyQt5.QtCore import Qt, pyqtSignal +from PyQt5.QtGui import QIcon +from PyQt5.QtWidgets import QApplication, QStackedWidget, QHBoxLayout, QFrame, QWidget + +from qfluentwidgets import (NavigationInterface, NavigationItemPostion, MessageBox, + isDarkTheme) +from qfluentwidgets import FluentIcon as FIF +from qframelesswindow import FramelessWindow + +from .title_bar import CustomTitleBar +from .setting_interface import SettingInterface, cfg +from .basic_input_interface import BasicInputInterface +from ..components.avatar_widget import AvatarWidget +from ..common.icon import Icon + + +class StackedWidget(QFrame): + """ Stacked widget """ + + currentWidgetChanged = pyqtSignal(QWidget) + + def __init__(self, parent=None): + super().__init__(parent=parent) + self.hBoxLayout = QHBoxLayout(self) + self.view = QStackedWidget(self) + + self.hBoxLayout.setContentsMargins(0, 0, 0, 0) + self.hBoxLayout.addWidget(self.view) + + self.view.currentChanged.connect( + lambda i: self.currentWidgetChanged.emit(self.view.widget(i))) + + def addWidget(self, widget): + """ add widget to view """ + self.view.addWidget(widget) + + def setCurrentWidget(self, widget): + self.view.setCurrentWidget(widget) + + def setCurrentIndex(self, index): + self.view.setCurrentIndex(index) + + +class MainWindow(FramelessWindow): + + def __init__(self): + super().__init__() + self.setTitleBar(CustomTitleBar(self)) + self.hBoxLayout = QHBoxLayout(self) + self.widgetLayout = QHBoxLayout() + + self.stackWidget = StackedWidget(self) + self.navigationInterface = NavigationInterface(self, True, True) + + # create sub interface + self.basicInputInterface = BasicInputInterface(self) + self.settingInterface = SettingInterface(self) + + self.stackWidget.addWidget(self.basicInputInterface) + self.stackWidget.addWidget(self.settingInterface) + + # initialize layout + self.initLayout() + + # add items to navigation interface + self.initNavigation() + + self.initWindow() + + def initLayout(self): + self.hBoxLayout.setSpacing(0) + self.hBoxLayout.setContentsMargins(0, 0, 0, 0) + self.hBoxLayout.addWidget(self.navigationInterface) + self.hBoxLayout.addLayout(self.widgetLayout) + self.hBoxLayout.setStretchFactor(self.widgetLayout, 1) + + self.widgetLayout.addWidget(self.stackWidget) + self.widgetLayout.setContentsMargins(0, 48, 0, 0) + + self.navigationInterface.displayModeChanged.connect( + self.titleBar.raise_) + self.titleBar.raise_() + + def initNavigation(self): + self.basicInputInterface.setObjectName('basicInterface') + self.settingInterface.setObjectName('settingsInterface') + + self.navigationInterface.addItem( + routeKey='Home', + icon=Icon.HOME, + text=self.tr('Home'), + onClick=print + ) + self.navigationInterface.addSeparator() + + self.navigationInterface.addItem( + routeKey=self.basicInputInterface.objectName(), + icon=Icon.CHECKBOX, + text=self.tr('Basic input'), + onClick=lambda: self.switchTo(self.basicInputInterface), + position=NavigationItemPostion.SCROLL + ) + self.navigationInterface.addItem( + routeKey='Dialogs', + icon=Icon.MESSAGE, + text=self.tr('Dialogs'), + onClick=print, + position=NavigationItemPostion.SCROLL + ) + self.navigationInterface.addItem( + routeKey='Status&info', + icon=Icon.LAYOUT, + text=self.tr('Layout'), + onClick=print, + position=NavigationItemPostion.SCROLL + ) + self.navigationInterface.addItem( + routeKey='Menus', + icon=Icon.MENU, + text=self.tr('Menus'), + onClick=print, + position=NavigationItemPostion.SCROLL + ) + self.navigationInterface.addItem( + routeKey='Material', + icon=FIF.PALETTE, + text=self.tr('Material'), + onClick=print, + position=NavigationItemPostion.SCROLL + ) + self.navigationInterface.addItem( + routeKey='Layout', + icon=Icon.CHAT, + text=self.tr('Status & info'), + onClick=print, + position=NavigationItemPostion.SCROLL + ) + + # add custom widget to bottom + self.navigationInterface.addWidget( + routeKey='avatar', + widget=AvatarWidget('app/resource/images/shoko.png'), + onClick=self.showMessageBox, + position=NavigationItemPostion.BOTTOM + ) + + self.navigationInterface.addItem( + routeKey=self.settingInterface.objectName(), + icon=FIF.SETTING, + text='Settings', + onClick=lambda: self.switchTo(self.settingInterface), + position=NavigationItemPostion.BOTTOM + ) + + #!IMPORTANT: don't forget to set the default route key if you enable the return button + self.navigationInterface.setDefaultRouteKey( + self.basicInputInterface.objectName()) + + self.stackWidget.currentWidgetChanged.connect( + lambda w: self.navigationInterface.setCurrentItem(w.objectName())) + self.navigationInterface.setCurrentItem(self.basicInputInterface.objectName()) + self.stackWidget.setCurrentIndex(0) + + def initWindow(self): + self.resize(900, 700) + self.setWindowIcon(QIcon('app/resource/images/logo.png')) + self.setWindowTitle('PyQt-Fluent-Widgets') + self.titleBar.setAttribute(Qt.WA_StyledBackground) + + desktop = QApplication.desktop().availableGeometry() + w, h = desktop.width(), desktop.height() + self.move(w//2 - self.width()//2, h//2 - self.height()//2) + + cfg.themeChanged.connect(self.setQss) + self.setQss() + + def setQss(self): + color = 'dark' if isDarkTheme() else 'light' + with open(f'app/resource/qss/{color}/main_window.qss', encoding='utf-8') as f: + self.setStyleSheet(f.read()) + + def switchTo(self, widget): + self.stackWidget.setCurrentWidget(widget) + + def resizeEvent(self, e): + self.titleBar.move(46, 0) + self.titleBar.resize(self.width()-46, self.titleBar.height()) + + def showMessageBox(self): + w = MessageBox( + 'This is a help message', + 'You clicked a customized navigation widget. You can add more custom widgets by calling `NavigationInterface.addWidget()` 😉', + self + ) + w.exec() diff --git a/examples/gallery/app/view/setting_interface.py b/examples/gallery/app/view/setting_interface.py new file mode 100644 index 0000000000000000000000000000000000000000..b0c64605a109f6e820c29704bfca62fca24a57fa --- /dev/null +++ b/examples/gallery/app/view/setting_interface.py @@ -0,0 +1,219 @@ +# coding:utf-8 +from qfluentwidgets import (SettingCardGroup, SwitchSettingCard, FolderListSettingCard, + OptionsSettingCard, PushSettingCard, + HyperlinkCard, PrimaryPushSettingCard, ScrollArea, + ComboBoxSettingCard, ExpandLayout, Theme, ToastToolTip, CustomColorSettingCard, + setTheme, setThemeColor) +from qfluentwidgets import FluentIcon as FIF +from PyQt5.QtCore import Qt, pyqtSignal, QUrl, QStandardPaths +from PyQt5.QtGui import QDesktopServices +from PyQt5.QtWidgets import QWidget, QLabel, QFileDialog + +from ..common.config import cfg, HELP_URL, FEEDBACK_URL, AUTHOR, VERSION, YEAR + + +class SettingInterface(ScrollArea): + """ Setting interface """ + + checkUpdateSig = pyqtSignal() + musicFoldersChanged = pyqtSignal(list) + acrylicEnableChanged = pyqtSignal(bool) + downloadFolderChanged = pyqtSignal(str) + minimizeToTrayChanged = pyqtSignal(bool) + + def __init__(self, parent=None): + super().__init__(parent=parent) + self.scrollWidget = QWidget() + self.expandLayout = ExpandLayout(self.scrollWidget) + + # setting label + self.settingLabel = QLabel(self.tr("Settings"), self) + + # music folders + self.musicInThisPCGroup = SettingCardGroup( + self.tr("Music on this PC"), self.scrollWidget) + self.musicFolderCard = FolderListSettingCard( + cfg.musicFolders, + self.tr("Local music library"), + directory=QStandardPaths.writableLocation(QStandardPaths.MusicLocation), + parent=self.musicInThisPCGroup + ) + self.downloadFolderCard = PushSettingCard( + self.tr('Choose folder'), + FIF.DOWNLOAD, + self.tr("Download directory"), + cfg.get(cfg.downloadFolder), + self.musicInThisPCGroup + ) + + # personalization + self.personalGroup = SettingCardGroup(self.tr('Personalization'), self.scrollWidget) + self.themeCard = OptionsSettingCard( + cfg.themeMode, + FIF.BRUSH, + self.tr('Application theme'), + self.tr("Change the appearance of your application"), + texts=[ + self.tr('Light'), self.tr('Dark'), + self.tr('Use system setting') + ], + parent=self.personalGroup + ) + self.themeColorCard=CustomColorSettingCard( + cfg.themeColor, + FIF.PALETTE, + self.tr('Theme color'), + self.tr('Change the theme color of you application'), + self.personalGroup + ) + self.zoomCard = OptionsSettingCard( + cfg.dpiScale, + FIF.ZOOM, + self.tr("Interface zoom"), + self.tr("Change the size of widgets and fonts"), + texts=[ + "100%", "125%", "150%", "175%", "200%", + self.tr("Use system setting") + ], + parent=self.personalGroup + ) + self.languageCard = ComboBoxSettingCard( + cfg.language, + FIF.LANGUAGE, + self.tr('Language'), + self.tr('Set your preferred language for UI'), + texts=['简体中文', '繁體中文', 'English', self.tr('Use system setting')], + parent=self.personalGroup + ) + + # update software + self.updateSoftwareGroup = SettingCardGroup(self.tr("Software update"), self.scrollWidget) + self.updateOnStartUpCard = SwitchSettingCard( + FIF.UPDATE, + self.tr('Check for updates when the application starts'), + self.tr('The new version will be more stable and have more features'), + configItem=cfg.checkUpdateAtStartUp, + parent=self.updateSoftwareGroup + ) + + # application + self.aboutGroup = SettingCardGroup(self.tr('About'), self.scrollWidget) + self.helpCard = HyperlinkCard( + HELP_URL, + self.tr('Open help page'), + FIF.HELP, + self.tr('Help'), + self.tr('Discover new features and learn useful tips about PyQt-Fluent-Widgets'), + self.aboutGroup + ) + self.feedbackCard = PrimaryPushSettingCard( + self.tr('Provide feedback'), + FIF.FEEDBACK, + self.tr('Provide feedback'), + self.tr('Help us improve PyQt-Fluent-Widgets by providing feedback'), + self.aboutGroup + ) + self.aboutCard = PrimaryPushSettingCard( + self.tr('Check update'), + FIF.INFO, + self.tr('About'), + '© ' + self.tr('Copyright') + f" {YEAR}, {AUTHOR}. " + + self.tr('Version') + f" {VERSION[1:]}", + self.aboutGroup + ) + + self.__initWidget() + + def __initWidget(self): + self.resize(1000, 800) + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.setViewportMargins(0, 80, 0, 20) + self.setWidget(self.scrollWidget) + self.setWidgetResizable(True) + + # initialize style sheet + self.__setQss(cfg.theme) + + # initialize layout + self.__initLayout() + self.__connectSignalToSlot() + + def __initLayout(self): + self.settingLabel.move(36, 30) + + # add cards to group + self.musicInThisPCGroup.addSettingCard(self.musicFolderCard) + self.musicInThisPCGroup.addSettingCard(self.downloadFolderCard) + + self.personalGroup.addSettingCard(self.themeCard) + self.personalGroup.addSettingCard(self.themeColorCard) + self.personalGroup.addSettingCard(self.zoomCard) + self.personalGroup.addSettingCard(self.languageCard) + + self.updateSoftwareGroup.addSettingCard(self.updateOnStartUpCard) + + self.aboutGroup.addSettingCard(self.helpCard) + self.aboutGroup.addSettingCard(self.feedbackCard) + self.aboutGroup.addSettingCard(self.aboutCard) + + # add setting card group to layout + self.expandLayout.setSpacing(28) + self.expandLayout.setContentsMargins(36, 10, 36, 0) + self.expandLayout.addWidget(self.musicInThisPCGroup) + self.expandLayout.addWidget(self.personalGroup) + self.expandLayout.addWidget(self.updateSoftwareGroup) + self.expandLayout.addWidget(self.aboutGroup) + + def __setQss(self, theme: Theme): + """ set style sheet """ + self.scrollWidget.setObjectName('scrollWidget') + self.settingLabel.setObjectName('settingLabel') + + theme = 'dark' if theme == Theme.DARK else 'light' + with open(f'app/resource/qss/{theme}/setting_interface.qss', encoding='utf-8') as f: + self.setStyleSheet(f.read()) + + def __showRestartTooltip(self): + """ show restart tooltip """ + ToastToolTip.warn( + self.tr('Configuration updated successfully'), + self.tr('Configuration takes effect after restart'), + self.window() + ) + + def __onDownloadFolderCardClicked(self): + """ download folder card clicked slot """ + folder = QFileDialog.getExistingDirectory( + self, self.tr("Choose folder"), "./") + if not folder or cfg.get(cfg.downloadFolder) == folder: + return + + cfg.set(cfg.downloadFolder, folder) + self.downloadFolderCard.setContent(folder) + + def __onThemeChanged(self, theme: Theme): + """ theme changed slot """ + # change the theme of qfluentwidgets + setTheme(theme) + + # chang the theme of setting interface + self.__setQss(theme) + + def __connectSignalToSlot(self): + """ connect signal to slot """ + cfg.appRestartSig.connect(self.__showRestartTooltip) + cfg.themeChanged.connect(self.__onThemeChanged) + + # music in the pc + self.musicFolderCard.folderChanged.connect( + self.musicFoldersChanged) + self.downloadFolderCard.clicked.connect( + self.__onDownloadFolderCardClicked) + + # personalization + self.themeColorCard.colorChanged.connect(setThemeColor) + + # about + self.aboutCard.clicked.connect(self.checkUpdateSig) + self.feedbackCard.clicked.connect( + lambda: QDesktopServices.openUrl(QUrl(FEEDBACK_URL))) diff --git a/examples/gallery/app/view/title_bar.py b/examples/gallery/app/view/title_bar.py new file mode 100644 index 0000000000000000000000000000000000000000..e661a040075feb0c555ca8321d664bb25c201a9e --- /dev/null +++ b/examples/gallery/app/view/title_bar.py @@ -0,0 +1,31 @@ +# coding: utf-8 +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QIcon +from PyQt5.QtWidgets import QLabel +from qframelesswindow import TitleBar + + +class CustomTitleBar(TitleBar): + """ Title bar with icon and title """ + + def __init__(self, parent): + super().__init__(parent) + # add window icon + self.iconLabel = QLabel(self) + self.iconLabel.setFixedSize(18, 18) + self.hBoxLayout.insertSpacing(0, 10) + self.hBoxLayout.insertWidget(1, self.iconLabel, 0, Qt.AlignLeft | Qt.AlignBottom) + self.window().windowIconChanged.connect(self.setIcon) + + # add title label + self.titleLabel = QLabel(self) + self.hBoxLayout.insertWidget(2, self.titleLabel, 0, Qt.AlignLeft | Qt.AlignBottom) + self.titleLabel.setObjectName('titleLabel') + self.window().windowTitleChanged.connect(self.setTitle) + + def setTitle(self, title): + self.titleLabel.setText(title) + self.titleLabel.adjustSize() + + def setIcon(self, icon): + self.iconLabel.setPixmap(QIcon(icon).pixmap(18, 18)) diff --git a/examples/gallery/demo.py b/examples/gallery/demo.py new file mode 100644 index 0000000000000000000000000000000000000000..51f8c3acf5d61178f1af3925a3b769799b2306ec --- /dev/null +++ b/examples/gallery/demo.py @@ -0,0 +1,42 @@ +# coding:utf-8 +import os +import sys + +from PyQt5.QtCore import Qt, QLocale, QTranslator +from PyQt5.QtWidgets import QApplication + +from app.common.config import cfg, Language +from app.view.main_window import MainWindow + + +# enable dpi scale +if cfg.get(cfg.dpiScale) == "Auto": + QApplication.setHighDpiScaleFactorRoundingPolicy( + Qt.HighDpiScaleFactorRoundingPolicy.PassThrough) + QApplication.setAttribute(Qt.AA_EnableHighDpiScaling) +else: + os.environ["QT_ENABLE_HIGHDPI_SCALING"] = "0" + os.environ["QT_SCALE_FACTOR"] = str(cfg.get(cfg.dpiScale)) + +QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps) + +# create application +app = QApplication(sys.argv) +app.setAttribute(Qt.AA_DontCreateNativeWidgetSiblings) + +# internationalization +translator = QTranslator() +language = cfg.get(cfg.language) + +if language == Language.AUTO: + translator.load(QLocale.system(), "app/resource/i18n/qfluentwidgets_") +elif language != Language.ENGLISH: + translator.load(f"app/resource/i18n/qfluentwidgets_{language.value}.qm") + +app.installTranslator(translator) + +# create main window +w = MainWindow() +w.show() + +app.exec_() \ No newline at end of file diff --git a/qfluentwidgets/common/config.py b/qfluentwidgets/common/config.py index 093255787c120716b9f57e94375297f36543bc2e..5e8d22935791ac23c1826290fae17adb9c313a28 100644 --- a/qfluentwidgets/common/config.py +++ b/qfluentwidgets/common/config.py @@ -155,9 +155,11 @@ class ColorSerializer(ConfigSerializer): return QColor(value) -class ConfigItem: +class ConfigItem(QObject): """ Config item """ + valueChanged = pyqtSignal(object) + def __init__(self, group, name, default, validator=None, serializer=None, restart=False): """ Parameters @@ -180,6 +182,7 @@ class ConfigItem: restart: bool whether to restart the application after updating value """ + super().__init__() self.group = group self.name = name self.validator = validator or ConfigValidator() @@ -196,7 +199,11 @@ class ConfigItem: @value.setter def value(self, v): - self.__value = self.validator.correct(v) + v = self.validator.correct(v) + ov = self.__value + self.__value = v + if ov != v: + self.valueChanged.emit(v) @property def key(self): diff --git a/qfluentwidgets/components/settings/expand_setting_card.py b/qfluentwidgets/components/settings/expand_setting_card.py index 1b3182479e8f2abff320d5d0335f690f31954b36..6e99530b1ef5ac824d0b47e319c25dfec8a0302a 100644 --- a/qfluentwidgets/components/settings/expand_setting_card.py +++ b/qfluentwidgets/components/settings/expand_setting_card.py @@ -199,6 +199,10 @@ class ExpandSettingCard(QFrame): if self.view.isVisible(): self.resize(self.width(), h + self.card.height()) + def setValue(self, value): + """ set the value of config item """ + pass + class GroupSeparator(QWidget): diff --git a/qfluentwidgets/components/settings/options_setting_card.py b/qfluentwidgets/components/settings/options_setting_card.py index ba0451745865aac8a9cd34c833f5a56eee4c7bfa..5785a3b9a3f30b0f950edbf2f3435dd0ce44072e 100644 --- a/qfluentwidgets/components/settings/options_setting_card.py +++ b/qfluentwidgets/components/settings/options_setting_card.py @@ -56,7 +56,8 @@ class OptionsSettingCard(ExpandSettingCard): button.setProperty(self.configName, option) self._adjustViewSize() - self.setSelected(qconfig.get(self.configItem)) + self.setValue(qconfig.get(self.configItem)) + configItem.valueChanged.connect(self.setValue) self.buttonGroup.buttonClicked.connect(self.__onButtonClicked) def __onButtonClicked(self, button: RadioButton): @@ -71,11 +72,14 @@ class OptionsSettingCard(ExpandSettingCard): self.choiceLabel.adjustSize() self.optionChanged.emit(self.configItem) - def setSelected(self, value): + def setValue(self, value): """ select button according to the value """ + qconfig.set(self.configItem, value) + for button in self.viewLayout.widgets: isChecked = button.property(self.configName) == value button.setChecked(isChecked) + if isChecked: self.choiceLabel.setText(button.text()) self.choiceLabel.adjustSize() diff --git a/qfluentwidgets/components/settings/setting_card.py b/qfluentwidgets/components/settings/setting_card.py index 99c9f043415e4500ec97479c103db866ee6e2fb2..eb9e9e9f5fc51fdc7a80b7352a64ad546a9d2ac7 100644 --- a/qfluentwidgets/components/settings/setting_card.py +++ b/qfluentwidgets/components/settings/setting_card.py @@ -1,8 +1,8 @@ # coding:utf-8 from typing import Union -from PyQt5.QtCore import QUrl, Qt, pyqtSignal -from PyQt5.QtGui import QColor, QDesktopServices, QIcon, QPainter +from PyQt5.QtCore import Qt, pyqtSignal +from PyQt5.QtGui import QColor, QIcon, QPainter from PyQt5.QtWidgets import (QFrame, QHBoxLayout, QLabel, QToolButton, QVBoxLayout, QPushButton) @@ -13,7 +13,7 @@ from ..widgets.slider import Slider from ..widgets.icon_widget import IconWidget from ..widgets.button import HyperlinkButton from ...common.style_sheet import setStyleSheet -from ...common.config import qconfig, isDarkTheme +from ...common.config import qconfig, isDarkTheme, ConfigItem from ...common.icon import FluentIconBase @@ -79,13 +79,18 @@ class SettingCard(QFrame): self.contentLabel.setText(content) self.contentLabel.setVisible(bool(content)) + def setValue(self, value): + """ set the value of config item """ + pass + class SwitchSettingCard(SettingCard): """ Setting card with switch button """ checkedChanged = pyqtSignal(bool) - def __init__(self, icon: Union[str, QIcon, FluentIconBase], title, content=None, configItem=None, parent=None): + def __init__(self, icon: Union[str, QIcon, FluentIconBase], title, content=None, + configItem: ConfigItem = None, parent=None): """ Parameters ---------- @@ -110,7 +115,8 @@ class SwitchSettingCard(SettingCard): self.tr('Off'), self, IndicatorPosition.RIGHT) if configItem: - self.setChecked(qconfig.get(configItem)) + self.setValue(qconfig.get(configItem)) + configItem.valueChanged.connect(self.setValue) # add switch button to layout self.hBoxLayout.addWidget(self.switchButton, 0, Qt.AlignRight) @@ -120,11 +126,10 @@ class SwitchSettingCard(SettingCard): def __onCheckedChanged(self, isChecked: bool): """ switch button checked state changed slot """ - self.setChecked(isChecked) + self.setValue(isChecked) self.checkedChanged.emit(isChecked) - def setChecked(self, isChecked: bool): - """ set switch button checked state """ + def setValue(self, isChecked: bool): if self.configItem: qconfig.set(self.configItem, isChecked) @@ -177,14 +182,18 @@ class RangeSettingCard(SettingCard): self.hBoxLayout.addSpacing(16) self.valueLabel.setObjectName('valueLabel') + configItem.valueChanged.connect(self.setValue) self.slider.valueChanged.connect(self.__onValueChanged) def __onValueChanged(self, value: int): """ slider value changed slot """ + self.setValue(value) + self.valueChanged.emit(value) + + def setValue(self, value): qconfig.set(self.configItem, value) self.valueLabel.setNum(value) self.valueLabel.adjustSize() - self.valueChanged.emit(value) class PushSettingCard(SettingCard): @@ -333,11 +342,16 @@ class ColorSettingCard(SettingCard): self.hBoxLayout.addWidget(self.colorPicker, 0, Qt.AlignRight) self.hBoxLayout.addSpacing(16) self.colorPicker.colorChanged.connect(self.__onColorChanged) + configItem.valueChanged.connect(self.setValue) def __onColorChanged(self, color: QColor): qconfig.set(self.configItem, color) self.colorChanged.emit(color) + def setValue(self, color: QColor): + self.colorPicker.setColor(color) + qconfig.set(self.configItem, color) + class ComboBoxSettingCard(SettingCard): """ Setting card with a combo box """ @@ -375,6 +389,11 @@ class ComboBoxSettingCard(SettingCard): self.comboBox.addItems(texts) self.comboBox.setCurrentText(self.optionToText[qconfig.get(configItem)]) self.comboBox.currentTextChanged.connect(self._onCurrentTextChanged) + configItem.valueChanged.connect(self.setValue) def _onCurrentTextChanged(self, text): + qconfig.set(self.configItem, self.textToOption[text]) + + def setValue(self, text): + self.comboBox.setCurrentText(text) qconfig.set(self.configItem, self.textToOption[text]) \ No newline at end of file diff --git a/qfluentwidgets/components/widgets/__init__.py b/qfluentwidgets/components/widgets/__init__.py index 47852bcc239bbcfc3249c35a03c17286942659c8..3fc5973ca2aa132faaaa42625b91d7ccfc49841a 100644 --- a/qfluentwidgets/components/widgets/__init__.py +++ b/qfluentwidgets/components/widgets/__init__.py @@ -8,4 +8,5 @@ from .scroll_area import ScrollArea, SmoothMode, SmoothScrollArea, SmoothScrollB from .tool_tip import ToolTip, ToolTipFilter from .button import PrimaryPushButton, PushButton, RadioButton, HyperlinkButton, ToolButton from .line_edit import LineEdit -from .check_box import CheckBox \ No newline at end of file +from .check_box import CheckBox +from .icon_widget import IconWidget \ No newline at end of file diff --git a/qfluentwidgets/components/widgets/line_edit.py b/qfluentwidgets/components/widgets/line_edit.py index 8bc2ec35785956ee7ce2dafc7ca445333595a358..8fed341e6c96d4c49470a53705b2568d844d88eb 100644 --- a/qfluentwidgets/components/widgets/line_edit.py +++ b/qfluentwidgets/components/widgets/line_edit.py @@ -16,6 +16,7 @@ class LineEdit(QLineEdit): setStyleSheet(self, 'line_edit') self.setFixedHeight(33) + self.setAttribute(Qt.WA_MacShowFocusRect, False) self.clearButton = QToolButton(self) self.setTextMargins(0, 0, 33, 0) diff --git a/qfluentwidgets/components/widgets/menu.py b/qfluentwidgets/components/widgets/menu.py index b1ba3527558fb02c0b841116aaa549bfaf0fd0ea..383c532b0841f12579275e513169c22aa96af9d5 100644 --- a/qfluentwidgets/components/widgets/menu.py +++ b/qfluentwidgets/components/widgets/menu.py @@ -563,8 +563,8 @@ class RoundMenu(QWidget): rect = QApplication.screenAt(QCursor.pos()).availableGeometry() w, h = self.width() + 5, self.height() + 5 - pos.setX(max(10, min(pos.x() - self.layout().contentsMargins().left(), rect.right() - w))) - pos.setY(max(10, min(pos.y() - 4, rect.bottom() - h))) + pos.setX(min(pos.x() - self.layout().contentsMargins().left(), rect.right() - w)) + pos.setY(min(pos.y() - 4, rect.bottom() - h)) if ani: self.ani.setStartValue(pos-QPoint(0, int(h/2))) diff --git a/qfluentwidgets/components/widgets/switch_button.py b/qfluentwidgets/components/widgets/switch_button.py index 68251b80144b4f5f473ef8c5c086dfccf042aa4a..9489c2ffb2ac299f8e0646ce25e4f3996bc51685 100644 --- a/qfluentwidgets/components/widgets/switch_button.py +++ b/qfluentwidgets/components/widgets/switch_button.py @@ -52,8 +52,9 @@ class Indicator(QToolButton): return super().setChecked(isChecked) + self.sliderRadius = (self.height()-2*self.padding)//2 self.sliderEndX = self.width()-2*self.sliderRadius - \ - self.padding if self.isChecked() else self.padding + self.padding if isChecked else self.padding self.timer.start(5) def mouseReleaseEvent(self, e): diff --git a/qfluentwidgets/components/widgets/tool_tip.py b/qfluentwidgets/components/widgets/tool_tip.py index 15f1bfe749f6533208dc8fbf996782e821faafc5..3f3f10082a3ff37afa64efc34850151a13423952 100644 --- a/qfluentwidgets/components/widgets/tool_tip.py +++ b/qfluentwidgets/components/widgets/tool_tip.py @@ -93,7 +93,7 @@ class ToolTip(QFrame): # adjust postion to prevent tooltips from appearing outside the screen rect = QApplication.screenAt(QCursor.pos()).availableGeometry() - x = min(max(0, x), rect.width() - self.width() - 4) + x = min(max(0, x) if QCursor().pos().x() >= 0 else x, rect.width() - self.width() - 4) y = min(max(0, y), rect.height() - self.height() - 4) self.move(x, y)