pyqtshadcn 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. pyqtshadcn-0.1.0/LICENSE +21 -0
  2. pyqtshadcn-0.1.0/PKG-INFO +105 -0
  3. pyqtshadcn-0.1.0/README.md +90 -0
  4. pyqtshadcn-0.1.0/pyproject.toml +31 -0
  5. pyqtshadcn-0.1.0/setup.cfg +4 -0
  6. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnAvatar.qml +100 -0
  7. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnBadge.qml +52 -0
  8. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnButton.qml +239 -0
  9. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnButtonGroup.qml +28 -0
  10. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnCard.qml +69 -0
  11. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnCardContent.qml +9 -0
  12. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnCardDescription.qml +13 -0
  13. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnCardFooter.qml +9 -0
  14. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnCardHeader.qml +14 -0
  15. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnCardTitle.qml +13 -0
  16. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnCheckbox.qml +61 -0
  17. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnDialog.qml +97 -0
  18. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnDialogContent.qml +151 -0
  19. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnDialogDescription.qml +13 -0
  20. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnDialogFooter.qml +10 -0
  21. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnDialogHeader.qml +9 -0
  22. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnDialogTitle.qml +14 -0
  23. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnInput.qml +54 -0
  24. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnInputGroup.qml +104 -0
  25. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnProgress.qml +55 -0
  26. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnRadio.qml +65 -0
  27. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnRadioGroup.qml +37 -0
  28. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnSelect.qml +180 -0
  29. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnSlider.qml +79 -0
  30. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnSpinner.qml +48 -0
  31. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnStatusDot.qml +50 -0
  32. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnSwitch.qml +77 -0
  33. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnTabsContent.qml +22 -0
  34. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnTabsList.qml +83 -0
  35. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnTabsTrigger.qml +163 -0
  36. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnTextarea.qml +119 -0
  37. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnToggle.qml +10 -0
  38. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Components/ShadcnToggleGroup.qml +33 -0
  39. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Icons/ShadcnIcon.qml +48 -0
  40. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Theme/QtShadcnTheme.qml +51 -0
  41. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qml/Theme/VariantTokens.qml +101 -0
  42. pyqtshadcn-0.1.0/src/pyqtshadcn/QtShadcn/qmldir +38 -0
  43. pyqtshadcn-0.1.0/src/pyqtshadcn/__init__.py +42 -0
  44. pyqtshadcn-0.1.0/src/pyqtshadcn/capability.py +247 -0
  45. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/alert-circle.svg +17 -0
  46. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/alert-triangle.svg +17 -0
  47. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/arrow-down.svg +16 -0
  48. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/arrow-left.svg +16 -0
  49. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/arrow-right.svg +16 -0
  50. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/arrow-up.svg +16 -0
  51. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/bell.svg +16 -0
  52. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/book.svg +15 -0
  53. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/bookmark.svg +15 -0
  54. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/briefcase.svg +16 -0
  55. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/calendar.svg +18 -0
  56. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/check-circle.svg +16 -0
  57. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/check.svg +15 -0
  58. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/chevron-down.svg +15 -0
  59. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/chevron-left.svg +15 -0
  60. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/chevron-right.svg +15 -0
  61. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/chevron-up.svg +15 -0
  62. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/clock.svg +16 -0
  63. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/code.svg +16 -0
  64. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/columns.svg +16 -0
  65. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/copy.svg +16 -0
  66. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/credit-card.svg +16 -0
  67. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/download.svg +17 -0
  68. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/external-link.svg +17 -0
  69. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/eye-off.svg +18 -0
  70. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/eye.svg +16 -0
  71. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/file.svg +16 -0
  72. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/filter.svg +15 -0
  73. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/flag.svg +15 -0
  74. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/folder.svg +15 -0
  75. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/github.svg +14 -0
  76. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/globe.svg +17 -0
  77. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/grid.svg +19 -0
  78. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/heart.svg +15 -0
  79. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/home.svg +16 -0
  80. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/image.svg +17 -0
  81. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/info.svg +17 -0
  82. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/key.svg +17 -0
  83. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/link.svg +16 -0
  84. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/list.svg +20 -0
  85. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/loader-circle.svg +15 -0
  86. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/lock.svg +16 -0
  87. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/mail.svg +16 -0
  88. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/map-pin.svg +16 -0
  89. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/menu.svg +17 -0
  90. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/message-circle.svg +15 -0
  91. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/minus.svg +15 -0
  92. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/monitor.svg +17 -0
  93. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/moon.svg +15 -0
  94. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/more-horizontal.svg +17 -0
  95. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/more-vertical.svg +17 -0
  96. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/palette.svg +19 -0
  97. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/pause.svg +16 -0
  98. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/pencil.svg +16 -0
  99. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/phone.svg +15 -0
  100. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/play.svg +15 -0
  101. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/plus.svg +16 -0
  102. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/refresh-cw.svg +18 -0
  103. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/search.svg +16 -0
  104. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/send.svg +16 -0
  105. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/settings.svg +16 -0
  106. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/shield.svg +15 -0
  107. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/sparkles.svg +18 -0
  108. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/star.svg +15 -0
  109. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/sun.svg +23 -0
  110. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/table.svg +18 -0
  111. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/tag.svg +16 -0
  112. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/thumbs-up.svg +16 -0
  113. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/trash-2.svg +19 -0
  114. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/unlock.svg +16 -0
  115. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/upload.svg +17 -0
  116. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/user.svg +16 -0
  117. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/users.svg +18 -0
  118. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/wrench.svg +15 -0
  119. pyqtshadcn-0.1.0/src/pyqtshadcn/icons/x.svg +16 -0
  120. pyqtshadcn-0.1.0/src/pyqtshadcn.egg-info/PKG-INFO +105 -0
  121. pyqtshadcn-0.1.0/src/pyqtshadcn.egg-info/SOURCES.txt +122 -0
  122. pyqtshadcn-0.1.0/src/pyqtshadcn.egg-info/dependency_links.txt +1 -0
  123. pyqtshadcn-0.1.0/src/pyqtshadcn.egg-info/requires.txt +1 -0
  124. pyqtshadcn-0.1.0/src/pyqtshadcn.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 QtShadcn
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyqtshadcn
3
+ Version: 0.1.0
4
+ Summary: PyQt6 pure-Python adapter for the QtShadcn QML component library (no C++ build).
5
+ Author: QtShadcn
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/QtShadcn/pyqtshadcn
8
+ Project-URL: Repository, https://github.com/QtShadcn/pyqtshadcn
9
+ Keywords: qt,qml,pyqt6,shadcn,ui,components
10
+ Requires-Python: >=3.10
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: PyQt6>=6.10
14
+ Dynamic: license-file
15
+
16
+ # pyqtshadcn
17
+
18
+ QtShadcn 组件库的 **PyQt6 纯 Python 适配包**——无需 C++ 编译,安装即用。
19
+
20
+ 对应主仓库 [`qtshadcn`](https://github.com/QtShadcn/qtshadcn) 的 QML 组件,本包用纯 Python 重写其
21
+ C++ 能力层(`ThemeManager` / `IconRegistry` 两个 QML singleton),使 PyQt6 引擎可直接加载
22
+ 原仓库的 30+ 个 QML 组件(零修改复用)。
23
+
24
+ ## 为什么不用 C++ 插件
25
+
26
+ 主仓库的 C++ 插件针对特定 Qt 小版本编译,而 pip 的 PyQt6 自带另一版本 Qt,ABI 不匹配时
27
+ QML 引擎会拒绝加载插件。本包的能力层用 Python 实现、与 PyQt6 自带 Qt 同 ABI,安装即跑。
28
+
29
+ ## 安装
30
+
31
+ ```bash
32
+ pip install pyqtshadcn
33
+ ```
34
+
35
+ 需要 Python ≥ 3.10 与 PyQt6 ≥ 6.10(pip 会自动安装 PyQt6 依赖)。
36
+
37
+ ## 用法
38
+
39
+ ```python
40
+ import sys
41
+ from PyQt6.QtGui import QGuiApplication
42
+ from PyQt6.QtQml import QQmlApplicationEngine
43
+ from PyQt6.QtCore import QUrl
44
+
45
+ from pyqtshadcn import setup # 一行接入
46
+
47
+ app = QGuiApplication(sys.argv)
48
+ engine = QQmlApplicationEngine()
49
+ setup(engine) # 注册 singleton + 添加 QML import path
50
+
51
+ engine.loadData(b"""
52
+ import QtQuick
53
+ import QtQuick.Controls.Basic
54
+ import QtShadcn
55
+ Window {
56
+ width: 400; height: 300; visible: true
57
+ QtShadcnTheme { id: theme }
58
+ ShadcnButton { text: "Hello"; onClicked: theme.mode = "dark" }
59
+ }
60
+ """, QUrl("qrc:/main.qml"))
61
+
62
+ sys.exit(app.exec())
63
+ ```
64
+
65
+ `setup(engine)` 做两件事:
66
+
67
+ 1. `engine.addImportPath(<包内 QtShadcn 目录>)` —— 使 QML 中 `import QtShadcn` 可解析
68
+ 2. 注册 `ThemeManager` / `IconRegistry` 为 QML singleton
69
+
70
+ ## 本地开发运行
71
+
72
+ ```bash
73
+ git clone https://github.com/QtShadcn/pyqtshadcn
74
+ cd pyqtshadcn
75
+ python -m venv .venv && source .venv/bin/activate
76
+ pip install -e . PyQt6
77
+
78
+ # 无界面验证(无需显示器)
79
+ QT_QPA_PLATFORM=offscreen python examples/prototype_main.py
80
+ ```
81
+
82
+ ## 与主仓库的同步
83
+
84
+ 包内的 `qml/` 与 `icons/` 是主仓库 `src/qml`、`src/assets/icons` 的**副本**(发布时打包进 wheel)。
85
+ 主仓库更新组件后,运行同步脚本拉取最新副本:
86
+
87
+ ```bash
88
+ python sync.py --src /path/to/qtshadcn # 写文件
89
+ python sync.py --src /path/to/qtshadcn --check # 仅检查是否过期(CI 用)
90
+ ```
91
+
92
+ 同步会自动排除 `ShadcnTable`(见下)并校验 `qmldir` 与目录一致。
93
+
94
+ ## 已知限制
95
+
96
+ 1. **`ShadcnTable` 暂不支持**:该组件依赖主仓库 C++ 的 `ShadcnTableModel`(QAbstractTableModel 子类)。
97
+ 纯 Python 路线下尚未用 `QAbstractTableModel` 移植,故包内未包含此组件。
98
+ 2. `IconRegistry` 仅实现**本地内置图标**(随包附带的 75 个 svg),未实现主仓库 C++ 版的
99
+ 远程 CDN 兜底(lucide 按需下载 + 磁盘缓存)。
100
+ 3. 组件 QML 内已用 `import QtQuick.Controls.Basic` 强制 Basic 风格(token 自绘的前提),
101
+ 不依赖 C++ 侧设置样式。
102
+
103
+ ## 许可
104
+
105
+ 与主仓库一致(MIT)。
@@ -0,0 +1,90 @@
1
+ # pyqtshadcn
2
+
3
+ QtShadcn 组件库的 **PyQt6 纯 Python 适配包**——无需 C++ 编译,安装即用。
4
+
5
+ 对应主仓库 [`qtshadcn`](https://github.com/QtShadcn/qtshadcn) 的 QML 组件,本包用纯 Python 重写其
6
+ C++ 能力层(`ThemeManager` / `IconRegistry` 两个 QML singleton),使 PyQt6 引擎可直接加载
7
+ 原仓库的 30+ 个 QML 组件(零修改复用)。
8
+
9
+ ## 为什么不用 C++ 插件
10
+
11
+ 主仓库的 C++ 插件针对特定 Qt 小版本编译,而 pip 的 PyQt6 自带另一版本 Qt,ABI 不匹配时
12
+ QML 引擎会拒绝加载插件。本包的能力层用 Python 实现、与 PyQt6 自带 Qt 同 ABI,安装即跑。
13
+
14
+ ## 安装
15
+
16
+ ```bash
17
+ pip install pyqtshadcn
18
+ ```
19
+
20
+ 需要 Python ≥ 3.10 与 PyQt6 ≥ 6.10(pip 会自动安装 PyQt6 依赖)。
21
+
22
+ ## 用法
23
+
24
+ ```python
25
+ import sys
26
+ from PyQt6.QtGui import QGuiApplication
27
+ from PyQt6.QtQml import QQmlApplicationEngine
28
+ from PyQt6.QtCore import QUrl
29
+
30
+ from pyqtshadcn import setup # 一行接入
31
+
32
+ app = QGuiApplication(sys.argv)
33
+ engine = QQmlApplicationEngine()
34
+ setup(engine) # 注册 singleton + 添加 QML import path
35
+
36
+ engine.loadData(b"""
37
+ import QtQuick
38
+ import QtQuick.Controls.Basic
39
+ import QtShadcn
40
+ Window {
41
+ width: 400; height: 300; visible: true
42
+ QtShadcnTheme { id: theme }
43
+ ShadcnButton { text: "Hello"; onClicked: theme.mode = "dark" }
44
+ }
45
+ """, QUrl("qrc:/main.qml"))
46
+
47
+ sys.exit(app.exec())
48
+ ```
49
+
50
+ `setup(engine)` 做两件事:
51
+
52
+ 1. `engine.addImportPath(<包内 QtShadcn 目录>)` —— 使 QML 中 `import QtShadcn` 可解析
53
+ 2. 注册 `ThemeManager` / `IconRegistry` 为 QML singleton
54
+
55
+ ## 本地开发运行
56
+
57
+ ```bash
58
+ git clone https://github.com/QtShadcn/pyqtshadcn
59
+ cd pyqtshadcn
60
+ python -m venv .venv && source .venv/bin/activate
61
+ pip install -e . PyQt6
62
+
63
+ # 无界面验证(无需显示器)
64
+ QT_QPA_PLATFORM=offscreen python examples/prototype_main.py
65
+ ```
66
+
67
+ ## 与主仓库的同步
68
+
69
+ 包内的 `qml/` 与 `icons/` 是主仓库 `src/qml`、`src/assets/icons` 的**副本**(发布时打包进 wheel)。
70
+ 主仓库更新组件后,运行同步脚本拉取最新副本:
71
+
72
+ ```bash
73
+ python sync.py --src /path/to/qtshadcn # 写文件
74
+ python sync.py --src /path/to/qtshadcn --check # 仅检查是否过期(CI 用)
75
+ ```
76
+
77
+ 同步会自动排除 `ShadcnTable`(见下)并校验 `qmldir` 与目录一致。
78
+
79
+ ## 已知限制
80
+
81
+ 1. **`ShadcnTable` 暂不支持**:该组件依赖主仓库 C++ 的 `ShadcnTableModel`(QAbstractTableModel 子类)。
82
+ 纯 Python 路线下尚未用 `QAbstractTableModel` 移植,故包内未包含此组件。
83
+ 2. `IconRegistry` 仅实现**本地内置图标**(随包附带的 75 个 svg),未实现主仓库 C++ 版的
84
+ 远程 CDN 兜底(lucide 按需下载 + 磁盘缓存)。
85
+ 3. 组件 QML 内已用 `import QtQuick.Controls.Basic` 强制 Basic 风格(token 自绘的前提),
86
+ 不依赖 C++ 侧设置样式。
87
+
88
+ ## 许可
89
+
90
+ 与主仓库一致(MIT)。
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pyqtshadcn"
7
+ version = "0.1.0"
8
+ description = "PyQt6 pure-Python adapter for the QtShadcn QML component library (no C++ build)."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "QtShadcn" }]
14
+ keywords = ["qt", "qml", "pyqt6", "shadcn", "ui", "components"]
15
+ dependencies = [
16
+ "PyQt6>=6.10",
17
+ ]
18
+
19
+ [project.urls]
20
+ Homepage = "https://github.com/QtShadcn/pyqtshadcn"
21
+ Repository = "https://github.com/QtShadcn/pyqtshadcn"
22
+
23
+ [tool.setuptools.packages.find]
24
+ where = ["src"]
25
+
26
+ [tool.setuptools.package-data]
27
+ pyqtshadcn = [
28
+ "QtShadcn/qml/**/*.qml",
29
+ "icons/**/*.svg",
30
+ "QtShadcn/qmldir",
31
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,100 @@
1
+ import QtQuick
2
+ import QtShadcn
3
+
4
+ // 圆形头像组件:首字母 / 图标 / 图片 三选一;右下角可选状态小圆点
5
+ //
6
+ // 用法:
7
+ // ShadcnAvatar {
8
+ // size: ShadcnAvatar.Size.Medium
9
+ // text: "Ryan"
10
+ // status: ShadcnStatusDot.Status.Online
11
+ // }
12
+ //
13
+ // ShadcnAvatar { iconName: "user" }
14
+ // ShadcnAvatar { source: "file:/path/to/photo.png"; size: ShadcnAvatar.Size.Large }
15
+ Item {
16
+ id: root
17
+
18
+ enum Size { XSmall, Small, Medium, Large, XLarge }
19
+
20
+ property int size: ShadcnAvatar.Size.Medium
21
+ // 显示内容,按优先级:source > iconName > text(首字母)
22
+ property url source: ""
23
+ property string iconName: ""
24
+ property string text: ""
25
+
26
+ property color bgColor: theme.primary
27
+ property color textColor: theme.primaryForeground
28
+ property color iconColor: theme.primaryForeground
29
+
30
+ property int status: ShadcnStatusDot.Status.None
31
+
32
+ QtShadcnTheme { id: theme }
33
+
34
+ // Size 枚举 → 像素直径
35
+ readonly property int _diameter:
36
+ size === ShadcnAvatar.Size.XSmall ? 24 :
37
+ size === ShadcnAvatar.Size.Small ? 32 :
38
+ size === ShadcnAvatar.Size.Large ? 56 :
39
+ size === ShadcnAvatar.Size.XLarge ? 72 : 44 // Medium
40
+
41
+ implicitWidth: _diameter
42
+ implicitHeight: _diameter
43
+
44
+ // 是否使用某种内容模式(source / icon / text 三选一)
45
+ readonly property bool _useImage: root.source.toString().length > 0
46
+ readonly property bool _useIcon: !_useImage && root.iconName.length > 0
47
+
48
+ // 视觉底:圆形填充(根是 Item 不 clip,方便 status 点越界)
49
+ Rectangle {
50
+ id: bg
51
+ anchors.fill: parent
52
+ radius: width / 2
53
+ color: root.bgColor
54
+ clip: _useImage // 只有图片模式需要 clip 保证圆形裁切
55
+
56
+ // 模式一:图片
57
+ Image {
58
+ anchors.fill: parent
59
+ visible: root._useImage
60
+ source: root.source
61
+ fillMode: Image.PreserveAspectCrop
62
+ asynchronous: true
63
+ cache: true
64
+ sourceSize.width: parent.width * 2 // Retina 清晰
65
+ sourceSize.height: parent.height * 2
66
+ }
67
+
68
+ // 模式二:图标(无图片时)
69
+ ShadcnIcon {
70
+ visible: root._useIcon
71
+ anchors.centerIn: parent
72
+ name: root.iconName
73
+ size: Math.max(12, Math.round(root._diameter * 0.5))
74
+ color: root.iconColor
75
+ }
76
+
77
+ // 模式三:首字母(无图片也无图标时)
78
+ Text {
79
+ visible: !root._useImage && !root._useIcon && root.text.length > 0
80
+ anchors.centerIn: parent
81
+ text: root.text.charAt(0).toUpperCase()
82
+ color: root.textColor
83
+ font.pixelSize: Math.max(12, Math.round(root._diameter * 0.5))
84
+ font.weight: Font.Bold
85
+ }
86
+ }
87
+
88
+ // 状态覆盖层:右下角,大小约为头像直径 ÷4,带 1px 描边贴合头像底
89
+ ShadcnStatusDot {
90
+ id: statusDot
91
+ visible: root.status !== ShadcnStatusDot.Status.None
92
+ anchors.right: parent.right
93
+ anchors.bottom: parent.bottom
94
+ anchors.rightMargin: Math.max(0, -1)
95
+ anchors.bottomMargin: Math.max(0, -1)
96
+ status: root.status
97
+ size: Math.max(6, Math.round(root._diameter / 4))
98
+ border: true
99
+ }
100
+ }
@@ -0,0 +1,52 @@
1
+ import QtQuick
2
+ import QtShadcn
3
+
4
+ // shadcn/ui 风格徽标
5
+ // 6 variant(v4):default / secondary / destructive(透明底红字)/ outline / ghost / link
6
+ // 高度 20px(h-5)、胶囊圆角、文字 12px Medium
7
+ //
8
+ // 用法:
9
+ // ShadcnBadge { text: "New"; variant: ShadcnBadge.Variant.Default }
10
+ Item {
11
+ id: root
12
+
13
+ enum Variant { Default, Secondary, Destructive, Outline, Ghost, Link }
14
+
15
+ property int variant: ShadcnBadge.Variant.Default
16
+ property string text: ""
17
+
18
+ QtShadcnTheme { id: theme }
19
+ VariantTokens { id: vt }
20
+
21
+ readonly property var _v: vt.badge[root.variant] ?? vt.badge[0]
22
+ // 中转 color 类型:theme.tokens[..] 返回 QVariant(QString),直接 .r/.g/.b 取不到
23
+ // (undefined → Qt.rgba 变黑,曾致 Secondary badge 黑底深字看不见)
24
+ readonly property color _bgColor: _v.bg === "" ? "transparent" : theme.tokens[_v.bg]
25
+ readonly property color _bg: _v.bg === ""
26
+ ? "transparent"
27
+ : Qt.rgba(_bgColor.r, _bgColor.g, _bgColor.b,
28
+ _v.bgAlpha !== undefined ? _v.bgAlpha : 1.0)
29
+ readonly property color _fg: theme.tokens[_v.fg]
30
+ readonly property color _border: theme.tokens[_v.borderToken !== undefined ? _v.borderToken : "border"]
31
+
32
+ implicitWidth: contentText.implicitWidth + 16 // px-2(8) * 2
33
+ implicitHeight: 20
34
+
35
+ Rectangle {
36
+ anchors.fill: parent
37
+ radius: 999 // 胶囊
38
+ color: root._bg
39
+ border.width: root._v.border ? 1 : 0
40
+ border.color: root._border
41
+ }
42
+
43
+ Text {
44
+ id: contentText
45
+
46
+ anchors.centerIn: parent
47
+ text: root.text
48
+ color: root._fg
49
+ font.pixelSize: 12
50
+ font.weight: Font.Medium
51
+ }
52
+ }
@@ -0,0 +1,239 @@
1
+ import QtQuick
2
+ import QtQuick.Controls.Basic
3
+ import QtQuick.Layouts
4
+ import QtShadcn
5
+
6
+ // shadcn/ui 风格按钮
7
+ // 基于 QQC Button(Basic style):键盘导航 / Focus / 无障碍继承,只替换视觉与 API
8
+ // 注意:自定义 contentItem/background 的文件使用 style-specific import
9
+ // (QtQuick.Controls.Basic),锁定 Basic 风格是 token 自绘的前提
10
+ //
11
+ // 用法:
12
+ // ShadcnButton {
13
+ // text: "Deploy"
14
+ // variant: ShadcnButton.Variant.Primary // Primary/Secondary/Outline/Ghost/Destructive
15
+ // size: ShadcnButton.Size.Medium // Small/Medium/Large/Icon
16
+ // loading: false
17
+ // onClicked: ...
18
+ // }
19
+ Button {
20
+ id: root
21
+
22
+ // 选中时 z 提升:组内 spacing:-1 相邻按钮重叠 1px(后声明者盖前者右缘),
23
+ // 选中按钮盖住相邻按钮左缘,保证自身右缘描边完整(否则右缘 1px 被盖,
24
+ // 曾表现为"选中项右侧边框不存在")
25
+ z: root._checked ? 1 : 0
26
+
27
+ enum Variant { Primary, Secondary, Outline, Ghost, Destructive, Link }
28
+ enum Size { ExtraSmall, Small, Medium, Large, Icon }
29
+
30
+ // ── 公开 API ──
31
+ property int variant: ShadcnButton.Variant.Primary
32
+ property int size: ShadcnButton.Size.Medium
33
+ property bool loading: false
34
+ // 图标(lucide 名,见 IconRegistry.names):显示在文本左侧(shadcn 带图标按钮)
35
+ property string iconName: ""
36
+
37
+ // ShadcnButtonGroup / ShadcnToggleGroup 内部使用:'only' | 'first' | 'middle' | 'last'
38
+ property string _groupPosition: "only"
39
+
40
+ QtShadcnTheme { id: theme }
41
+ VariantTokens { id: vt }
42
+
43
+ // ── 尺寸表(对齐 shadcn/ui:xs=32 / sm=36 / default=40 / lg=44 / icon=40)──
44
+ readonly property int _ctrlHeight: size === ShadcnButton.Size.ExtraSmall ? 32
45
+ : size === ShadcnButton.Size.Small ? 36
46
+ : size === ShadcnButton.Size.Large ? 44 : 40 // Medium / Icon
47
+ readonly property int _hPad: size === ShadcnButton.Size.ExtraSmall ? 10
48
+ : size === ShadcnButton.Size.Small ? 12
49
+ : size === ShadcnButton.Size.Large ? 32 : 16 // shadcn px-8=32 / px-4=16
50
+ readonly property int _fontSize: size === ShadcnButton.Size.ExtraSmall ? 12
51
+ : size === ShadcnButton.Size.Small ? 12 : 14 // text-sm
52
+ readonly property bool _isIcon: size === ShadcnButton.Size.Icon
53
+
54
+ implicitWidth: _isIcon ? _ctrlHeight : contentItem.implicitWidth + _hPad * 2
55
+ implicitHeight: _ctrlHeight
56
+
57
+ // ── 配色:variant → token 查表(映射集中在 VariantTokens,新增 variant
58
+ // 只需枚举 + 映射表各加一项;token 名经 theme.tokens[] 查询保持绑定)──
59
+ readonly property bool _checked: checkable && checked
60
+ readonly property var _variantMap: vt.button[root.variant] ?? vt.button[0]
61
+ // 背景 token 不随 checked 切换(选中态视觉由 background 内的覆盖层负责——
62
+ // 教训:bg 在 accent↔transparent 间 color 动画会 RGBA 插值出中间灰,点击时闪烁)
63
+ readonly property string _bgToken: _variantMap.bg
64
+ // 前景色随选中切换(文字色,无动画,瞬间切换不闪)
65
+ readonly property string _fgToken: _checked ? vt.checkedOverride.fg : _variantMap.fg
66
+ readonly property color _baseBg: _bgToken === "" ? "transparent" : theme.tokens[_bgToken]
67
+ // hover 文字色:outline/ghost hover 时用 accentForeground(shadcn: hover:text-accent-foreground)
68
+ readonly property color _baseFg: (root.hovered && (variant === ShadcnButton.Variant.Outline
69
+ || variant === ShadcnButton.Variant.Ghost))
70
+ ? theme.accentForeground : theme.tokens[_fgToken]
71
+ readonly property bool _showBorder: _variantMap.border
72
+ // 边框宽(outline 等 border variant 才有;组内 spacing:-1 合并)
73
+ readonly property int _borderW: root._showBorder ? 1 : 0
74
+
75
+ // hover/pressed 混合底色:选中时用覆盖层色(accent),否则用 variant 底色。
76
+ // 教训:_bgToken 不随 checked 后,选中按钮的实际背景是 accent 覆盖层,
77
+ // 若用 primary 等深色 variant 底色混合会叠出深灰("选中项 hover 变黑")
78
+ readonly property color _hoverBase: _checked
79
+ ? theme.tokens[vt.checkedOverride.bg] : _baseBg
80
+
81
+ // hover 背景:outline/ghost → accent 色(shadcn: hover:bg-accent);
82
+ // 其余 → 按钮色与页面背景按 hoverMix 混合(等效 shadcn hover:bg-*/90|80:
83
+ // 深色按钮变浅、浅色变暗,随主题背景色自动适配)。
84
+ // 教训:固定黑 8% 在 light 模式 primary(#18181b 近黑)上黑上加黑,肉眼不可见。
85
+ readonly property color _hoverOverlay: {
86
+ if (variant === ShadcnButton.Variant.Outline || variant === ShadcnButton.Variant.Ghost)
87
+ return theme.accent
88
+ return _mix(_hoverBase, theme.background, _variantMap.hoverMix)
89
+ }
90
+ // pressed:混合比例加重(按压反馈更深);accent 上叠黑 15% 压暗
91
+ readonly property color _pressedOverlay: {
92
+ if (variant === ShadcnButton.Variant.Outline || variant === ShadcnButton.Variant.Ghost)
93
+ return Qt.rgba(0, 0, 0, 0.15)
94
+ return _mix(_hoverBase, theme.background, Math.min(0.25, _variantMap.hoverMix * 2))
95
+ }
96
+
97
+ // a 向 b 按 t 混合(t=0.1 等效 shadcn /90:0.9×a + 0.1×b)
98
+ function _mix(a, b, t) {
99
+ return Qt.rgba(a.r * (1 - t) + b.r * t, a.g * (1 - t) + b.g * t, a.b * (1 - t) + b.b * t, 1)
100
+ }
101
+
102
+ // 圆角:shadcn button 用 rounded-md = 6px(theme.radius 8 用于卡片/容器)
103
+ readonly property int _r: 6
104
+ readonly property int _tl: _groupPosition === "only" || _groupPosition === "first" ? _r : 0
105
+ readonly property int _tr: _groupPosition === "only" || _groupPosition === "last" ? _r : 0
106
+ readonly property int _bl: _groupPosition === "only" || _groupPosition === "first" ? _r : 0
107
+ readonly property int _br: _groupPosition === "only" || _groupPosition === "last" ? _r : 0
108
+
109
+ // ── 背景 ────────────────────────────────────────
110
+ background: Rectangle {
111
+ topLeftRadius: root._tl
112
+ topRightRadius: root._tr
113
+ bottomLeftRadius: root._bl
114
+ bottomRightRadius: root._br
115
+ color: root._baseBg
116
+ border.width: root._borderW
117
+ // 选中态描边用 accentForeground(light 下深色描边圈住选中项,弥补 accent 背景对比度弱;
118
+ // dark 下自动变白色描边。保持 border token 语义不变)
119
+ border.color: root.activeFocus ? theme.ring
120
+ : root._checked ? theme.accentForeground : theme.border
121
+
122
+ Behavior on color { ColorAnimation { duration: 120 } }
123
+
124
+ // 组内分隔线:middle/last 左侧 1px(仅无 border 的 variant,如 Primary)
125
+ // 用按钮自身前景色(primary 组 = 白色 primaryForeground,深底上可见;
126
+ // 教训:theme.foreground 在 light 模式是深色,叠深色按钮上不可见)
127
+ Rectangle {
128
+ visible: (root._groupPosition === "middle" || root._groupPosition === "last")
129
+ && !root._showBorder
130
+ width: 1
131
+ anchors.top: parent.top
132
+ anchors.bottom: parent.bottom
133
+ anchors.left: parent.left
134
+ color: Qt.rgba(root._baseFg.r, root._baseFg.g, root._baseFg.b, 0.15)
135
+ }
136
+
137
+ // 选中态覆盖层(checkable 用):accent 背景 + opacity 动画。
138
+ // 教训:background.color 在 accent↔transparent 间 ColorAnimation 会 RGBA 插值
139
+ // 出中间灰(半透明灰叠白底,点击切换时闪烁);opacity 动画 RGB 恒定不闪灰。
140
+ // 内缩边框宽:outline 选中时保留 1px 边框
141
+ Rectangle {
142
+ anchors.fill: parent
143
+ anchors.margins: root._borderW
144
+ topLeftRadius: Math.max(0, parent.topLeftRadius - root._borderW)
145
+ topRightRadius: Math.max(0, parent.topRightRadius - root._borderW)
146
+ bottomLeftRadius: Math.max(0, parent.bottomLeftRadius - root._borderW)
147
+ bottomRightRadius: Math.max(0, parent.bottomRightRadius - root._borderW)
148
+ visible: root.checkable
149
+ color: theme.tokens[vt.checkedOverride.bg]
150
+ opacity: root._checked ? 1 : 0
151
+ Behavior on opacity { NumberAnimation { duration: 120 } }
152
+ }
153
+
154
+ // hover / pressed:背景色叠加(outline/ghost → accent;其余按亮度黑/白;link 无)
155
+ // anchors.margins = 边框宽:不盖住 outline 的 1px 边框(否则 hover 时边框视觉消失)
156
+ Rectangle {
157
+ anchors.fill: parent
158
+ anchors.margins: root._borderW
159
+ topLeftRadius: Math.max(0, parent.topLeftRadius - root._borderW)
160
+ topRightRadius: Math.max(0, parent.topRightRadius - root._borderW)
161
+ bottomLeftRadius: Math.max(0, parent.bottomLeftRadius - root._borderW)
162
+ bottomRightRadius: Math.max(0, parent.bottomRightRadius - root._borderW)
163
+ visible: root.hovered && root.variant !== ShadcnButton.Variant.Link
164
+ color: root._hoverOverlay
165
+ }
166
+ Rectangle {
167
+ anchors.fill: parent
168
+ anchors.margins: root._borderW
169
+ topLeftRadius: Math.max(0, parent.topLeftRadius - root._borderW)
170
+ topRightRadius: Math.max(0, parent.topRightRadius - root._borderW)
171
+ bottomLeftRadius: Math.max(0, parent.bottomLeftRadius - root._borderW)
172
+ bottomRightRadius: Math.max(0, parent.bottomRightRadius - root._borderW)
173
+ visible: root.pressed && root.variant !== ShadcnButton.Variant.Link
174
+ color: root._pressedOverlay
175
+ }
176
+
177
+ // 键盘焦点环(activeFocus,Tab 可达)
178
+ Rectangle {
179
+ anchors.fill: parent
180
+ anchors.margins: -3
181
+ radius: root._r + 3
182
+ visible: root.activeFocus
183
+ color: "transparent"
184
+ border.width: 2
185
+ border.color: theme.ring
186
+ opacity: 0.7
187
+ }
188
+ }
189
+
190
+ // ── 内容 ────────────────────────────────────────
191
+ // Item 包装:contentItem 会被 QQC 拉伸到内容区全宽,RowLayout 必须居中于
192
+ // Item(anchors.centerIn)才能保证文字水平+垂直都居中(按钮被拉伸时也成立);
193
+ // RowLayout 内部子项默认垂直居中
194
+ contentItem: Item {
195
+ implicitWidth: row.implicitWidth
196
+ implicitHeight: row.implicitHeight
197
+
198
+ RowLayout {
199
+ id: row
200
+ anchors.centerIn: parent
201
+ spacing: theme.spacingXs
202
+
203
+ ShadcnSpinner {
204
+ Layout.preferredWidth: 14
205
+ Layout.preferredHeight: 14
206
+ visible: root.loading
207
+ color: root._baseFg
208
+ }
209
+
210
+ // 图标(iconName,shadcn 带图标按钮:svg size-4=16px,随文字色)
211
+ ShadcnIcon {
212
+ Layout.preferredWidth: 16
213
+ Layout.preferredHeight: 16
214
+ visible: root.iconName.length > 0
215
+ name: root.iconName
216
+ size: 16
217
+ color: root._baseFg
218
+ }
219
+
220
+ Text {
221
+ visible: root.text.length > 0
222
+ text: root.text
223
+ // 组属性限制:font 整体赋值后不能局部覆盖,故逐项复制
224
+ font.family: root.font.family
225
+ font.pixelSize: root._fontSize
226
+ font.weight: Font.Medium // shadcn: font-medium
227
+ // link variant:hover/聚焦时显示下划线(shadcn: hover:underline)
228
+ font.underline: root.variant === ShadcnButton.Variant.Link
229
+ && (root.hovered || root.activeFocus)
230
+ color: root._baseFg
231
+ opacity: root.loading ? 0.6 : 1
232
+ }
233
+ }
234
+ }
235
+
236
+ // loading:禁用交互;disabled/loading 半透明(shadcn: disabled:opacity-50)
237
+ enabled: !root.loading
238
+ opacity: root.loading || !root.enabled ? 0.5 : 1
239
+ }
@@ -0,0 +1,28 @@
1
+ import QtQuick
2
+ import QtShadcn
3
+
4
+ // 按钮组:相邻按钮边框合并(spacing: -1),圆角只保留两端
5
+ // 子项直接放 ShadcnButton / ShadcnToggle 即可,圆角位置自动分配
6
+ Row {
7
+ id: root
8
+
9
+ spacing: -1
10
+
11
+ onChildrenChanged: updateGroup()
12
+ Component.onCompleted: updateGroup()
13
+
14
+ function updateGroup() {
15
+ var btns = []
16
+ for (var i = 0; i < root.children.length; ++i) {
17
+ var c = root.children[i]
18
+ if (c.hasOwnProperty("_groupPosition"))
19
+ btns.push(c)
20
+ }
21
+ var n = btns.length
22
+ for (var j = 0; j < n; ++j) {
23
+ btns[j]._groupPosition = n === 1 ? "only"
24
+ : j === 0 ? "first"
25
+ : j === n - 1 ? "last" : "middle"
26
+ }
27
+ }
28
+ }