pysfi 0.1.10__tar.gz → 0.1.12__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 (117) hide show
  1. {pysfi-0.1.10 → pysfi-0.1.12}/.gitignore +1 -1
  2. {pysfi-0.1.10 → pysfi-0.1.12}/PKG-INFO +9 -7
  3. {pysfi-0.1.10 → pysfi-0.1.12}/README.md +6 -6
  4. {pysfi-0.1.10 → pysfi-0.1.12}/pyproject.toml +30 -6
  5. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/__init__.py +1 -1
  6. pysfi-0.1.12/sfi/alarmclock/README.md +64 -0
  7. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/alarmclock/alarmclock.py +40 -40
  8. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/bumpversion/README.md +218 -218
  9. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/bumpversion/__init__.py +1 -1
  10. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/bumpversion/tests/test_bumpversion.py +698 -698
  11. pysfi-0.1.12/sfi/cleanbuild/cleanbuild.py +155 -0
  12. pysfi-0.1.12/sfi/condasetup/condasetup.py +116 -0
  13. pysfi-0.1.12/sfi/docdiff/README.md +108 -0
  14. pysfi-0.1.12/sfi/docdiff/docdiff.py +238 -0
  15. pysfi-0.1.12/sfi/docdiff/tests/test_benchmark.py +98 -0
  16. pysfi-0.1.12/sfi/docdiff/tests/test_docdiff.py +188 -0
  17. pysfi-0.1.12/sfi/docdiff/tests/test_enhanced.py +150 -0
  18. pysfi-0.1.12/sfi/docdiff/tests/test_functional.py +186 -0
  19. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/docscan/README.md +813 -813
  20. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/docscan/__init__.py +1 -1
  21. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/docscan/docscan_gui.py +1 -1
  22. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/docscan/lang/eng.py +152 -152
  23. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/docscan/lang/zhcn.py +170 -170
  24. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/docscan/tests/__init__.py +1 -1
  25. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/docscan/tests/test_benchmark.py +416 -427
  26. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/docscan/tests/test_docscan.py +1245 -1245
  27. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/docscan/tests/test_enhanced.py +279 -267
  28. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/filedate/README.md +45 -45
  29. pysfi-0.1.12/sfi/filedate/filedate.py +185 -0
  30. pysfi-0.1.12/sfi/filedate/tests/test_filedate.py +830 -0
  31. pysfi-0.1.12/sfi/gittool/__init__.py +2 -0
  32. pysfi-0.1.12/sfi/gittool/gittool.py +401 -0
  33. pysfi-0.1.12/sfi/llmclient/llmclient.py +592 -0
  34. pysfi-0.1.12/sfi/llmclient/tests/__init__.py +1 -0
  35. pysfi-0.1.12/sfi/llmclient/tests/test_benchmark.py +399 -0
  36. pysfi-0.1.12/sfi/llmclient/tests/test_llmclient.py +636 -0
  37. pysfi-0.1.12/sfi/llmquantize/llmquantize.py +480 -0
  38. pysfi-0.1.12/sfi/llmquantize/tests/__init__.py +1 -0
  39. pysfi-0.1.12/sfi/llmquantize/tests/test_llmquantize.py +518 -0
  40. pysfi-0.1.12/sfi/llmserver/llmserver.py +335 -0
  41. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/makepython/makepython.py +2 -2
  42. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/pdfsplit/pdfsplit.py +4 -4
  43. pysfi-0.1.12/sfi/pyarchive/pyarchive.py +418 -0
  44. pysfi-0.1.12/sfi/pyembedinstall/pyembedinstall.py +629 -0
  45. pysfi-0.1.12/sfi/pyembedinstall/tests/test_pyembedinstall.py +1626 -0
  46. pysfi-0.1.12/sfi/pylibpack/__init__.py +0 -0
  47. pysfi-0.1.12/sfi/pylibpack/pylibpack.py +1457 -0
  48. pysfi-0.1.12/sfi/pylibpack/tests/__init__.py +0 -0
  49. pysfi-0.1.12/sfi/pylibpack/tests/test_benchmark.py +653 -0
  50. pysfi-0.1.12/sfi/pylibpack/tests/test_pylibpack.py +774 -0
  51. pysfi-0.1.12/sfi/pyloadergen/__init__.py +0 -0
  52. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/pyloadergen/pyloadergen.py +271 -572
  53. pysfi-0.1.12/sfi/pyloadergen/tests/__init__.py +0 -0
  54. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/pyloadergen/tests/test_benchmark.py +235 -235
  55. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/pyloadergen/tests/test_pyloadergen.py +10 -154
  56. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/pypack/README.md +8 -8
  57. pysfi-0.1.12/sfi/pypack/__init__.py +0 -0
  58. pysfi-0.1.12/sfi/pypack/pypack.py +1142 -0
  59. pysfi-0.1.12/sfi/pypack/tests/__init__.py +0 -0
  60. pysfi-0.1.12/sfi/pypack/tests/test_pack.py +124 -0
  61. pysfi-0.1.12/sfi/pyprojectparse/README.md +104 -0
  62. pysfi-0.1.12/sfi/pyprojectparse/__init__.py +0 -0
  63. pysfi-0.1.12/sfi/pyprojectparse/pyprojectparse.py +500 -0
  64. pysfi-0.1.12/sfi/pyprojectparse/tests/test_benchmark.py +817 -0
  65. pysfi-0.1.12/sfi/pyprojectparse/tests/test_projectparse.py +604 -0
  66. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/pysourcepack/README.md +201 -201
  67. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/pysourcepack/pysourcepack.py +308 -369
  68. pysfi-0.1.12/sfi/quizbase/README.md +397 -0
  69. pysfi-0.1.12/sfi/quizbase/__init__.py +0 -0
  70. pysfi-0.1.12/sfi/quizbase/quizbase.py +828 -0
  71. pysfi-0.1.12/sfi/quizbase/quizbase_gui.py +987 -0
  72. pysfi-0.1.12/sfi/quizbase/tests/__init__.py +1 -0
  73. pysfi-0.1.12/sfi/quizbase/tests/test_quizbase.py +590 -0
  74. pysfi-0.1.12/sfi/quizbase/tests/test_quizbase_gui.py +371 -0
  75. pysfi-0.1.12/sfi/regexvalidate/README.md +60 -0
  76. pysfi-0.1.12/sfi/regexvalidate/__init__.py +0 -0
  77. pysfi-0.1.12/sfi/regexvalidate/regexvalidate.py +468 -0
  78. pysfi-0.1.12/sfi/taskkill/__init__.py +0 -0
  79. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/taskkill/taskkill.py +0 -2
  80. pysfi-0.1.12/sfi/which/__init__.py +0 -0
  81. pysfi-0.1.12/sfi/workflowengine/README.md +0 -0
  82. pysfi-0.1.12/sfi/workflowengine/__init__.py +0 -0
  83. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/workflowengine/tests/__init__.py +1 -1
  84. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/workflowengine/tests/test_benchmark.py +725 -725
  85. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/workflowengine/tests/test_workflowengine.py +740 -740
  86. pysfi-0.1.10/sfi/embedinstall/embedinstall.py +0 -478
  87. pysfi-0.1.10/sfi/filedate/filedate.py +0 -112
  88. pysfi-0.1.10/sfi/projectparse/projectparse.py +0 -152
  89. pysfi-0.1.10/sfi/pylibpack/pylibpack.py +0 -913
  90. pysfi-0.1.10/sfi/pypack/pypack.py +0 -791
  91. pysfi-0.1.10/sfi/pypack/tests/test_pack.py +0 -257
  92. {pysfi-0.1.10 → pysfi-0.1.12}/examples/pack_demo/README.md +0 -0
  93. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/alarmclock/__init__.py +0 -0
  94. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/bumpversion/bumpversion.py +0 -0
  95. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/bumpversion/tests/__init__.py +0 -0
  96. {pysfi-0.1.10/sfi/workflowengine → pysfi-0.1.12/sfi/cleanbuild}/README.md +0 -0
  97. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/cli.py +0 -0
  98. /pysfi-0.1.10/sfi/docscan/lang/__init__.py → /pysfi-0.1.12/sfi/condasetup/README.md +0 -0
  99. {pysfi-0.1.10/sfi/embedinstall → pysfi-0.1.12/sfi/docdiff/tests}/__init__.py +0 -0
  100. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/docscan/docscan.py +0 -0
  101. {pysfi-0.1.10/sfi/filedate → pysfi-0.1.12/sfi/docscan/lang}/__init__.py +0 -0
  102. {pysfi-0.1.10/sfi/makepython → pysfi-0.1.12/sfi/filedate}/__init__.py +0 -0
  103. /pysfi-0.1.10/sfi/pdfsplit/__init__.py → /pysfi-0.1.12/sfi/gittool/README.md +0 -0
  104. /pysfi-0.1.10/sfi/pdfsplit/tests/__init__.py → /pysfi-0.1.12/sfi/llmclient/README.md +0 -0
  105. {pysfi-0.1.10/sfi/projectparse → pysfi-0.1.12/sfi/llmclient}/__init__.py +0 -0
  106. /pysfi-0.1.10/sfi/pylibpack/__init__.py → /pysfi-0.1.12/sfi/llmquantize/README.md +0 -0
  107. {pysfi-0.1.10/sfi/pyloadergen → pysfi-0.1.12/sfi/llmquantize}/__init__.py +0 -0
  108. /pysfi-0.1.10/sfi/pyloadergen/tests/__init__.py → /pysfi-0.1.12/sfi/llmserver/README.md +0 -0
  109. {pysfi-0.1.10/sfi/pypack → pysfi-0.1.12/sfi/makepython}/__init__.py +0 -0
  110. {pysfi-0.1.10/sfi/pypack/tests → pysfi-0.1.12/sfi/pdfsplit}/__init__.py +0 -0
  111. {pysfi-0.1.10/sfi/taskkill → pysfi-0.1.12/sfi/pdfsplit/tests}/__init__.py +0 -0
  112. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/pdfsplit/tests/test_pdfsplit.py +0 -0
  113. /pysfi-0.1.10/sfi/which/__init__.py → /pysfi-0.1.12/sfi/pyarchive/README.md +0 -0
  114. {pysfi-0.1.10/sfi/workflowengine → pysfi-0.1.12/sfi/pyembedinstall}/__init__.py +0 -0
  115. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/pylibpack/README.md +0 -0
  116. {pysfi-0.1.10 → pysfi-0.1.12}/sfi/which/which.py +0 -0
  117. {pysfi-0.1.10 → pysfi-0.1.12}/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.12
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'
@@ -17,6 +17,7 @@ Requires-Dist: pyside2>=5.15.2.1; extra == 'all'
17
17
  Requires-Dist: pytesseract>=0.3.10; extra == 'all'
18
18
  Requires-Dist: python-docx>=1.1.0; extra == 'all'
19
19
  Requires-Dist: python-pptx>=0.6.21; extra == 'all'
20
+ Requires-Dist: pywin32>=311; (sys_platform == 'win32') and extra == 'all'
20
21
  Provides-Extra: extra
21
22
  Requires-Dist: ebooklib>=0.18; extra == 'extra'
22
23
  Requires-Dist: markdown>=3.5; extra == 'extra'
@@ -33,6 +34,7 @@ Requires-Dist: openpyxl>=3.1.0; extra == 'office'
33
34
  Requires-Dist: pymupdf>=1.24.11; extra == 'office'
34
35
  Requires-Dist: python-docx>=1.1.0; extra == 'office'
35
36
  Requires-Dist: python-pptx>=0.6.21; extra == 'office'
37
+ Requires-Dist: pywin32>=311; (sys_platform == 'win32') and extra == 'office'
36
38
  Description-Content-Type: text/markdown
37
39
 
38
40
  # pysfi
@@ -47,10 +49,10 @@ pysfi is a Python project that provides single-file command-line utilities, desi
47
49
 
48
50
  - **alarmclk**: Alarm clock functionality
49
51
  - **[bumpversion](sfi/bumpversion/README.md)**: Automated version number management tool
50
- - **embedinstall**: Embed installation utilities
52
+ - **pyembedinstall**: Embed installation utilities
51
53
  - **[filedate](sfi/filedate/README.md)**: A file date management tool that normalizes date prefixes in filenames
52
54
  - **mkp**: Make Python project utilities
53
- - **projectparse**: Project parsing and analysis tools
55
+ - **pyprojectparse**: Project parsing and analysis tools
54
56
  - **pyloadergen**: Python loader code generation
55
57
  - **pypack**: Python packaging utilities
56
58
 
@@ -101,8 +103,8 @@ pysfi/
101
103
  │ ├── alarmclock.py
102
104
  │ ├── pyproject.toml
103
105
  │ └── __init__.py
104
- ├── embedinstall/ # embedinstall command module
105
- │ ├── embedinstall.py
106
+ ├── pyembedinstall/ # pyembedinstall command module
107
+ │ ├── pyembedinstall.py
106
108
  │ ├── pyproject.toml
107
109
  │ └── __init__.py
108
110
  ├── filedate/ # filedate command module
@@ -114,8 +116,8 @@ pysfi/
114
116
  │ ├── makepython.py
115
117
  │ ├── pyproject.toml
116
118
  │ └── __init__.py
117
- ├── projectparse/ # projectparse command module
118
- │ ├── projectparse.py
119
+ ├── pyprojectparse/ # pyprojectparse command module
120
+ │ ├── pyprojectparse.py
119
121
  │ ├── pyproject.toml
120
122
  │ └── __init__.py
121
123
  ├── 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,34 @@ 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.12"
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"
18
+ docdiff = "sfi.docdiff.docdiff:main"
16
19
  docscan = "sfi.docscan.docscan:main"
17
20
  docscan-gui = "sfi.docscan.docscan_gui:main"
18
- embedinstall = "sfi.embedinstall.embedinstall:main"
19
21
  filedate = "sfi.filedate.filedate:main"
22
+ gitt = "sfi.gittool.gittool:main"
23
+ llmcli = "sfi.llmclient.llmclient:main"
24
+ llmqnt = "sfi.llmquantize.llmquantize:main"
25
+ llmsvr = "sfi.llmserver.llmserver:main"
20
26
  mkp = "sfi.makepython.makepython:main"
21
27
  pdfsplit = "sfi.pdfsplit.pdfsplit:main"
22
- projectparse = "sfi.projectparse.projectparse:main"
28
+ pyarchive = "sfi.pyarchive.pyarchive:main"
29
+ pyembedinstall = "sfi.pyembedinstall.pyembedinstall:main"
23
30
  pylibpack = "sfi.pylibpack.pylibpack:main"
24
31
  pyloadergen = "sfi.pyloadergen.pyloadergen:main"
25
32
  pyp = "sfi.pypack.pypack:main"
26
33
  pypack = "sfi.pypack.pypack:main"
34
+ pyprojectparse = "sfi.pyprojectparse.pyprojectparse:main"
27
35
  pysourcepack = "sfi.pysourcepack.pysourcepack:main"
36
+ quizbase = "sfi.quizbase.quizbase:main"
37
+ quizbase-gui = "sfi.quizbase.quizbase_gui:main"
38
+ regval = "sfi.regexvalidate.regexvalidate:main"
28
39
  sfi = "sfi.cli:main"
29
40
  taskk = "sfi.taskkill.taskkill:main"
30
41
  wch = "sfi.which.which:main"
@@ -39,6 +50,7 @@ office = [
39
50
  "pymupdf>=1.24.11",
40
51
  "python-docx>=1.1.0",
41
52
  "python-pptx>=0.6.21",
53
+ "pywin32>=311; sys.platform=='win32'",
42
54
  ]
43
55
 
44
56
  [tool.hatch.build.targets.wheel]
@@ -55,7 +67,7 @@ packages = ["sfi"]
55
67
  include = ["README.md", "sfi/**/*.py", "sfi/pyproject.toml"]
56
68
 
57
69
  [tool.ruff]
58
- line-length = 120
70
+ line-length = 88
59
71
  target-version = "py38"
60
72
 
61
73
  # Exclude files and directories
@@ -117,16 +129,26 @@ known-first-party = ["sfi"]
117
129
  members = [
118
130
  "sfi/alarmclock",
119
131
  "sfi/bumpversion",
132
+ "sfi/cleanbuild",
133
+ "sfi/condasetup",
134
+ "sfi/docdiff",
120
135
  "sfi/docscan",
121
- "sfi/embedinstall",
122
136
  "sfi/filedate",
137
+ "sfi/gittool",
138
+ "sfi/llmclient",
139
+ "sfi/llmquantize",
140
+ "sfi/llmserver",
123
141
  "sfi/makepython",
124
142
  "sfi/pdfsplit",
125
- "sfi/projectparse",
143
+ "sfi/pyarchive",
144
+ "sfi/pyembedinstall",
126
145
  "sfi/pylibpack",
127
146
  "sfi/pyloadergen",
128
147
  "sfi/pypack",
148
+ "sfi/pyprojectparse",
129
149
  "sfi/pysourcepack",
150
+ "sfi/quizbase",
151
+ "sfi/regexvalidate",
130
152
  "sfi/taskkill",
131
153
  "sfi/which",
132
154
  "sfi/workflowengine",
@@ -135,12 +157,14 @@ members = [
135
157
  [dependency-groups]
136
158
  dev = [
137
159
  "hatch>=1.14.2",
160
+ "hypothesis>=6.113.0",
138
161
  "pysfi[all]",
139
162
  "pyside2>=5.15.2.1",
140
163
  "pytest-asyncio>=0.24.0",
141
164
  "pytest-benchmark>=4.0.0",
142
165
  "pytest-cov>=5.0.0",
143
166
  "pytest-mock>=3.14.0",
167
+ "pytest-qt>=4.4.0",
144
168
  "pytest>=8.3.5",
145
169
  "qdarkstyle>=3.2.3",
146
170
  "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.12"
@@ -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