pysfi 0.1.10__tar.gz → 0.1.11__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.
- {pysfi-0.1.10 → pysfi-0.1.11}/.gitignore +1 -1
- {pysfi-0.1.10 → pysfi-0.1.11}/PKG-INFO +7 -7
- {pysfi-0.1.10 → pysfi-0.1.11}/README.md +6 -6
- {pysfi-0.1.10 → pysfi-0.1.11}/pyproject.toml +27 -6
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/__init__.py +1 -1
- pysfi-0.1.11/sfi/alarmclock/README.md +64 -0
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/alarmclock/alarmclock.py +40 -40
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/bumpversion/README.md +218 -218
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/bumpversion/__init__.py +1 -1
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/bumpversion/tests/test_bumpversion.py +698 -698
- pysfi-0.1.11/sfi/cleanbuild/cleanbuild.py +155 -0
- pysfi-0.1.11/sfi/condasetup/condasetup.py +116 -0
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/README.md +813 -813
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/__init__.py +1 -1
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/docscan_gui.py +1 -1
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/lang/eng.py +152 -152
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/lang/zhcn.py +170 -170
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/tests/__init__.py +1 -1
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/tests/test_benchmark.py +416 -427
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/tests/test_docscan.py +1245 -1245
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/tests/test_enhanced.py +279 -267
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/filedate/README.md +45 -45
- pysfi-0.1.11/sfi/filedate/filedate.py +185 -0
- pysfi-0.1.11/sfi/filedate/tests/test_filedate.py +830 -0
- pysfi-0.1.11/sfi/gittool/__init__.py +2 -0
- pysfi-0.1.11/sfi/gittool/gittool.py +401 -0
- pysfi-0.1.11/sfi/llmclient/llmclient.py +592 -0
- pysfi-0.1.11/sfi/llmclient/tests/__init__.py +1 -0
- pysfi-0.1.11/sfi/llmclient/tests/test_benchmark.py +399 -0
- pysfi-0.1.11/sfi/llmclient/tests/test_llmclient.py +636 -0
- pysfi-0.1.11/sfi/llmquantize/llmquantize.py +480 -0
- pysfi-0.1.11/sfi/llmquantize/tests/__init__.py +1 -0
- pysfi-0.1.11/sfi/llmquantize/tests/test_llmquantize.py +518 -0
- pysfi-0.1.11/sfi/llmserver/llmserver.py +335 -0
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/makepython/makepython.py +2 -2
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/pdfsplit/pdfsplit.py +4 -4
- pysfi-0.1.11/sfi/pyarchive/pyarchive.py +418 -0
- pysfi-0.1.11/sfi/pyembedinstall/pyembedinstall.py +629 -0
- pysfi-0.1.11/sfi/pyembedinstall/tests/test_pyembedinstall.py +1626 -0
- pysfi-0.1.11/sfi/pylibpack/pylibpack.py +1457 -0
- pysfi-0.1.11/sfi/pylibpack/tests/test_benchmark.py +653 -0
- pysfi-0.1.11/sfi/pylibpack/tests/test_pylibpack.py +774 -0
- pysfi-0.1.11/sfi/pyloadergen/__init__.py +0 -0
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/pyloadergen/pyloadergen.py +271 -572
- pysfi-0.1.11/sfi/pyloadergen/tests/__init__.py +0 -0
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/pyloadergen/tests/test_benchmark.py +235 -235
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/pyloadergen/tests/test_pyloadergen.py +10 -154
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/pypack/README.md +8 -8
- pysfi-0.1.11/sfi/pypack/__init__.py +0 -0
- pysfi-0.1.11/sfi/pypack/pypack.py +1142 -0
- pysfi-0.1.11/sfi/pypack/tests/__init__.py +0 -0
- pysfi-0.1.11/sfi/pypack/tests/test_pack.py +124 -0
- pysfi-0.1.11/sfi/pyprojectparse/README.md +104 -0
- pysfi-0.1.11/sfi/pyprojectparse/__init__.py +0 -0
- pysfi-0.1.11/sfi/pyprojectparse/pyprojectparse.py +500 -0
- pysfi-0.1.11/sfi/pyprojectparse/tests/test_benchmark.py +817 -0
- pysfi-0.1.11/sfi/pyprojectparse/tests/test_projectparse.py +604 -0
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/pysourcepack/README.md +201 -201
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/pysourcepack/pysourcepack.py +308 -369
- pysfi-0.1.11/sfi/quizbase/README.md +397 -0
- pysfi-0.1.11/sfi/quizbase/__init__.py +0 -0
- pysfi-0.1.11/sfi/quizbase/quizbase.py +828 -0
- pysfi-0.1.11/sfi/quizbase/quizbase_gui.py +987 -0
- pysfi-0.1.11/sfi/quizbase/tests/__init__.py +1 -0
- pysfi-0.1.11/sfi/quizbase/tests/test_quizbase.py +590 -0
- pysfi-0.1.11/sfi/quizbase/tests/test_quizbase_gui.py +371 -0
- pysfi-0.1.11/sfi/regexvalidate/README.md +60 -0
- pysfi-0.1.11/sfi/regexvalidate/__init__.py +0 -0
- pysfi-0.1.11/sfi/regexvalidate/regexvalidate.py +468 -0
- pysfi-0.1.11/sfi/taskkill/__init__.py +0 -0
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/taskkill/taskkill.py +0 -2
- pysfi-0.1.11/sfi/which/__init__.py +0 -0
- pysfi-0.1.11/sfi/workflowengine/README.md +0 -0
- pysfi-0.1.11/sfi/workflowengine/__init__.py +0 -0
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/workflowengine/tests/__init__.py +1 -1
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/workflowengine/tests/test_benchmark.py +725 -725
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/workflowengine/tests/test_workflowengine.py +740 -740
- pysfi-0.1.10/sfi/embedinstall/embedinstall.py +0 -478
- pysfi-0.1.10/sfi/filedate/filedate.py +0 -112
- pysfi-0.1.10/sfi/projectparse/projectparse.py +0 -152
- pysfi-0.1.10/sfi/pylibpack/pylibpack.py +0 -913
- pysfi-0.1.10/sfi/pypack/pypack.py +0 -791
- pysfi-0.1.10/sfi/pypack/tests/test_pack.py +0 -257
- {pysfi-0.1.10 → pysfi-0.1.11}/examples/pack_demo/README.md +0 -0
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/alarmclock/__init__.py +0 -0
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/bumpversion/bumpversion.py +0 -0
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/bumpversion/tests/__init__.py +0 -0
- {pysfi-0.1.10/sfi/workflowengine → pysfi-0.1.11/sfi/cleanbuild}/README.md +0 -0
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/cli.py +0 -0
- /pysfi-0.1.10/sfi/docscan/lang/__init__.py → /pysfi-0.1.11/sfi/condasetup/README.md +0 -0
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/docscan.py +0 -0
- {pysfi-0.1.10/sfi/embedinstall → pysfi-0.1.11/sfi/docscan/lang}/__init__.py +0 -0
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/filedate/__init__.py +0 -0
- /pysfi-0.1.10/sfi/makepython/__init__.py → /pysfi-0.1.11/sfi/gittool/README.md +0 -0
- /pysfi-0.1.10/sfi/pdfsplit/__init__.py → /pysfi-0.1.11/sfi/llmclient/README.md +0 -0
- /pysfi-0.1.10/sfi/pdfsplit/tests/__init__.py → /pysfi-0.1.11/sfi/llmquantize/README.md +0 -0
- {pysfi-0.1.10/sfi/projectparse → pysfi-0.1.11/sfi/llmquantize}/__init__.py +0 -0
- /pysfi-0.1.10/sfi/pylibpack/__init__.py → /pysfi-0.1.11/sfi/llmserver/README.md +0 -0
- {pysfi-0.1.10/sfi/pyloadergen → pysfi-0.1.11/sfi/makepython}/__init__.py +0 -0
- {pysfi-0.1.10/sfi/pyloadergen/tests → pysfi-0.1.11/sfi/pdfsplit}/__init__.py +0 -0
- {pysfi-0.1.10/sfi/pypack → pysfi-0.1.11/sfi/pdfsplit/tests}/__init__.py +0 -0
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/pdfsplit/tests/test_pdfsplit.py +0 -0
- /pysfi-0.1.10/sfi/pypack/tests/__init__.py → /pysfi-0.1.11/sfi/pyarchive/README.md +0 -0
- {pysfi-0.1.10/sfi/taskkill → pysfi-0.1.11/sfi/pyembedinstall}/__init__.py +0 -0
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/pylibpack/README.md +0 -0
- {pysfi-0.1.10/sfi/which → pysfi-0.1.11/sfi/pylibpack}/__init__.py +0 -0
- {pysfi-0.1.10/sfi/workflowengine → pysfi-0.1.11/sfi/pylibpack/tests}/__init__.py +0 -0
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/which/which.py +0 -0
- {pysfi-0.1.10 → pysfi-0.1.11}/sfi/workflowengine/workflowengine.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pysfi
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.11
|
|
4
4
|
Summary: Single File commands for Interactive python.
|
|
5
5
|
Requires-Python: >=3.8
|
|
6
6
|
Requires-Dist: tomli>=2.4.0; python_version < '3.11'
|
|
@@ -47,10 +47,10 @@ pysfi is a Python project that provides single-file command-line utilities, desi
|
|
|
47
47
|
|
|
48
48
|
- **alarmclk**: Alarm clock functionality
|
|
49
49
|
- **[bumpversion](sfi/bumpversion/README.md)**: Automated version number management tool
|
|
50
|
-
- **
|
|
50
|
+
- **pyembedinstall**: Embed installation utilities
|
|
51
51
|
- **[filedate](sfi/filedate/README.md)**: A file date management tool that normalizes date prefixes in filenames
|
|
52
52
|
- **mkp**: Make Python project utilities
|
|
53
|
-
- **
|
|
53
|
+
- **pyprojectparse**: Project parsing and analysis tools
|
|
54
54
|
- **pyloadergen**: Python loader code generation
|
|
55
55
|
- **pypack**: Python packaging utilities
|
|
56
56
|
|
|
@@ -101,8 +101,8 @@ pysfi/
|
|
|
101
101
|
│ ├── alarmclock.py
|
|
102
102
|
│ ├── pyproject.toml
|
|
103
103
|
│ └── __init__.py
|
|
104
|
-
├──
|
|
105
|
-
│ ├──
|
|
104
|
+
├── pyembedinstall/ # pyembedinstall command module
|
|
105
|
+
│ ├── pyembedinstall.py
|
|
106
106
|
│ ├── pyproject.toml
|
|
107
107
|
│ └── __init__.py
|
|
108
108
|
├── filedate/ # filedate command module
|
|
@@ -114,8 +114,8 @@ pysfi/
|
|
|
114
114
|
│ ├── makepython.py
|
|
115
115
|
│ ├── pyproject.toml
|
|
116
116
|
│ └── __init__.py
|
|
117
|
-
├──
|
|
118
|
-
│ ├──
|
|
117
|
+
├── pyprojectparse/ # pyprojectparse command module
|
|
118
|
+
│ ├── pyprojectparse.py
|
|
119
119
|
│ ├── pyproject.toml
|
|
120
120
|
│ └── __init__.py
|
|
121
121
|
├── pyloadergen/ # pyloadergen command module
|
|
@@ -10,10 +10,10 @@ pysfi is a Python project that provides single-file command-line utilities, desi
|
|
|
10
10
|
|
|
11
11
|
- **alarmclk**: Alarm clock functionality
|
|
12
12
|
- **[bumpversion](sfi/bumpversion/README.md)**: Automated version number management tool
|
|
13
|
-
- **
|
|
13
|
+
- **pyembedinstall**: Embed installation utilities
|
|
14
14
|
- **[filedate](sfi/filedate/README.md)**: A file date management tool that normalizes date prefixes in filenames
|
|
15
15
|
- **mkp**: Make Python project utilities
|
|
16
|
-
- **
|
|
16
|
+
- **pyprojectparse**: Project parsing and analysis tools
|
|
17
17
|
- **pyloadergen**: Python loader code generation
|
|
18
18
|
- **pypack**: Python packaging utilities
|
|
19
19
|
|
|
@@ -64,8 +64,8 @@ pysfi/
|
|
|
64
64
|
│ ├── alarmclock.py
|
|
65
65
|
│ ├── pyproject.toml
|
|
66
66
|
│ └── __init__.py
|
|
67
|
-
├──
|
|
68
|
-
│ ├──
|
|
67
|
+
├── pyembedinstall/ # pyembedinstall command module
|
|
68
|
+
│ ├── pyembedinstall.py
|
|
69
69
|
│ ├── pyproject.toml
|
|
70
70
|
│ └── __init__.py
|
|
71
71
|
├── filedate/ # filedate command module
|
|
@@ -77,8 +77,8 @@ pysfi/
|
|
|
77
77
|
│ ├── makepython.py
|
|
78
78
|
│ ├── pyproject.toml
|
|
79
79
|
│ └── __init__.py
|
|
80
|
-
├──
|
|
81
|
-
│ ├──
|
|
80
|
+
├── pyprojectparse/ # pyprojectparse command module
|
|
81
|
+
│ ├── pyprojectparse.py
|
|
82
82
|
│ ├── pyproject.toml
|
|
83
83
|
│ └── __init__.py
|
|
84
84
|
├── pyloadergen/ # pyloadergen command module
|
|
@@ -8,23 +8,33 @@ description = "Single File commands for Interactive python."
|
|
|
8
8
|
name = "pysfi"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
11
|
-
version = "0.1.
|
|
11
|
+
version = "0.1.11"
|
|
12
12
|
|
|
13
13
|
[project.scripts]
|
|
14
14
|
alarmclk = "sfi.alarmclock.alarmclock:main"
|
|
15
15
|
bumpversion = "sfi.bumpversion.bumpversion:main"
|
|
16
|
+
cleanbuild = "sfi.cleanbuild.cleanbuild:main"
|
|
17
|
+
condasetup = "sfi.condasetup.condasetup:main"
|
|
16
18
|
docscan = "sfi.docscan.docscan:main"
|
|
17
19
|
docscan-gui = "sfi.docscan.docscan_gui:main"
|
|
18
|
-
embedinstall = "sfi.embedinstall.embedinstall:main"
|
|
19
20
|
filedate = "sfi.filedate.filedate:main"
|
|
21
|
+
gitt = "sfi.gittool.gittool:main"
|
|
22
|
+
llmcli = "sfi.llmclient.llmclient:main"
|
|
23
|
+
llmqnt = "sfi.llmquantize.llmquantize:main"
|
|
24
|
+
llmsvr = "sfi.llmserver.llmserver:main"
|
|
20
25
|
mkp = "sfi.makepython.makepython:main"
|
|
21
26
|
pdfsplit = "sfi.pdfsplit.pdfsplit:main"
|
|
22
|
-
|
|
27
|
+
pyarchive = "sfi.pyarchive.pyarchive:main"
|
|
28
|
+
pyembedinstall = "sfi.pyembedinstall.pyembedinstall:main"
|
|
23
29
|
pylibpack = "sfi.pylibpack.pylibpack:main"
|
|
24
30
|
pyloadergen = "sfi.pyloadergen.pyloadergen:main"
|
|
25
31
|
pyp = "sfi.pypack.pypack:main"
|
|
26
32
|
pypack = "sfi.pypack.pypack:main"
|
|
33
|
+
pyprojectparse = "sfi.pyprojectparse.pyprojectparse:main"
|
|
27
34
|
pysourcepack = "sfi.pysourcepack.pysourcepack:main"
|
|
35
|
+
quizbase = "sfi.quizbase.quizbase:main"
|
|
36
|
+
quizbase-gui = "sfi.quizbase.quizbase_gui:main"
|
|
37
|
+
regval = "sfi.regexvalidate.regexvalidate:main"
|
|
28
38
|
sfi = "sfi.cli:main"
|
|
29
39
|
taskk = "sfi.taskkill.taskkill:main"
|
|
30
40
|
wch = "sfi.which.which:main"
|
|
@@ -55,7 +65,7 @@ packages = ["sfi"]
|
|
|
55
65
|
include = ["README.md", "sfi/**/*.py", "sfi/pyproject.toml"]
|
|
56
66
|
|
|
57
67
|
[tool.ruff]
|
|
58
|
-
line-length =
|
|
68
|
+
line-length = 88
|
|
59
69
|
target-version = "py38"
|
|
60
70
|
|
|
61
71
|
# Exclude files and directories
|
|
@@ -117,16 +127,25 @@ known-first-party = ["sfi"]
|
|
|
117
127
|
members = [
|
|
118
128
|
"sfi/alarmclock",
|
|
119
129
|
"sfi/bumpversion",
|
|
130
|
+
"sfi/cleanbuild",
|
|
131
|
+
"sfi/condasetup",
|
|
120
132
|
"sfi/docscan",
|
|
121
|
-
"sfi/embedinstall",
|
|
122
133
|
"sfi/filedate",
|
|
134
|
+
"sfi/gittool",
|
|
135
|
+
"sfi/llmclient",
|
|
136
|
+
"sfi/llmquantize",
|
|
137
|
+
"sfi/llmserver",
|
|
123
138
|
"sfi/makepython",
|
|
124
139
|
"sfi/pdfsplit",
|
|
125
|
-
"sfi/
|
|
140
|
+
"sfi/pyarchive",
|
|
141
|
+
"sfi/pyembedinstall",
|
|
126
142
|
"sfi/pylibpack",
|
|
127
143
|
"sfi/pyloadergen",
|
|
128
144
|
"sfi/pypack",
|
|
145
|
+
"sfi/pyprojectparse",
|
|
129
146
|
"sfi/pysourcepack",
|
|
147
|
+
"sfi/quizbase",
|
|
148
|
+
"sfi/regexvalidate",
|
|
130
149
|
"sfi/taskkill",
|
|
131
150
|
"sfi/which",
|
|
132
151
|
"sfi/workflowengine",
|
|
@@ -135,12 +154,14 @@ members = [
|
|
|
135
154
|
[dependency-groups]
|
|
136
155
|
dev = [
|
|
137
156
|
"hatch>=1.14.2",
|
|
157
|
+
"hypothesis>=6.113.0",
|
|
138
158
|
"pysfi[all]",
|
|
139
159
|
"pyside2>=5.15.2.1",
|
|
140
160
|
"pytest-asyncio>=0.24.0",
|
|
141
161
|
"pytest-benchmark>=4.0.0",
|
|
142
162
|
"pytest-cov>=5.0.0",
|
|
143
163
|
"pytest-mock>=3.14.0",
|
|
164
|
+
"pytest-qt>=4.4.0",
|
|
144
165
|
"pytest>=8.3.5",
|
|
145
166
|
"qdarkstyle>=3.2.3",
|
|
146
167
|
"ruff>=0.14.11",
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Alarm Clock
|
|
2
|
+
|
|
3
|
+
A digital alarm clock application with a graphical user interface built using PySide2. Features include configurable alarms, digital time display, and visual notifications.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Digital clock display with animated border effects
|
|
8
|
+
- Configurable alarm time setting
|
|
9
|
+
- Delay options (1, 5, 10, 15, 30, 60 minutes)
|
|
10
|
+
- Repeat alarm functionality
|
|
11
|
+
- Visual notification with blinking effect when alarm triggers
|
|
12
|
+
- Dark theme interface
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
Install the required dependencies:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install -e .
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
Run the alarm clock application:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
alarmclk
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or directly with Python:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
python -m sfi.alarmclock
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Command Line Options
|
|
37
|
+
|
|
38
|
+
- `-d, --debug`: Enable debug mode
|
|
39
|
+
- `-v, --version`: Show version information
|
|
40
|
+
|
|
41
|
+
## Configuration
|
|
42
|
+
|
|
43
|
+
The application uses the following configuration parameters:
|
|
44
|
+
|
|
45
|
+
- **Digital Clock**: Displays current time with animated border colors
|
|
46
|
+
- **Alarm Time**: Set specific time for the alarm to trigger
|
|
47
|
+
- **Delay Options**: Quick buttons to set alarms for specific intervals
|
|
48
|
+
- **Repeat Option**: Enable/disable alarm repetition
|
|
49
|
+
- **Visual Notifications**: Alert dialog with blinking effect
|
|
50
|
+
|
|
51
|
+
## Architecture
|
|
52
|
+
|
|
53
|
+
The application follows a modular design:
|
|
54
|
+
|
|
55
|
+
- `AlarmClockConfig`: Configuration dataclass with all application settings
|
|
56
|
+
- `DigitalClock`: Widget displaying current time with animation
|
|
57
|
+
- `BlinkDialog`: Notification dialog with visual effects
|
|
58
|
+
- `AlarmClock`: Main application window with GUI controls
|
|
59
|
+
|
|
60
|
+
## Dependencies
|
|
61
|
+
|
|
62
|
+
- PySide2: GUI framework
|
|
63
|
+
- qdarkstyle: Dark theme styling
|
|
64
|
+
- typing-extensions: Type hinting support for older Python versions
|
|
@@ -4,9 +4,9 @@ import argparse
|
|
|
4
4
|
import logging
|
|
5
5
|
import random
|
|
6
6
|
import sys
|
|
7
|
+
from dataclasses import dataclass
|
|
7
8
|
from datetime import datetime, timedelta, timezone
|
|
8
9
|
from functools import partial
|
|
9
|
-
from typing import ClassVar
|
|
10
10
|
|
|
11
11
|
import qdarkstyle
|
|
12
12
|
from PySide2.QtCore import QSize, Qt, QTime, QTimer
|
|
@@ -24,42 +24,42 @@ from PySide2.QtWidgets import (
|
|
|
24
24
|
QWidget,
|
|
25
25
|
)
|
|
26
26
|
|
|
27
|
-
__version__ = "0.1.
|
|
28
|
-
__build_date__ = "
|
|
27
|
+
__version__ = "0.1.3"
|
|
28
|
+
__build_date__ = "2026-01-22"
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
@dataclass(frozen=True)
|
|
31
32
|
class AlarmClockConfig:
|
|
32
|
-
"""
|
|
33
|
-
|
|
34
|
-
ALARM_CLOCK_TITLE = "Digital Alarm Clock"
|
|
33
|
+
"""Configuration for the alarm clock application."""
|
|
35
34
|
|
|
35
|
+
ALARM_CLOCK_TITLE: str = "Digital Alarm Clock"
|
|
36
36
|
DIGITAL_FONT: str = "bold italic 81px 'Consolas'"
|
|
37
37
|
DIGITAL_COLOR: str = "#ccee00"
|
|
38
|
-
DIGITAL_BORDER_COLORS:
|
|
38
|
+
DIGITAL_BORDER_COLORS: tuple[str, ...] = (
|
|
39
39
|
"#00aa00",
|
|
40
40
|
"#eecc00",
|
|
41
41
|
"#aa00aa",
|
|
42
42
|
"#c0e0b0",
|
|
43
|
-
|
|
43
|
+
)
|
|
44
44
|
DIGITAL_TIMER_FORMAT: str = "%H:%M:%S"
|
|
45
45
|
DIGITAL_UPDATE_INTERVAL: int = 1000
|
|
46
46
|
|
|
47
47
|
BLINK_TITLE: str = "Alarm Reminder!"
|
|
48
48
|
BLINK_CONTENT: str = "⏰ Time's Up!"
|
|
49
49
|
BLINK_TYPE: str = "color" # Options: 'color' or 'opacity'
|
|
50
|
-
BLINK_BG_COLORS:
|
|
50
|
+
BLINK_BG_COLORS: tuple[str, ...] = (
|
|
51
51
|
"#baf1ba",
|
|
52
52
|
"#f8ccc3",
|
|
53
53
|
"#aab4f0",
|
|
54
54
|
"#efaec0",
|
|
55
|
-
|
|
56
|
-
BLINK_INTERVAL:
|
|
57
|
-
DELAY_STEPS:
|
|
55
|
+
)
|
|
56
|
+
BLINK_INTERVAL: int = 300 # ms
|
|
57
|
+
DELAY_STEPS: tuple[int, ...] = (1, 5, 10, 15, 30, 60) # minutes
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
config = AlarmClockConfig()
|
|
63
63
|
logger = logging.getLogger(__name__)
|
|
64
64
|
|
|
65
65
|
|
|
@@ -69,30 +69,30 @@ class DigitalClock(QLabel):
|
|
|
69
69
|
def __init__(self, parent: QWidget | None = None) -> None:
|
|
70
70
|
super().__init__(parent)
|
|
71
71
|
|
|
72
|
-
self.setAlignment(Qt.AlignCenter)
|
|
72
|
+
self.setAlignment(Qt.AlignCenter)
|
|
73
73
|
|
|
74
|
-
self._color =
|
|
74
|
+
self._color = config.DIGITAL_BORDER_COLORS[0]
|
|
75
75
|
|
|
76
76
|
# Timer to update current time
|
|
77
77
|
self._timer = QTimer()
|
|
78
|
-
self._timer.timeout.connect(self.update_time)
|
|
79
|
-
self._timer.start(
|
|
78
|
+
self._timer.timeout.connect(self.update_time)
|
|
79
|
+
self._timer.start(config.DIGITAL_UPDATE_INTERVAL) # Update every second
|
|
80
80
|
|
|
81
81
|
self.update_time()
|
|
82
82
|
|
|
83
83
|
def update_time(self) -> None:
|
|
84
84
|
"""Update current time display."""
|
|
85
85
|
current = datetime.now(timezone.utc) + timedelta(hours=8) # Beijing time
|
|
86
|
-
self.setText(current.strftime(
|
|
86
|
+
self.setText(current.strftime(config.DIGITAL_TIMER_FORMAT))
|
|
87
87
|
logger.debug(f"Updated time: {current}")
|
|
88
88
|
|
|
89
89
|
# Add blink effect
|
|
90
90
|
self._color = random.choice(
|
|
91
|
-
[_ for _ in
|
|
91
|
+
[_ for _ in config.DIGITAL_BORDER_COLORS if _ != self._color],
|
|
92
92
|
)
|
|
93
93
|
self.setStyleSheet(f"""
|
|
94
|
-
font: {
|
|
95
|
-
color: {
|
|
94
|
+
font: {config.DIGITAL_FONT};
|
|
95
|
+
color: {config.DIGITAL_COLOR};
|
|
96
96
|
background-color: black;
|
|
97
97
|
border: 2px dashed {self._color};
|
|
98
98
|
border-radius: 10px;
|
|
@@ -106,47 +106,47 @@ class BlinkDialog(QDialog):
|
|
|
106
106
|
def __init__(self) -> None:
|
|
107
107
|
super().__init__()
|
|
108
108
|
|
|
109
|
-
self.setWindowTitle(
|
|
109
|
+
self.setWindowTitle(config.BLINK_TITLE)
|
|
110
110
|
self.setModal(True)
|
|
111
111
|
self.setWindowFlags(
|
|
112
|
-
self.windowFlags() | Qt.WindowStaysOnTopHint | Qt.WindowType.Dialog,
|
|
112
|
+
self.windowFlags() | Qt.WindowStaysOnTopHint | Qt.WindowType.Dialog,
|
|
113
113
|
)
|
|
114
114
|
self.setFixedSize(QSize(400, 240))
|
|
115
115
|
|
|
116
116
|
layout = QVBoxLayout()
|
|
117
|
-
msg_label = QLabel(
|
|
117
|
+
msg_label = QLabel(config.BLINK_CONTENT)
|
|
118
118
|
msg_label.setStyleSheet("""
|
|
119
119
|
color: red;
|
|
120
120
|
font-size: 24px;
|
|
121
121
|
""")
|
|
122
|
-
msg_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
122
|
+
msg_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
123
123
|
|
|
124
124
|
close_button = QPushButton("Close Alarm")
|
|
125
|
-
close_button.clicked.connect(self.accept)
|
|
125
|
+
close_button.clicked.connect(self.accept)
|
|
126
126
|
|
|
127
127
|
layout.addWidget(msg_label)
|
|
128
128
|
layout.addWidget(close_button)
|
|
129
129
|
self.setLayout(layout)
|
|
130
130
|
|
|
131
131
|
# Prevent user from closing dialog by other means, ensure button click only
|
|
132
|
-
self.setWindowFlag(Qt.WindowCloseButtonHint, False)
|
|
132
|
+
self.setWindowFlag(Qt.WindowCloseButtonHint, False)
|
|
133
133
|
|
|
134
134
|
# Blink control variables and timer
|
|
135
135
|
self.blink_timer = QTimer(self)
|
|
136
|
-
self.blink_timer.timeout.connect(self.update_blink)
|
|
136
|
+
self.blink_timer.timeout.connect(self.update_blink)
|
|
137
137
|
self.blink_state = False
|
|
138
|
-
self.blink_type =
|
|
138
|
+
self.blink_type = config.BLINK_TYPE
|
|
139
139
|
|
|
140
140
|
# Initialize style
|
|
141
|
-
self.bg_color = random.choice(
|
|
141
|
+
self.bg_color = random.choice(config.BLINK_BG_COLORS)
|
|
142
142
|
self.origin_style = self.styleSheet()
|
|
143
|
-
self.blink_timer.start(
|
|
143
|
+
self.blink_timer.start(config.BLINK_INTERVAL)
|
|
144
144
|
|
|
145
145
|
def update_blink(self) -> None:
|
|
146
146
|
"""Timer timeout, update blink state."""
|
|
147
147
|
if self.blink_type == "color":
|
|
148
148
|
# Color blink logic
|
|
149
|
-
colors = [_ for _ in
|
|
149
|
+
colors = [_ for _ in config.BLINK_BG_COLORS[:] if _ != self.bg_color]
|
|
150
150
|
self.setStyleSheet(f"background-color: {random.choice(colors)}")
|
|
151
151
|
elif self.blink_type == "opacity":
|
|
152
152
|
# Opacity blink logic - Note: Some systems may not fully support window opacity
|
|
@@ -172,7 +172,7 @@ class AlarmClock(QMainWindow):
|
|
|
172
172
|
|
|
173
173
|
def __init__(self) -> None:
|
|
174
174
|
super().__init__()
|
|
175
|
-
self.setWindowTitle(f"{
|
|
175
|
+
self.setWindowTitle(f"{config.ALARM_CLOCK_TITLE} v{__version__}")
|
|
176
176
|
self.setGeometry(
|
|
177
177
|
QApplication.desktop().screenGeometry().center().x() - self.width() // 4,
|
|
178
178
|
QApplication.desktop().screenGeometry().center().y() - self.height() // 2,
|
|
@@ -238,7 +238,7 @@ class AlarmClock(QMainWindow):
|
|
|
238
238
|
self.alarm_time_edit = QTimeEdit()
|
|
239
239
|
self.alarm_time_edit.setDisplayFormat("HH:mm:ss")
|
|
240
240
|
self.alarm_time_edit.setTime(
|
|
241
|
-
QTime.currentTime().addSecs(
|
|
241
|
+
QTime.currentTime().addSecs(config.DELAY_STEPS[0] * 60),
|
|
242
242
|
)
|
|
243
243
|
|
|
244
244
|
time_layout.addWidget(time_label)
|
|
@@ -249,10 +249,10 @@ class AlarmClock(QMainWindow):
|
|
|
249
249
|
delay_label = QLabel("Delay (min):")
|
|
250
250
|
delay_label.setStyleSheet("color: white; font-size: 16px;")
|
|
251
251
|
delay_layout.addWidget(delay_label)
|
|
252
|
-
for minutes in
|
|
252
|
+
for minutes in config.DELAY_STEPS:
|
|
253
253
|
button = QPushButton(str(minutes))
|
|
254
254
|
button.setStyleSheet("color: white; font-size: 16px;")
|
|
255
|
-
button.clicked.connect(partial(self.set_delay, minutes))
|
|
255
|
+
button.clicked.connect(partial(self.set_delay, minutes))
|
|
256
256
|
delay_layout.addWidget(button)
|
|
257
257
|
main_layout.addLayout(delay_layout)
|
|
258
258
|
|
|
@@ -263,9 +263,9 @@ class AlarmClock(QMainWindow):
|
|
|
263
263
|
# Control buttons
|
|
264
264
|
button_layout = QHBoxLayout()
|
|
265
265
|
self.set_alarm_button = QPushButton("Set Alarm")
|
|
266
|
-
self.set_alarm_button.clicked.connect(self.set_alarm)
|
|
266
|
+
self.set_alarm_button.clicked.connect(self.set_alarm)
|
|
267
267
|
self.cancel_alarm_button = QPushButton("Cancel Alarm")
|
|
268
|
-
self.cancel_alarm_button.clicked.connect(self.cancel_alarm)
|
|
268
|
+
self.cancel_alarm_button.clicked.connect(self.cancel_alarm)
|
|
269
269
|
self.cancel_alarm_button.setEnabled(False)
|
|
270
270
|
button_layout.addWidget(self.set_alarm_button)
|
|
271
271
|
button_layout.addWidget(self.cancel_alarm_button)
|
|
@@ -273,13 +273,13 @@ class AlarmClock(QMainWindow):
|
|
|
273
273
|
|
|
274
274
|
# Status display
|
|
275
275
|
self.status_label = QLabel("Alarm not set")
|
|
276
|
-
self.status_label.setAlignment(Qt.AlignCenter)
|
|
276
|
+
self.status_label.setAlignment(Qt.AlignCenter)
|
|
277
277
|
self.status_label.setStyleSheet("color: #aaaaaa; font-size: 16px;")
|
|
278
278
|
main_layout.addWidget(self.status_label)
|
|
279
279
|
|
|
280
280
|
# Alarm timer
|
|
281
281
|
self.alarm_timer = QTimer()
|
|
282
|
-
self.alarm_timer.timeout.connect(self.check_alarm)
|
|
282
|
+
self.alarm_timer.timeout.connect(self.check_alarm)
|
|
283
283
|
|
|
284
284
|
# Alarm state
|
|
285
285
|
self.alarm_set = False
|