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.
Files changed (109) hide show
  1. {pysfi-0.1.10 → pysfi-0.1.11}/.gitignore +1 -1
  2. {pysfi-0.1.10 → pysfi-0.1.11}/PKG-INFO +7 -7
  3. {pysfi-0.1.10 → pysfi-0.1.11}/README.md +6 -6
  4. {pysfi-0.1.10 → pysfi-0.1.11}/pyproject.toml +27 -6
  5. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/__init__.py +1 -1
  6. pysfi-0.1.11/sfi/alarmclock/README.md +64 -0
  7. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/alarmclock/alarmclock.py +40 -40
  8. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/bumpversion/README.md +218 -218
  9. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/bumpversion/__init__.py +1 -1
  10. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/bumpversion/tests/test_bumpversion.py +698 -698
  11. pysfi-0.1.11/sfi/cleanbuild/cleanbuild.py +155 -0
  12. pysfi-0.1.11/sfi/condasetup/condasetup.py +116 -0
  13. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/README.md +813 -813
  14. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/__init__.py +1 -1
  15. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/docscan_gui.py +1 -1
  16. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/lang/eng.py +152 -152
  17. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/lang/zhcn.py +170 -170
  18. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/tests/__init__.py +1 -1
  19. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/tests/test_benchmark.py +416 -427
  20. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/tests/test_docscan.py +1245 -1245
  21. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/tests/test_enhanced.py +279 -267
  22. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/filedate/README.md +45 -45
  23. pysfi-0.1.11/sfi/filedate/filedate.py +185 -0
  24. pysfi-0.1.11/sfi/filedate/tests/test_filedate.py +830 -0
  25. pysfi-0.1.11/sfi/gittool/__init__.py +2 -0
  26. pysfi-0.1.11/sfi/gittool/gittool.py +401 -0
  27. pysfi-0.1.11/sfi/llmclient/llmclient.py +592 -0
  28. pysfi-0.1.11/sfi/llmclient/tests/__init__.py +1 -0
  29. pysfi-0.1.11/sfi/llmclient/tests/test_benchmark.py +399 -0
  30. pysfi-0.1.11/sfi/llmclient/tests/test_llmclient.py +636 -0
  31. pysfi-0.1.11/sfi/llmquantize/llmquantize.py +480 -0
  32. pysfi-0.1.11/sfi/llmquantize/tests/__init__.py +1 -0
  33. pysfi-0.1.11/sfi/llmquantize/tests/test_llmquantize.py +518 -0
  34. pysfi-0.1.11/sfi/llmserver/llmserver.py +335 -0
  35. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/makepython/makepython.py +2 -2
  36. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/pdfsplit/pdfsplit.py +4 -4
  37. pysfi-0.1.11/sfi/pyarchive/pyarchive.py +418 -0
  38. pysfi-0.1.11/sfi/pyembedinstall/pyembedinstall.py +629 -0
  39. pysfi-0.1.11/sfi/pyembedinstall/tests/test_pyembedinstall.py +1626 -0
  40. pysfi-0.1.11/sfi/pylibpack/pylibpack.py +1457 -0
  41. pysfi-0.1.11/sfi/pylibpack/tests/test_benchmark.py +653 -0
  42. pysfi-0.1.11/sfi/pylibpack/tests/test_pylibpack.py +774 -0
  43. pysfi-0.1.11/sfi/pyloadergen/__init__.py +0 -0
  44. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/pyloadergen/pyloadergen.py +271 -572
  45. pysfi-0.1.11/sfi/pyloadergen/tests/__init__.py +0 -0
  46. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/pyloadergen/tests/test_benchmark.py +235 -235
  47. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/pyloadergen/tests/test_pyloadergen.py +10 -154
  48. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/pypack/README.md +8 -8
  49. pysfi-0.1.11/sfi/pypack/__init__.py +0 -0
  50. pysfi-0.1.11/sfi/pypack/pypack.py +1142 -0
  51. pysfi-0.1.11/sfi/pypack/tests/__init__.py +0 -0
  52. pysfi-0.1.11/sfi/pypack/tests/test_pack.py +124 -0
  53. pysfi-0.1.11/sfi/pyprojectparse/README.md +104 -0
  54. pysfi-0.1.11/sfi/pyprojectparse/__init__.py +0 -0
  55. pysfi-0.1.11/sfi/pyprojectparse/pyprojectparse.py +500 -0
  56. pysfi-0.1.11/sfi/pyprojectparse/tests/test_benchmark.py +817 -0
  57. pysfi-0.1.11/sfi/pyprojectparse/tests/test_projectparse.py +604 -0
  58. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/pysourcepack/README.md +201 -201
  59. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/pysourcepack/pysourcepack.py +308 -369
  60. pysfi-0.1.11/sfi/quizbase/README.md +397 -0
  61. pysfi-0.1.11/sfi/quizbase/__init__.py +0 -0
  62. pysfi-0.1.11/sfi/quizbase/quizbase.py +828 -0
  63. pysfi-0.1.11/sfi/quizbase/quizbase_gui.py +987 -0
  64. pysfi-0.1.11/sfi/quizbase/tests/__init__.py +1 -0
  65. pysfi-0.1.11/sfi/quizbase/tests/test_quizbase.py +590 -0
  66. pysfi-0.1.11/sfi/quizbase/tests/test_quizbase_gui.py +371 -0
  67. pysfi-0.1.11/sfi/regexvalidate/README.md +60 -0
  68. pysfi-0.1.11/sfi/regexvalidate/__init__.py +0 -0
  69. pysfi-0.1.11/sfi/regexvalidate/regexvalidate.py +468 -0
  70. pysfi-0.1.11/sfi/taskkill/__init__.py +0 -0
  71. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/taskkill/taskkill.py +0 -2
  72. pysfi-0.1.11/sfi/which/__init__.py +0 -0
  73. pysfi-0.1.11/sfi/workflowengine/README.md +0 -0
  74. pysfi-0.1.11/sfi/workflowengine/__init__.py +0 -0
  75. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/workflowengine/tests/__init__.py +1 -1
  76. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/workflowengine/tests/test_benchmark.py +725 -725
  77. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/workflowengine/tests/test_workflowengine.py +740 -740
  78. pysfi-0.1.10/sfi/embedinstall/embedinstall.py +0 -478
  79. pysfi-0.1.10/sfi/filedate/filedate.py +0 -112
  80. pysfi-0.1.10/sfi/projectparse/projectparse.py +0 -152
  81. pysfi-0.1.10/sfi/pylibpack/pylibpack.py +0 -913
  82. pysfi-0.1.10/sfi/pypack/pypack.py +0 -791
  83. pysfi-0.1.10/sfi/pypack/tests/test_pack.py +0 -257
  84. {pysfi-0.1.10 → pysfi-0.1.11}/examples/pack_demo/README.md +0 -0
  85. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/alarmclock/__init__.py +0 -0
  86. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/bumpversion/bumpversion.py +0 -0
  87. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/bumpversion/tests/__init__.py +0 -0
  88. {pysfi-0.1.10/sfi/workflowengine → pysfi-0.1.11/sfi/cleanbuild}/README.md +0 -0
  89. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/cli.py +0 -0
  90. /pysfi-0.1.10/sfi/docscan/lang/__init__.py → /pysfi-0.1.11/sfi/condasetup/README.md +0 -0
  91. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/docscan/docscan.py +0 -0
  92. {pysfi-0.1.10/sfi/embedinstall → pysfi-0.1.11/sfi/docscan/lang}/__init__.py +0 -0
  93. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/filedate/__init__.py +0 -0
  94. /pysfi-0.1.10/sfi/makepython/__init__.py → /pysfi-0.1.11/sfi/gittool/README.md +0 -0
  95. /pysfi-0.1.10/sfi/pdfsplit/__init__.py → /pysfi-0.1.11/sfi/llmclient/README.md +0 -0
  96. /pysfi-0.1.10/sfi/pdfsplit/tests/__init__.py → /pysfi-0.1.11/sfi/llmquantize/README.md +0 -0
  97. {pysfi-0.1.10/sfi/projectparse → pysfi-0.1.11/sfi/llmquantize}/__init__.py +0 -0
  98. /pysfi-0.1.10/sfi/pylibpack/__init__.py → /pysfi-0.1.11/sfi/llmserver/README.md +0 -0
  99. {pysfi-0.1.10/sfi/pyloadergen → pysfi-0.1.11/sfi/makepython}/__init__.py +0 -0
  100. {pysfi-0.1.10/sfi/pyloadergen/tests → pysfi-0.1.11/sfi/pdfsplit}/__init__.py +0 -0
  101. {pysfi-0.1.10/sfi/pypack → pysfi-0.1.11/sfi/pdfsplit/tests}/__init__.py +0 -0
  102. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/pdfsplit/tests/test_pdfsplit.py +0 -0
  103. /pysfi-0.1.10/sfi/pypack/tests/__init__.py → /pysfi-0.1.11/sfi/pyarchive/README.md +0 -0
  104. {pysfi-0.1.10/sfi/taskkill → pysfi-0.1.11/sfi/pyembedinstall}/__init__.py +0 -0
  105. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/pylibpack/README.md +0 -0
  106. {pysfi-0.1.10/sfi/which → pysfi-0.1.11/sfi/pylibpack}/__init__.py +0 -0
  107. {pysfi-0.1.10/sfi/workflowengine → pysfi-0.1.11/sfi/pylibpack/tests}/__init__.py +0 -0
  108. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/which/which.py +0 -0
  109. {pysfi-0.1.10 → pysfi-0.1.11}/sfi/workflowengine/workflowengine.py +0 -0
@@ -16,4 +16,4 @@ runtime
16
16
  .coverage
17
17
  pyloader.exe
18
18
  *.c
19
- .python-version
19
+ .hypothesis
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pysfi
3
- Version: 0.1.10
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
- - **embedinstall**: Embed installation utilities
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
- - **projectparse**: Project parsing and analysis tools
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
- ├── embedinstall/ # embedinstall command module
105
- │ ├── embedinstall.py
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
- ├── projectparse/ # projectparse command module
118
- │ ├── projectparse.py
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
- - **embedinstall**: Embed installation utilities
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
- - **projectparse**: Project parsing and analysis tools
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
- ├── embedinstall/ # embedinstall command module
68
- │ ├── embedinstall.py
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
- ├── projectparse/ # projectparse command module
81
- │ ├── projectparse.py
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.10"
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
- projectparse = "sfi.projectparse.projectparse:main"
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 = 120
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/projectparse",
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",
@@ -1,3 +1,3 @@
1
1
  """Single File commands for Interactive python."""
2
2
 
3
- __version__ = "0.1.10"
3
+ __version__ = "0.1.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.2"
28
- __build_date__ = "2025-09-16"
27
+ __version__ = "0.1.3"
28
+ __build_date__ = "2026-01-22"
29
29
 
30
30
 
31
+ @dataclass(frozen=True)
31
32
  class AlarmClockConfig:
32
- """Alarm clock configuration."""
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: ClassVar[list[str]] = [
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: ClassVar[list[str]] = [
50
+ BLINK_BG_COLORS: tuple[str, ...] = (
51
51
  "#baf1ba",
52
52
  "#f8ccc3",
53
53
  "#aab4f0",
54
54
  "#efaec0",
55
- ]
56
- BLINK_INTERVAL: ClassVar[int] = 300 # ms
57
- DELAY_STEPS: ClassVar[list[int]] = [1, 5, 10, 15, 30, 60] # minutes
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
- conf = AlarmClockConfig()
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) # type: ignore
72
+ self.setAlignment(Qt.AlignCenter)
73
73
 
74
- self._color = conf.DIGITAL_BORDER_COLORS[0]
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) # type: ignore
79
- self._timer.start(conf.DIGITAL_UPDATE_INTERVAL) # Update every second
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(conf.DIGITAL_TIMER_FORMAT))
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 conf.DIGITAL_BORDER_COLORS if _ != self._color],
91
+ [_ for _ in config.DIGITAL_BORDER_COLORS if _ != self._color],
92
92
  )
93
93
  self.setStyleSheet(f"""
94
- font: {conf.DIGITAL_FONT};
95
- color: {conf.DIGITAL_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(conf.BLINK_TITLE)
109
+ self.setWindowTitle(config.BLINK_TITLE)
110
110
  self.setModal(True)
111
111
  self.setWindowFlags(
112
- self.windowFlags() | Qt.WindowStaysOnTopHint | Qt.WindowType.Dialog, # type: ignore
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(conf.BLINK_CONTENT)
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) # type: ignore
122
+ msg_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
123
123
 
124
124
  close_button = QPushButton("Close Alarm")
125
- close_button.clicked.connect(self.accept) # type: ignore
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) # type: ignore
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) # type: ignore
136
+ self.blink_timer.timeout.connect(self.update_blink)
137
137
  self.blink_state = False
138
- self.blink_type = conf.BLINK_TYPE
138
+ self.blink_type = config.BLINK_TYPE
139
139
 
140
140
  # Initialize style
141
- self.bg_color = random.choice(conf.BLINK_BG_COLORS)
141
+ self.bg_color = random.choice(config.BLINK_BG_COLORS)
142
142
  self.origin_style = self.styleSheet()
143
- self.blink_timer.start(conf.BLINK_INTERVAL)
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 conf.BLINK_BG_COLORS[:] if _ != self.bg_color]
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"{conf.ALARM_CLOCK_TITLE} v{__version__}")
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(conf.DELAY_STEPS[0] * 60),
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 conf.DELAY_STEPS:
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)) # type: ignore
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) # type: ignore
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) # type: ignore
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) # type: ignore
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) # type: ignore
282
+ self.alarm_timer.timeout.connect(self.check_alarm)
283
283
 
284
284
  # Alarm state
285
285
  self.alarm_set = False