pyside6stylekit 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.
- pyside6stylekit-0.1.0/LICENSE +21 -0
- pyside6stylekit-0.1.0/PKG-INFO +107 -0
- pyside6stylekit-0.1.0/README.md +82 -0
- pyside6stylekit-0.1.0/pyproject.toml +38 -0
- pyside6stylekit-0.1.0/pyside6stylekit/__init__.py +16 -0
- pyside6stylekit-0.1.0/pyside6stylekit/presets.py +6 -0
- pyside6stylekit-0.1.0/pyside6stylekit/theme.py +71 -0
- pyside6stylekit-0.1.0/pyside6stylekit/utils/__init__.py +6 -0
- pyside6stylekit-0.1.0/pyside6stylekit/utils/colors.py +33 -0
- pyside6stylekit-0.1.0/pyside6stylekit/widgets/__init__.py +15 -0
- pyside6stylekit-0.1.0/pyside6stylekit/widgets/button.py +44 -0
- pyside6stylekit-0.1.0/pyside6stylekit/widgets/indus_alternate_button.py +60 -0
- pyside6stylekit-0.1.0/pyside6stylekit/widgets/indus_lamp.py +53 -0
- pyside6stylekit-0.1.0/pyside6stylekit/widgets/indus_momentary_button.py +53 -0
- pyside6stylekit-0.1.0/pyside6stylekit/widgets/label.py +25 -0
- pyside6stylekit-0.1.0/pyside6stylekit/widgets/lineedit.py +128 -0
- pyside6stylekit-0.1.0/pyside6stylekit.egg-info/PKG-INFO +107 -0
- pyside6stylekit-0.1.0/pyside6stylekit.egg-info/SOURCES.txt +20 -0
- pyside6stylekit-0.1.0/pyside6stylekit.egg-info/dependency_links.txt +1 -0
- pyside6stylekit-0.1.0/pyside6stylekit.egg-info/requires.txt +1 -0
- pyside6stylekit-0.1.0/pyside6stylekit.egg-info/top_level.txt +2 -0
- pyside6stylekit-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Noritama-Lab
|
|
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,107 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyside6stylekit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight and extensible styled UI component kit for PySide6 applications.
|
|
5
|
+
Author: Noritama-Lab
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/Noritama-Lab/PySide6StyleKit
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Topic :: Software Development :: User Interfaces
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: PySide6>=6.0.0
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# PySide6StyleKit
|
|
27
|
+
PySide6 アプリのための軽量で拡張性の高いスタイル済み UI コンポーネントキット。
|
|
28
|
+
A lightweight and extensible styled UI component kit for PySide6 applications.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 特徴 / Features
|
|
33
|
+
- 統一テーマ(ライト/ダーク/背景色)
|
|
34
|
+
Unified themes (light, dark, background-aware)
|
|
35
|
+
- スタイル済みウィジェット(Label / LineEdit / Button / Indus Alternate Button / Indus Lamp / Indus Momentary Button など)
|
|
36
|
+
Styled widgets such as Label, LineEdit, Button, Indus Alternate Button, Indus Lamp, and Indus Momentary Button
|
|
37
|
+
- 数値バリデーション(int / float / range)
|
|
38
|
+
Robust numeric validation (int, float, range)
|
|
39
|
+
- エラー表示の柔軟な制御(枠線のみ・メッセージ表示)
|
|
40
|
+
Flexible error signaling (border-only or message)
|
|
41
|
+
- 拡張しやすいモジュール構造
|
|
42
|
+
Modular and extensible architecture
|
|
43
|
+
- 実用的なサンプルコード付き
|
|
44
|
+
Includes practical example scripts
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## インストール / Installation
|
|
49
|
+
開発モードで利用する場合:
|
|
50
|
+
For development mode:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install -e .
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 使い方 / Usage
|
|
59
|
+
`examples/demo.py` と `examples/demo_indus.py` に基本的な使い方があります。
|
|
60
|
+
Basic usage is available in `examples/demo.py` and `examples/demo_indus.py`.
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from pyside6stylekit import Theme, StyledLabel, StyledLineEdit, StyledButton
|
|
64
|
+
|
|
65
|
+
theme = Theme.light()
|
|
66
|
+
|
|
67
|
+
label = StyledLabel("Hello")
|
|
68
|
+
lineedit = StyledLineEdit(validator="int")
|
|
69
|
+
button = StyledButton("Submit")
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## プロジェクト構造 / Project Structure
|
|
75
|
+
```
|
|
76
|
+
pyside6stylekit/
|
|
77
|
+
├─ pyside6stylekit/
|
|
78
|
+
│ ├─ utils/
|
|
79
|
+
│ │ ├─ __init__.py
|
|
80
|
+
│ │ └─ colors.py
|
|
81
|
+
│ ├─ widgets/
|
|
82
|
+
│ │ ├─ __init__.py
|
|
83
|
+
│ │ ├─ button.py
|
|
84
|
+
│ │ ├─ indus_alternate_button.py
|
|
85
|
+
│ │ ├─ indus_lamp.py
|
|
86
|
+
│ │ ├─ indus_momentary_button.py
|
|
87
|
+
│ │ ├─ label.py
|
|
88
|
+
│ │ └─ lineedit.py
|
|
89
|
+
│ ├─ __init__.py
|
|
90
|
+
│ ├─ presets.py
|
|
91
|
+
│ └─ theme.py
|
|
92
|
+
├─ examples/
|
|
93
|
+
│ ├─ demo.py
|
|
94
|
+
│ └─ demo_indus.py
|
|
95
|
+
├─ LICENSE
|
|
96
|
+
└─ README.md
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## ライセンス / License
|
|
102
|
+
MIT License © 2026 Mitsunori
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 作者 / Author
|
|
107
|
+
Noritama-Lab
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# PySide6StyleKit
|
|
2
|
+
PySide6 アプリのための軽量で拡張性の高いスタイル済み UI コンポーネントキット。
|
|
3
|
+
A lightweight and extensible styled UI component kit for PySide6 applications.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 特徴 / Features
|
|
8
|
+
- 統一テーマ(ライト/ダーク/背景色)
|
|
9
|
+
Unified themes (light, dark, background-aware)
|
|
10
|
+
- スタイル済みウィジェット(Label / LineEdit / Button / Indus Alternate Button / Indus Lamp / Indus Momentary Button など)
|
|
11
|
+
Styled widgets such as Label, LineEdit, Button, Indus Alternate Button, Indus Lamp, and Indus Momentary Button
|
|
12
|
+
- 数値バリデーション(int / float / range)
|
|
13
|
+
Robust numeric validation (int, float, range)
|
|
14
|
+
- エラー表示の柔軟な制御(枠線のみ・メッセージ表示)
|
|
15
|
+
Flexible error signaling (border-only or message)
|
|
16
|
+
- 拡張しやすいモジュール構造
|
|
17
|
+
Modular and extensible architecture
|
|
18
|
+
- 実用的なサンプルコード付き
|
|
19
|
+
Includes practical example scripts
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## インストール / Installation
|
|
24
|
+
開発モードで利用する場合:
|
|
25
|
+
For development mode:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install -e .
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 使い方 / Usage
|
|
34
|
+
`examples/demo.py` と `examples/demo_indus.py` に基本的な使い方があります。
|
|
35
|
+
Basic usage is available in `examples/demo.py` and `examples/demo_indus.py`.
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from pyside6stylekit import Theme, StyledLabel, StyledLineEdit, StyledButton
|
|
39
|
+
|
|
40
|
+
theme = Theme.light()
|
|
41
|
+
|
|
42
|
+
label = StyledLabel("Hello")
|
|
43
|
+
lineedit = StyledLineEdit(validator="int")
|
|
44
|
+
button = StyledButton("Submit")
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## プロジェクト構造 / Project Structure
|
|
50
|
+
```
|
|
51
|
+
pyside6stylekit/
|
|
52
|
+
├─ pyside6stylekit/
|
|
53
|
+
│ ├─ utils/
|
|
54
|
+
│ │ ├─ __init__.py
|
|
55
|
+
│ │ └─ colors.py
|
|
56
|
+
│ ├─ widgets/
|
|
57
|
+
│ │ ├─ __init__.py
|
|
58
|
+
│ │ ├─ button.py
|
|
59
|
+
│ │ ├─ indus_alternate_button.py
|
|
60
|
+
│ │ ├─ indus_lamp.py
|
|
61
|
+
│ │ ├─ indus_momentary_button.py
|
|
62
|
+
│ │ ├─ label.py
|
|
63
|
+
│ │ └─ lineedit.py
|
|
64
|
+
│ ├─ __init__.py
|
|
65
|
+
│ ├─ presets.py
|
|
66
|
+
│ └─ theme.py
|
|
67
|
+
├─ examples/
|
|
68
|
+
│ ├─ demo.py
|
|
69
|
+
│ └─ demo_indus.py
|
|
70
|
+
├─ LICENSE
|
|
71
|
+
└─ README.md
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## ライセンス / License
|
|
77
|
+
MIT License © 2026 Mitsunori
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 作者 / Author
|
|
82
|
+
Noritama-Lab
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pyside6stylekit"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A lightweight and extensible styled UI component kit for PySide6 applications."
|
|
9
|
+
authors = [
|
|
10
|
+
{name = "Noritama-Lab"}
|
|
11
|
+
]
|
|
12
|
+
license = {text = "MIT"}
|
|
13
|
+
dependencies = [
|
|
14
|
+
"PySide6>=6.0.0"
|
|
15
|
+
]
|
|
16
|
+
readme = "README.md"
|
|
17
|
+
requires-python = ">=3.8"
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 3 - Alpha",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.8",
|
|
25
|
+
"Programming Language :: Python :: 3.9",
|
|
26
|
+
"Programming Language :: Python :: 3.10",
|
|
27
|
+
"Programming Language :: Python :: 3.11",
|
|
28
|
+
"Programming Language :: Python :: 3.12",
|
|
29
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
30
|
+
"Topic :: Software Development :: User Interfaces",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Repository = "https://github.com/Noritama-Lab/PySide6StyleKit"
|
|
35
|
+
|
|
36
|
+
[tool.setuptools.packages.find]
|
|
37
|
+
where = ["."]
|
|
38
|
+
exclude = ["examples*", "tests*"]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from .theme import Theme
|
|
2
|
+
from .presets import SIZE_PRESETS
|
|
3
|
+
from .widgets import (
|
|
4
|
+
StyledLabel, StyledButton, StyledLineEdit,
|
|
5
|
+
IndusMomentaryButton, IndusAlternateButton, IndusLamp
|
|
6
|
+
)
|
|
7
|
+
__all__ = [
|
|
8
|
+
"Theme",
|
|
9
|
+
"SIZE_PRESETS",
|
|
10
|
+
"StyledLabel",
|
|
11
|
+
"StyledButton",
|
|
12
|
+
"StyledLineEdit",
|
|
13
|
+
"IndusMomentaryButton",
|
|
14
|
+
"IndusAlternateButton",
|
|
15
|
+
"IndusLamp",
|
|
16
|
+
]
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# theme.py
|
|
2
|
+
from .presets import SIZE_PRESETS
|
|
3
|
+
from .utils.colors import normalize_color, adjust_color
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Theme:
|
|
7
|
+
def __init__(self, primary="blue", mode="light",
|
|
8
|
+
font_family="Segoe UI", size="mid",
|
|
9
|
+
text_color=None):
|
|
10
|
+
self.primary = normalize_color(primary)
|
|
11
|
+
self.mode = mode
|
|
12
|
+
self.font_family = font_family
|
|
13
|
+
self.size = size
|
|
14
|
+
|
|
15
|
+
if size not in SIZE_PRESETS:
|
|
16
|
+
raise ValueError("size must be small / mid / large")
|
|
17
|
+
|
|
18
|
+
# -------------------------
|
|
19
|
+
# 文字色(ユーザー指定 > モードデフォルト)
|
|
20
|
+
# -------------------------
|
|
21
|
+
if text_color:
|
|
22
|
+
self.text_color = normalize_color(text_color)
|
|
23
|
+
else:
|
|
24
|
+
if mode == "light":
|
|
25
|
+
self.text_color = "#000000"
|
|
26
|
+
elif mode == "dark":
|
|
27
|
+
self.text_color = "#ffffff"
|
|
28
|
+
else:
|
|
29
|
+
raise ValueError("mode must be light / dark")
|
|
30
|
+
|
|
31
|
+
# 背景・枠色
|
|
32
|
+
if mode == "light":
|
|
33
|
+
self.background = "#ffffff"
|
|
34
|
+
self.border_color = "#dcdde1"
|
|
35
|
+
else:
|
|
36
|
+
self.background = "#2f3640"
|
|
37
|
+
self.border_color = "#4b4b4b"
|
|
38
|
+
|
|
39
|
+
# -------------------------
|
|
40
|
+
# 色のバリエーション
|
|
41
|
+
# -------------------------
|
|
42
|
+
def primary_dark(self, factor=0.5):
|
|
43
|
+
"""OFF の時に使う暗い色"""
|
|
44
|
+
return adjust_color(self.primary, factor)
|
|
45
|
+
|
|
46
|
+
def hover(self):
|
|
47
|
+
return adjust_color(self.primary, 0.85)
|
|
48
|
+
|
|
49
|
+
def pressed(self):
|
|
50
|
+
return adjust_color(self.primary, 0.70)
|
|
51
|
+
|
|
52
|
+
# -------------------------
|
|
53
|
+
# サイズ
|
|
54
|
+
# -------------------------
|
|
55
|
+
def font_size(self):
|
|
56
|
+
return SIZE_PRESETS[self.size]["font"]
|
|
57
|
+
|
|
58
|
+
def padding(self):
|
|
59
|
+
return SIZE_PRESETS[self.size]["padding"]
|
|
60
|
+
|
|
61
|
+
# -------------------------
|
|
62
|
+
# QSS 出力
|
|
63
|
+
# -------------------------
|
|
64
|
+
def export_qss(self):
|
|
65
|
+
return f"""
|
|
66
|
+
* {{
|
|
67
|
+
font-family: '{self.font_family}';
|
|
68
|
+
color: {self.text_color};
|
|
69
|
+
background: {self.background};
|
|
70
|
+
}}
|
|
71
|
+
"""
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# utils/colors.py
|
|
2
|
+
def normalize_color(color):
|
|
3
|
+
css_colors = {
|
|
4
|
+
"red": "#ff0000", "green": "#00ff00", "blue": "#0000ff",
|
|
5
|
+
"black": "#000000", "white": "#ffffff", "gray": "#808080",
|
|
6
|
+
"orange": "#ffa500", "yellow": "#ffff00", "purple": "#800080",
|
|
7
|
+
"pink": "#ffc0cb", "sky": "#87ceeb",
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if isinstance(color, tuple) and len(color) == 3:
|
|
11
|
+
r, g, b = color
|
|
12
|
+
return f"#{r:02x}{g:02x}{b:02x}"
|
|
13
|
+
|
|
14
|
+
if isinstance(color, str):
|
|
15
|
+
if color.startswith("#") and len(color) == 7:
|
|
16
|
+
return color.lower()
|
|
17
|
+
if color.lower() in css_colors:
|
|
18
|
+
return css_colors[color.lower()]
|
|
19
|
+
|
|
20
|
+
raise ValueError(f"Invalid color: {color}")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def adjust_color(color, factor):
|
|
24
|
+
color = color.lstrip("#")
|
|
25
|
+
r = int(color[0:2], 16)
|
|
26
|
+
g = int(color[2:4], 16)
|
|
27
|
+
b = int(color[4:6], 16)
|
|
28
|
+
|
|
29
|
+
r = max(0, min(255, int(r * factor)))
|
|
30
|
+
g = max(0, min(255, int(g * factor)))
|
|
31
|
+
b = max(0, min(255, int(b * factor)))
|
|
32
|
+
|
|
33
|
+
return f"#{r:02x}{g:02x}{b:02x}"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .label import StyledLabel
|
|
2
|
+
from .button import StyledButton
|
|
3
|
+
from .lineedit import StyledLineEdit
|
|
4
|
+
from .indus_momentary_button import IndusMomentaryButton
|
|
5
|
+
from .indus_alternate_button import IndusAlternateButton
|
|
6
|
+
from .indus_lamp import IndusLamp
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"StyledLabel",
|
|
10
|
+
"StyledButton",
|
|
11
|
+
"StyledLineEdit",
|
|
12
|
+
"IndusMomentaryButton",
|
|
13
|
+
"IndusAlternateButton",
|
|
14
|
+
"IndusLamp",
|
|
15
|
+
]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# widgets/button.py
|
|
2
|
+
from PySide6.QtWidgets import QPushButton
|
|
3
|
+
from PySide6.QtGui import QFont, QIcon
|
|
4
|
+
|
|
5
|
+
class StyledButton(QPushButton):
|
|
6
|
+
def __init__(self, text, theme, icon_path=None, text_color=None):
|
|
7
|
+
super().__init__(text)
|
|
8
|
+
self.theme = theme
|
|
9
|
+
self.text_color = text_color or theme.text_color
|
|
10
|
+
|
|
11
|
+
self.setFont(QFont(theme.font_family, theme.font_size()))
|
|
12
|
+
|
|
13
|
+
if icon_path:
|
|
14
|
+
self.setIcon(QIcon(icon_path))
|
|
15
|
+
|
|
16
|
+
self.apply_style()
|
|
17
|
+
|
|
18
|
+
def apply_style(self):
|
|
19
|
+
pad_v, pad_h = self.theme.padding()
|
|
20
|
+
|
|
21
|
+
bg = self.theme.primary
|
|
22
|
+
hover = self.theme.hover()
|
|
23
|
+
pressed = self.theme.pressed()
|
|
24
|
+
|
|
25
|
+
self.setStyleSheet(f"""
|
|
26
|
+
QPushButton {{
|
|
27
|
+
background-color: {bg};
|
|
28
|
+
color: {self.text_color};
|
|
29
|
+
padding: {pad_v}px {pad_h}px;
|
|
30
|
+
border-radius: 6px;
|
|
31
|
+
font-family: '{self.theme.font_family}';
|
|
32
|
+
border: none;
|
|
33
|
+
}}
|
|
34
|
+
QPushButton:hover {{
|
|
35
|
+
background-color: {hover};
|
|
36
|
+
}}
|
|
37
|
+
QPushButton:pressed {{
|
|
38
|
+
background-color: {pressed};
|
|
39
|
+
}}
|
|
40
|
+
QPushButton:disabled {{
|
|
41
|
+
background-color: #999;
|
|
42
|
+
color: #666;
|
|
43
|
+
}}
|
|
44
|
+
""")
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# widgets/indus_alternate_button.py
|
|
2
|
+
from PySide6.QtWidgets import QPushButton
|
|
3
|
+
from PySide6.QtGui import QFont, QIcon, QColor
|
|
4
|
+
|
|
5
|
+
def darken(hex_color: str, factor: float = 0.7) -> str:
|
|
6
|
+
c = QColor(hex_color)
|
|
7
|
+
r = int(c.red() * factor)
|
|
8
|
+
g = int(c.green() * factor)
|
|
9
|
+
b = int(c.blue() * factor)
|
|
10
|
+
return f"#{r:02x}{g:02x}{b:02x}"
|
|
11
|
+
|
|
12
|
+
class IndusAlternateButton(QPushButton):
|
|
13
|
+
def __init__(self, text, theme, icon_path=None, diameter=48, text_color=None):
|
|
14
|
+
super().__init__(text)
|
|
15
|
+
self.theme = theme
|
|
16
|
+
self.diameter = diameter
|
|
17
|
+
self.text_color = text_color or theme.text_color
|
|
18
|
+
|
|
19
|
+
self.setCheckable(True)
|
|
20
|
+
self.setFixedSize(diameter, diameter)
|
|
21
|
+
self.setFont(QFont(theme.font_family, theme.font_size()))
|
|
22
|
+
|
|
23
|
+
if icon_path:
|
|
24
|
+
self.setIcon(QIcon(icon_path))
|
|
25
|
+
|
|
26
|
+
self.apply_style()
|
|
27
|
+
self.toggled.connect(self.apply_style)
|
|
28
|
+
|
|
29
|
+
def apply_style(self):
|
|
30
|
+
if self.isChecked():
|
|
31
|
+
bg = self.theme.primary
|
|
32
|
+
hover = self.theme.hover()
|
|
33
|
+
pressed = self.theme.pressed()
|
|
34
|
+
else:
|
|
35
|
+
bg = self.theme.primary_dark(0.5)
|
|
36
|
+
hover = self.theme.primary_dark(0.6)
|
|
37
|
+
pressed = self.theme.primary_dark(0.4)
|
|
38
|
+
|
|
39
|
+
border_color = darken(self.theme.primary, 0.8)
|
|
40
|
+
|
|
41
|
+
self.setStyleSheet(f"""
|
|
42
|
+
QPushButton {{
|
|
43
|
+
background-color: {bg};
|
|
44
|
+
color: {self.text_color};
|
|
45
|
+
border-radius: {self.diameter // 2}px;
|
|
46
|
+
border: 2px solid {border_color};
|
|
47
|
+
font-family: '{self.theme.font_family}';
|
|
48
|
+
font-weight: bold;
|
|
49
|
+
}}
|
|
50
|
+
QPushButton:hover {{
|
|
51
|
+
background-color: {hover};
|
|
52
|
+
}}
|
|
53
|
+
QPushButton:pressed {{
|
|
54
|
+
background-color: {pressed};
|
|
55
|
+
}}
|
|
56
|
+
QPushButton:disabled {{
|
|
57
|
+
background-color: #999;
|
|
58
|
+
color: #666;
|
|
59
|
+
}}
|
|
60
|
+
""")
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# widgets/indus_lamp.py
|
|
2
|
+
from PySide6.QtWidgets import QLabel
|
|
3
|
+
from PySide6.QtGui import QFont
|
|
4
|
+
from PySide6.QtCore import Qt
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class IndusLamp(QLabel):
|
|
8
|
+
def __init__(self, text, theme, diameter=48,
|
|
9
|
+
state=False, text_color=None):
|
|
10
|
+
super().__init__(text)
|
|
11
|
+
|
|
12
|
+
self.theme = theme
|
|
13
|
+
self.diameter = diameter
|
|
14
|
+
self.state = state
|
|
15
|
+
self.text_color = text_color or theme.text_color
|
|
16
|
+
|
|
17
|
+
self.setFixedSize(diameter, diameter)
|
|
18
|
+
self.setAlignment(Qt.AlignCenter)
|
|
19
|
+
self.setFont(QFont(theme.font_family, theme.font_size()))
|
|
20
|
+
|
|
21
|
+
self.apply_style()
|
|
22
|
+
|
|
23
|
+
# -------------------------
|
|
24
|
+
# 状態変更
|
|
25
|
+
# -------------------------
|
|
26
|
+
def set_state(self, state: bool):
|
|
27
|
+
self.state = state
|
|
28
|
+
self.apply_style()
|
|
29
|
+
|
|
30
|
+
# -------------------------
|
|
31
|
+
# 色適用(丸ボタンと完全一致)
|
|
32
|
+
# -------------------------
|
|
33
|
+
def apply_style(self):
|
|
34
|
+
if not self.isEnabled():
|
|
35
|
+
bg = "#999"
|
|
36
|
+
fg = "#666"
|
|
37
|
+
else:
|
|
38
|
+
if self.state:
|
|
39
|
+
bg = self.theme.primary
|
|
40
|
+
else:
|
|
41
|
+
bg = self.theme.primary_dark(0.5)
|
|
42
|
+
fg = self.text_color
|
|
43
|
+
|
|
44
|
+
self.setStyleSheet(f"""
|
|
45
|
+
QLabel {{
|
|
46
|
+
background-color: {bg};
|
|
47
|
+
color: {fg};
|
|
48
|
+
border-radius: {self.diameter // 2}px;
|
|
49
|
+
border: none;
|
|
50
|
+
font-family: '{self.theme.font_family}';
|
|
51
|
+
font-weight: bold;
|
|
52
|
+
}}
|
|
53
|
+
""")
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# widgets/indus_momentary_button.py
|
|
2
|
+
from PySide6.QtWidgets import QPushButton
|
|
3
|
+
from PySide6.QtGui import QFont, QIcon, QColor
|
|
4
|
+
|
|
5
|
+
def darken(hex_color: str, factor: float = 0.7) -> str:
|
|
6
|
+
c = QColor(hex_color)
|
|
7
|
+
r = int(c.red() * factor)
|
|
8
|
+
g = int(c.green() * factor)
|
|
9
|
+
b = int(c.blue() * factor)
|
|
10
|
+
return f"#{r:02x}{g:02x}{b:02x}"
|
|
11
|
+
|
|
12
|
+
class IndusMomentaryButton(QPushButton):
|
|
13
|
+
def __init__(self, text, theme, icon_path=None, diameter=48, text_color=None):
|
|
14
|
+
super().__init__(text)
|
|
15
|
+
self.theme = theme
|
|
16
|
+
self.diameter = diameter
|
|
17
|
+
self.text_color = text_color or theme.text_color
|
|
18
|
+
|
|
19
|
+
self.setCheckable(False)
|
|
20
|
+
self.setFixedSize(diameter, diameter)
|
|
21
|
+
self.setFont(QFont(theme.font_family, theme.font_size()))
|
|
22
|
+
|
|
23
|
+
if icon_path:
|
|
24
|
+
self.setIcon(QIcon(icon_path))
|
|
25
|
+
|
|
26
|
+
self.apply_style()
|
|
27
|
+
|
|
28
|
+
def apply_style(self):
|
|
29
|
+
bg = darken(self.theme.primary, 0.5)
|
|
30
|
+
hover = darken(self.theme.primary, 0.6)
|
|
31
|
+
pressed = self.theme.primary
|
|
32
|
+
border_color = darken(self.theme.primary, 0.8)
|
|
33
|
+
|
|
34
|
+
self.setStyleSheet(f"""
|
|
35
|
+
QPushButton {{
|
|
36
|
+
background-color: {bg};
|
|
37
|
+
color: {self.text_color};
|
|
38
|
+
border-radius: {self.diameter // 2}px;
|
|
39
|
+
border: 2px solid {border_color};
|
|
40
|
+
font-family: '{self.theme.font_family}';
|
|
41
|
+
font-weight: bold;
|
|
42
|
+
}}
|
|
43
|
+
QPushButton:hover {{
|
|
44
|
+
background-color: {hover};
|
|
45
|
+
}}
|
|
46
|
+
QPushButton:pressed {{
|
|
47
|
+
background-color: {pressed};
|
|
48
|
+
}}
|
|
49
|
+
QPushButton:disabled {{
|
|
50
|
+
background-color: #999;
|
|
51
|
+
color: #666;
|
|
52
|
+
}}
|
|
53
|
+
""")
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# widgets/label.py
|
|
2
|
+
from PySide6.QtWidgets import QLabel
|
|
3
|
+
from PySide6.QtGui import QFont
|
|
4
|
+
|
|
5
|
+
class StyledLabel(QLabel):
|
|
6
|
+
def __init__(self, text, theme, bold=False,
|
|
7
|
+
text_color=None, background_color=None):
|
|
8
|
+
super().__init__(text)
|
|
9
|
+
|
|
10
|
+
weight = QFont.Bold if bold else QFont.Normal
|
|
11
|
+
self.setFont(QFont(theme.font_family, theme.font_size(), weight))
|
|
12
|
+
|
|
13
|
+
# 文字色(個別指定 > theme)
|
|
14
|
+
color = text_color or theme.text_color
|
|
15
|
+
|
|
16
|
+
# 背景色(個別指定 > transparent)
|
|
17
|
+
bg = background_color or "transparent"
|
|
18
|
+
|
|
19
|
+
self.setStyleSheet(f"""
|
|
20
|
+
QLabel {{
|
|
21
|
+
color: {color};
|
|
22
|
+
background: {bg};
|
|
23
|
+
font-family: '{theme.font_family}';
|
|
24
|
+
}}
|
|
25
|
+
""")
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# widgets/lineedit.py
|
|
2
|
+
from PySide6.QtWidgets import QWidget, QLineEdit, QLabel, QVBoxLayout, QToolTip
|
|
3
|
+
from PySide6.QtGui import QFont, QRegularExpressionValidator, QIntValidator, QDoubleValidator
|
|
4
|
+
from PySide6.QtCore import QRegularExpression
|
|
5
|
+
|
|
6
|
+
class StyledLineEdit(QWidget):
|
|
7
|
+
def __init__(self, placeholder, theme, mode="free",
|
|
8
|
+
min_val=None, max_val=None):
|
|
9
|
+
super().__init__()
|
|
10
|
+
|
|
11
|
+
self.theme = theme
|
|
12
|
+
self.mode = mode
|
|
13
|
+
self.min_val = min_val
|
|
14
|
+
self.max_val = max_val
|
|
15
|
+
|
|
16
|
+
self.edit = QLineEdit()
|
|
17
|
+
self.error_label = QLabel(" ")
|
|
18
|
+
self.error_label.setFixedHeight(14)
|
|
19
|
+
self.error_label.setStyleSheet("color: transparent; font-size: 11px;")
|
|
20
|
+
|
|
21
|
+
layout = QVBoxLayout(self)
|
|
22
|
+
layout.setContentsMargins(0, 0, 0, 0)
|
|
23
|
+
layout.addWidget(self.edit)
|
|
24
|
+
layout.addWidget(self.error_label)
|
|
25
|
+
|
|
26
|
+
self.edit.setPlaceholderText(placeholder)
|
|
27
|
+
self.edit.setFont(QFont(theme.font_family, theme.font_size()))
|
|
28
|
+
|
|
29
|
+
self.apply_style()
|
|
30
|
+
self.apply_validator(mode, min_val, max_val)
|
|
31
|
+
|
|
32
|
+
def apply_style(self, error=False):
|
|
33
|
+
pad_v, pad_h = self.theme.padding()
|
|
34
|
+
border_color = "red" if error else self.theme.border_color
|
|
35
|
+
|
|
36
|
+
self.edit.setStyleSheet(f"""
|
|
37
|
+
QLineEdit {{
|
|
38
|
+
padding: {pad_v}px {pad_h}px;
|
|
39
|
+
border: 2px solid {border_color};
|
|
40
|
+
border-radius: 6px;
|
|
41
|
+
background: {self.theme.background};
|
|
42
|
+
color: {self.theme.text_color};
|
|
43
|
+
font-family: '{self.theme.font_family}';
|
|
44
|
+
}}
|
|
45
|
+
QLineEdit:focus {{
|
|
46
|
+
border: 2px solid {self.theme.primary};
|
|
47
|
+
}}
|
|
48
|
+
""")
|
|
49
|
+
|
|
50
|
+
def apply_validator(self, mode, min_val, max_val):
|
|
51
|
+
if mode == "free":
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
if mode == "numeric":
|
|
55
|
+
regex = QRegularExpression(r"^[0-9]+$")
|
|
56
|
+
self.edit.setValidator(QRegularExpressionValidator(regex))
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
if mode == "numeric_range":
|
|
60
|
+
self._apply_numeric_range(min_val, max_val)
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
if mode == "alnum":
|
|
64
|
+
regex = QRegularExpression(r"^[A-Za-z0-9]+$")
|
|
65
|
+
self.edit.setValidator(QRegularExpressionValidator(regex))
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
if mode == "filename":
|
|
69
|
+
regex = QRegularExpression(r'^[^\\/:*?"<>|]+$')
|
|
70
|
+
self.edit.setValidator(QRegularExpressionValidator(regex))
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
raise ValueError(f"Unknown mode: {mode}")
|
|
74
|
+
|
|
75
|
+
def _apply_numeric_range(self, min_val, max_val):
|
|
76
|
+
if min_val is None or max_val is None:
|
|
77
|
+
raise ValueError("numeric_range requires min_val and max_val")
|
|
78
|
+
|
|
79
|
+
is_int = isinstance(min_val, int) and isinstance(max_val, int)
|
|
80
|
+
|
|
81
|
+
if is_int:
|
|
82
|
+
validator = QIntValidator(min_val, max_val)
|
|
83
|
+
else:
|
|
84
|
+
decimals = max(self._decimal_places(min_val),
|
|
85
|
+
self._decimal_places(max_val))
|
|
86
|
+
validator = QDoubleValidator(min_val, max_val, decimals)
|
|
87
|
+
validator.setNotation(QDoubleValidator.StandardNotation)
|
|
88
|
+
|
|
89
|
+
self.edit.setValidator(validator)
|
|
90
|
+
|
|
91
|
+
def _decimal_places(self, value):
|
|
92
|
+
s = str(value)
|
|
93
|
+
return len(s.split(".")[1]) if "." in s else 0
|
|
94
|
+
|
|
95
|
+
def value(self):
|
|
96
|
+
try:
|
|
97
|
+
return float(self.edit.text())
|
|
98
|
+
except ValueError:
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
def is_valid(self):
|
|
102
|
+
if self.mode != "numeric_range":
|
|
103
|
+
return True
|
|
104
|
+
|
|
105
|
+
v = self.value()
|
|
106
|
+
if v is None:
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
return self.min_val <= v <= self.max_val
|
|
110
|
+
|
|
111
|
+
def show_error(self, message=None):
|
|
112
|
+
if message is None:
|
|
113
|
+
self.error_label.setText(" ")
|
|
114
|
+
self.error_label.setStyleSheet("color: transparent; font-size: 11px;")
|
|
115
|
+
self.apply_style(error=True)
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
if message == "":
|
|
119
|
+
self.error_label.setText(" ")
|
|
120
|
+
self.error_label.setStyleSheet("color: transparent; font-size: 11px;")
|
|
121
|
+
self.apply_style(error=False)
|
|
122
|
+
return
|
|
123
|
+
|
|
124
|
+
QToolTip.showText(self.edit.mapToGlobal(self.edit.rect().bottomLeft()), message)
|
|
125
|
+
|
|
126
|
+
self.error_label.setText(" ")
|
|
127
|
+
self.error_label.setStyleSheet("color: transparent; font-size: 11px;")
|
|
128
|
+
self.apply_style(error=True)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyside6stylekit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight and extensible styled UI component kit for PySide6 applications.
|
|
5
|
+
Author: Noritama-Lab
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/Noritama-Lab/PySide6StyleKit
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Topic :: Software Development :: User Interfaces
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: PySide6>=6.0.0
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# PySide6StyleKit
|
|
27
|
+
PySide6 アプリのための軽量で拡張性の高いスタイル済み UI コンポーネントキット。
|
|
28
|
+
A lightweight and extensible styled UI component kit for PySide6 applications.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 特徴 / Features
|
|
33
|
+
- 統一テーマ(ライト/ダーク/背景色)
|
|
34
|
+
Unified themes (light, dark, background-aware)
|
|
35
|
+
- スタイル済みウィジェット(Label / LineEdit / Button / Indus Alternate Button / Indus Lamp / Indus Momentary Button など)
|
|
36
|
+
Styled widgets such as Label, LineEdit, Button, Indus Alternate Button, Indus Lamp, and Indus Momentary Button
|
|
37
|
+
- 数値バリデーション(int / float / range)
|
|
38
|
+
Robust numeric validation (int, float, range)
|
|
39
|
+
- エラー表示の柔軟な制御(枠線のみ・メッセージ表示)
|
|
40
|
+
Flexible error signaling (border-only or message)
|
|
41
|
+
- 拡張しやすいモジュール構造
|
|
42
|
+
Modular and extensible architecture
|
|
43
|
+
- 実用的なサンプルコード付き
|
|
44
|
+
Includes practical example scripts
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## インストール / Installation
|
|
49
|
+
開発モードで利用する場合:
|
|
50
|
+
For development mode:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install -e .
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 使い方 / Usage
|
|
59
|
+
`examples/demo.py` と `examples/demo_indus.py` に基本的な使い方があります。
|
|
60
|
+
Basic usage is available in `examples/demo.py` and `examples/demo_indus.py`.
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from pyside6stylekit import Theme, StyledLabel, StyledLineEdit, StyledButton
|
|
64
|
+
|
|
65
|
+
theme = Theme.light()
|
|
66
|
+
|
|
67
|
+
label = StyledLabel("Hello")
|
|
68
|
+
lineedit = StyledLineEdit(validator="int")
|
|
69
|
+
button = StyledButton("Submit")
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## プロジェクト構造 / Project Structure
|
|
75
|
+
```
|
|
76
|
+
pyside6stylekit/
|
|
77
|
+
├─ pyside6stylekit/
|
|
78
|
+
│ ├─ utils/
|
|
79
|
+
│ │ ├─ __init__.py
|
|
80
|
+
│ │ └─ colors.py
|
|
81
|
+
│ ├─ widgets/
|
|
82
|
+
│ │ ├─ __init__.py
|
|
83
|
+
│ │ ├─ button.py
|
|
84
|
+
│ │ ├─ indus_alternate_button.py
|
|
85
|
+
│ │ ├─ indus_lamp.py
|
|
86
|
+
│ │ ├─ indus_momentary_button.py
|
|
87
|
+
│ │ ├─ label.py
|
|
88
|
+
│ │ └─ lineedit.py
|
|
89
|
+
│ ├─ __init__.py
|
|
90
|
+
│ ├─ presets.py
|
|
91
|
+
│ └─ theme.py
|
|
92
|
+
├─ examples/
|
|
93
|
+
│ ├─ demo.py
|
|
94
|
+
│ └─ demo_indus.py
|
|
95
|
+
├─ LICENSE
|
|
96
|
+
└─ README.md
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## ライセンス / License
|
|
102
|
+
MIT License © 2026 Mitsunori
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 作者 / Author
|
|
107
|
+
Noritama-Lab
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
pyside6stylekit/__init__.py
|
|
5
|
+
pyside6stylekit/presets.py
|
|
6
|
+
pyside6stylekit/theme.py
|
|
7
|
+
pyside6stylekit.egg-info/PKG-INFO
|
|
8
|
+
pyside6stylekit.egg-info/SOURCES.txt
|
|
9
|
+
pyside6stylekit.egg-info/dependency_links.txt
|
|
10
|
+
pyside6stylekit.egg-info/requires.txt
|
|
11
|
+
pyside6stylekit.egg-info/top_level.txt
|
|
12
|
+
pyside6stylekit/utils/__init__.py
|
|
13
|
+
pyside6stylekit/utils/colors.py
|
|
14
|
+
pyside6stylekit/widgets/__init__.py
|
|
15
|
+
pyside6stylekit/widgets/button.py
|
|
16
|
+
pyside6stylekit/widgets/indus_alternate_button.py
|
|
17
|
+
pyside6stylekit/widgets/indus_lamp.py
|
|
18
|
+
pyside6stylekit/widgets/indus_momentary_button.py
|
|
19
|
+
pyside6stylekit/widgets/label.py
|
|
20
|
+
pyside6stylekit/widgets/lineedit.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
PySide6>=6.0.0
|