pysfi 0.1.7__py3-none-any.whl

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.
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.4
2
+ Name: pysfi
3
+ Version: 0.1.7
4
+ Summary: Single File commands for Interactive python.
5
+ Requires-Python: >=3.8
6
+ Requires-Dist: tomli>=2.4.0; python_version < '3.11'
7
+ Provides-Extra: all
8
+ Requires-Dist: ebooklib>=0.18; extra == 'all'
9
+ Requires-Dist: markdown>=3.5; extra == 'all'
10
+ Requires-Dist: odfpy>=1.4.1; extra == 'all'
11
+ Requires-Dist: openpyxl>=3.1.0; extra == 'all'
12
+ Requires-Dist: pillow>=10.0.0; extra == 'all'
13
+ Requires-Dist: pymupdf>=1.24.11; extra == 'all'
14
+ Requires-Dist: pypdf>=3.0.0; extra == 'all'
15
+ Requires-Dist: pyside2>=5.15.2.1; extra == 'all'
16
+ Requires-Dist: pytesseract>=0.3.10; extra == 'all'
17
+ Requires-Dist: python-docx>=1.1.0; extra == 'all'
18
+ Requires-Dist: python-pptx>=0.6.21; extra == 'all'
19
+ Provides-Extra: extra
20
+ Requires-Dist: ebooklib>=0.18; extra == 'extra'
21
+ Requires-Dist: markdown>=3.5; extra == 'extra'
22
+ Requires-Dist: odfpy>=1.4.1; extra == 'extra'
23
+ Requires-Dist: pypdf>=3.0.0; extra == 'extra'
24
+ Provides-Extra: gui
25
+ Requires-Dist: pyside2>=5.15.2.1; extra == 'gui'
26
+ Provides-Extra: ocr
27
+ Requires-Dist: pillow>=10.0.0; extra == 'ocr'
28
+ Requires-Dist: pytesseract>=0.3.10; extra == 'ocr'
29
+ Provides-Extra: office
30
+ Requires-Dist: openpyxl>=3.1.0; extra == 'office'
31
+ Requires-Dist: pymupdf>=1.24.11; extra == 'office'
32
+ Requires-Dist: python-docx>=1.1.0; extra == 'office'
33
+ Requires-Dist: python-pptx>=0.6.21; extra == 'office'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # pysfi
37
+
38
+ Single File commands for Interactive python.
39
+
40
+ ## Overview
41
+
42
+ pysfi is a Python project that provides single-file command-line utilities, designed to be lightweight and easy-to-use.
43
+
44
+ ## Available Commands
45
+
46
+ - **alarmclk**: Alarm clock functionality
47
+ - **[bumpversion](sfi/bumpversion/README.md)**: Automated version number management tool
48
+ - **embedinstall**: Embed installation utilities
49
+ - **[filedate](sfi/filedate/README.md)**: A file date management tool that normalizes date prefixes in filenames
50
+ - **mkp**: Make Python project utilities
51
+ - **projectparse**: Project parsing and analysis tools
52
+ - **pyloadergen**: Python loader code generation
53
+ - **pypacker**: Python packaging utilities
54
+
55
+ ## Installation
56
+
57
+ ```bash
58
+ # Install using uv (recommended)
59
+ uv add pysfi
60
+
61
+ # Or using pip
62
+ pip install pysfi
63
+ ```
64
+
65
+ ## Development
66
+
67
+ ### Requirements
68
+
69
+ - Python >= 3.8
70
+ - [uv](https://github.com/astral-sh/uv) (recommended) or pip
71
+
72
+ ### Development Dependencies
73
+
74
+ ```bash
75
+ uv pip install -e ".[dev]"
76
+ ```
77
+
78
+ ### Code Standards
79
+
80
+ The project uses Ruff for code linting and formatting:
81
+
82
+ ```bash
83
+ # Check code
84
+ ruff check .
85
+
86
+ # Format code
87
+ ruff format .
88
+ ```
89
+
90
+ ## Project Structure
91
+
92
+ ```bash
93
+ pysfi/
94
+ ├── pyproject.toml # Main project configuration
95
+ ├── README.md
96
+ └── sfi/
97
+ ├── __init__.py
98
+ ├── alarmclock/ # alarmclk command module
99
+ │ ├── alarmclock.py
100
+ │ ├── pyproject.toml
101
+ │ └── __init__.py
102
+ ├── embedinstall/ # embedinstall command module
103
+ │ ├── embedinstall.py
104
+ │ ├── pyproject.toml
105
+ │ └── __init__.py
106
+ ├── filedate/ # filedate command module
107
+ │ ├── filedate.py
108
+ │ ├── pyproject.toml
109
+ │ ├── README.md # Detailed documentation
110
+ │ └── __init__.py
111
+ ├── makepython/ # mkp command module
112
+ │ ├── makepython.py
113
+ │ ├── pyproject.toml
114
+ │ └── __init__.py
115
+ ├── projectparse/ # projectparse command module
116
+ │ ├── projectparse.py
117
+ │ ├── pyproject.toml
118
+ │ └── __init__.py
119
+ ├── pyloadergen/ # pyloadergen command module
120
+ │ ├── pyloadergen.py
121
+ │ ├── pyproject.toml
122
+ │ └── __init__.py
123
+ └── pypacker/ # pypacker command module
124
+ ├── fspacker.py
125
+ └── pyproject.toml
126
+ ```
127
+
128
+ ## License
129
+
130
+ MIT License
131
+
132
+ ## Contributing
133
+
134
+ Issues and Pull Requests are welcome!
@@ -0,0 +1,31 @@
1
+ sfi/__init__.py,sha256=pSXPoNOdDwew8sssXmWQjA8u92VyNiQ3MXZMcPa9q3c,74
2
+ sfi/cli.py,sha256=bUUTOg18sJQbSKSfsVANhlMgSj9yzO2txIzFAd9B2Ok,296
3
+ sfi/alarmclock/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ sfi/alarmclock/alarmclock.py,sha256=65G8OyTGpe4oQ2SFerQG1N9PVJ4KxO7WzgsTxpGm4O0,12509
5
+ sfi/bumpversion/__init__.py,sha256=GvXipQzQiXXJJ-sG-W4JJTSnOlhClstCnUDgHGrVNMo,85
6
+ sfi/bumpversion/bumpversion.py,sha256=HOyHLaE0sZajrlcVZ8hsim8mPjz77qwQVSo6aIzjMXE,20735
7
+ sfi/docscan/__init__.py,sha256=h-NVUVySlTSfrpVYXKrcOToVRml7WYRynS2viIztWZo,120
8
+ sfi/docscan/docscan.py,sha256=Qigj3nB1hT5JpKQzY79QiS-R8BcqQbF7NZSsBSZRSXA,30899
9
+ sfi/docscan/docscan_gui.py,sha256=iJpUCsbxzgNCMUYinjm_dYvW7JSPb0IAlr_CX9KiiGE,21953
10
+ sfi/embedinstall/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ sfi/embedinstall/embedinstall.py,sha256=N5EbTDdX4bE3W0qHGAwAUuepqFr0sbdZuPI3KWrtuUY,14936
12
+ sfi/filedate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ sfi/filedate/filedate.py,sha256=DpVp26lumE_Lz_4TgqUEX8IxtK3Y6yHSEFV8qJyegyk,3645
14
+ sfi/makepython/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ sfi/makepython/makepython.py,sha256=IVy5MxrLQFJ0O3TPu0E2P7DBfHveFv-3ctBP5U2Dyts,11351
16
+ sfi/pdfsplit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ sfi/pdfsplit/pdfsplit.py,sha256=9M1o9QjTxAAa0DHbTTbDeap2evcazWlb98u80hzJmx0,6004
18
+ sfi/projectparse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ sfi/projectparse/projectparse.py,sha256=Ojg-z4lZEtjEBpJYWyznTgL307N45AxlQKnRkEH0P70,5525
20
+ sfi/pyloadergen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ sfi/pyloadergen/pyloadergen.py,sha256=Erzz1PwrEQcDSCxXG-4DZ-CZavDt6MNv7k3nET5IB9U,32423
22
+ sfi/pypacker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ sfi/pypacker/fspacker.py,sha256=3tlS7qiWoH_kOzsp9eSWsQ-SY7-bSTugwfB-HIL69iE,3238
24
+ sfi/taskkill/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
+ sfi/taskkill/taskkill.py,sha256=6Aw4afmgfLZcQnvgG_38A1VrwazDrnNdOmY1l4kr0lc,7758
26
+ sfi/which/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
+ sfi/which/which.py,sha256=zVIAwZA-pGGngxkkwZ6IxDX3ozVHg7cLSYwYO9FjaIc,2439
28
+ pysfi-0.1.7.dist-info/METADATA,sha256=f7gQc-a5QA5cNdnQf7X5DRCHR8ZHmFvNoSQYFI6GQLU,3922
29
+ pysfi-0.1.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
30
+ pysfi-0.1.7.dist-info/entry_points.txt,sha256=9AaMwUXB86BYgn-RkAqAdd7J3saw0emxsVnO0Q1xCww,564
31
+ pysfi-0.1.7.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,15 @@
1
+ [console_scripts]
2
+ alarmclk = sfi.alarmclock.alarmclock:main
3
+ bumpversion = sfi.bumpversion.bumpversion:main
4
+ docscan = sfi.docscan.docscan:main
5
+ docscan-gui = sfi.docscan.docscan_gui:main
6
+ embedinstall = sfi.embedinstall.embedinstall:main
7
+ filedate = sfi.filedate.filedate:main
8
+ mkp = sfi.makepython.makepython:main
9
+ pdfsplit = sfi.pdfsplit.pdfsplit:main
10
+ projectparse = sfi.projectparse.projectparse:main
11
+ pyloadergen = sfi.pyloadergen.pyloadergen:main
12
+ pypacker = sfi.pypacker.pypacker:main
13
+ sfi = sfi.cli:main
14
+ taskk = sfi.taskkill.taskkill:main
15
+ wch = sfi.which.which:main
sfi/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """Single File commands for Interactive python."""
2
+
3
+ __version__ = "0.1.7"
File without changes
@@ -0,0 +1,367 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import logging
5
+ import random
6
+ import sys
7
+ from datetime import datetime, timedelta, timezone
8
+ from functools import partial
9
+ from typing import ClassVar
10
+
11
+ import qdarkstyle
12
+ from PySide2.QtCore import QSize, Qt, QTime, QTimer
13
+ from PySide2.QtGui import QCloseEvent
14
+ from PySide2.QtWidgets import (
15
+ QApplication,
16
+ QCheckBox,
17
+ QDialog,
18
+ QHBoxLayout,
19
+ QLabel,
20
+ QMainWindow,
21
+ QPushButton,
22
+ QTimeEdit,
23
+ QVBoxLayout,
24
+ QWidget,
25
+ )
26
+
27
+ __version__ = "0.1.2"
28
+ __build_date__ = "2025-09-16"
29
+
30
+
31
+ class AlarmClockConfig:
32
+ """Alarm clock configuration."""
33
+
34
+ ALARM_CLOCK_TITLE = "Digital Alarm Clock"
35
+
36
+ DIGITAL_FONT: str = "bold italic 81px 'Consolas'"
37
+ DIGITAL_COLOR: str = "#ccee00"
38
+ DIGITAL_BORDER_COLORS: ClassVar[list[str]] = [
39
+ "#00aa00",
40
+ "#eecc00",
41
+ "#aa00aa",
42
+ "#c0e0b0",
43
+ ]
44
+ DIGITAL_TIMER_FORMAT: str = "%H:%M:%S"
45
+ DIGITAL_UPDATE_INTERVAL: int = 1000
46
+
47
+ BLINK_TITLE: str = "Alarm Reminder!"
48
+ BLINK_CONTENT: str = "⏰ Time's Up!"
49
+ BLINK_TYPE: str = "color" # Options: 'color' or 'opacity'
50
+ BLINK_BG_COLORS: ClassVar[list[str]] = [
51
+ "#baf1ba",
52
+ "#f8ccc3",
53
+ "#aab4f0",
54
+ "#efaec0",
55
+ ]
56
+ BLINK_INTERVAL: ClassVar[int] = 300 # ms
57
+ DELAY_STEPS: ClassVar[list[int]] = [1, 5, 10, 15, 30, 60] # minutes
58
+
59
+
60
+ logging.basicConfig(level=logging.INFO, format="%(message)s")
61
+
62
+ conf = AlarmClockConfig()
63
+ logger = logging.getLogger(__name__)
64
+
65
+
66
+ class DigitalClock(QLabel):
67
+ """Cool digital clock display."""
68
+
69
+ def __init__(self, parent: QWidget | None = None) -> None:
70
+ super().__init__(parent)
71
+
72
+ self.setAlignment(Qt.AlignCenter) # type: ignore
73
+
74
+ self._color = conf.DIGITAL_BORDER_COLORS[0]
75
+
76
+ # Timer to update current time
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
80
+
81
+ self.update_time()
82
+
83
+ def update_time(self) -> None:
84
+ """Update current time display."""
85
+ current = datetime.now(timezone.utc) + timedelta(hours=8) # Beijing time
86
+ self.setText(current.strftime(conf.DIGITAL_TIMER_FORMAT))
87
+ logger.debug(f"Updated time: {current}")
88
+
89
+ # Add blink effect
90
+ self._color = random.choice(
91
+ [_ for _ in conf.DIGITAL_BORDER_COLORS if _ != self._color],
92
+ )
93
+ self.setStyleSheet(f"""
94
+ font: {conf.DIGITAL_FONT};
95
+ color: {conf.DIGITAL_COLOR};
96
+ background-color: black;
97
+ border: 2px dashed {self._color};
98
+ border-radius: 10px;
99
+ padding: 10px;
100
+ """)
101
+
102
+
103
+ class BlinkDialog(QDialog):
104
+ """Alarm reminder dialog."""
105
+
106
+ def __init__(self) -> None:
107
+ super().__init__()
108
+
109
+ self.setWindowTitle(conf.BLINK_TITLE)
110
+ self.setModal(True)
111
+ self.setWindowFlags(
112
+ self.windowFlags() | Qt.WindowStaysOnTopHint | Qt.WindowType.Dialog, # type: ignore
113
+ )
114
+ self.setFixedSize(QSize(400, 240))
115
+
116
+ layout = QVBoxLayout()
117
+ msg_label = QLabel(conf.BLINK_CONTENT)
118
+ msg_label.setStyleSheet("""
119
+ color: red;
120
+ font-size: 24px;
121
+ """)
122
+ msg_label.setAlignment(Qt.AlignmentFlag.AlignCenter) # type: ignore
123
+
124
+ close_button = QPushButton("Close Alarm")
125
+ close_button.clicked.connect(self.accept) # type: ignore
126
+
127
+ layout.addWidget(msg_label)
128
+ layout.addWidget(close_button)
129
+ self.setLayout(layout)
130
+
131
+ # Prevent user from closing dialog by other means, ensure button click only
132
+ self.setWindowFlag(Qt.WindowCloseButtonHint, False) # type: ignore
133
+
134
+ # Blink control variables and timer
135
+ self.blink_timer = QTimer(self)
136
+ self.blink_timer.timeout.connect(self.update_blink) # type: ignore
137
+ self.blink_state = False
138
+ self.blink_type = conf.BLINK_TYPE
139
+
140
+ # Initialize style
141
+ self.bg_color = random.choice(conf.BLINK_BG_COLORS)
142
+ self.origin_style = self.styleSheet()
143
+ self.blink_timer.start(conf.BLINK_INTERVAL)
144
+
145
+ def update_blink(self) -> None:
146
+ """Timer timeout, update blink state."""
147
+ if self.blink_type == "color":
148
+ # Color blink logic
149
+ colors = [_ for _ in conf.BLINK_BG_COLORS[:] if _ != self.bg_color]
150
+ self.setStyleSheet(f"background-color: {random.choice(colors)}")
151
+ elif self.blink_type == "opacity":
152
+ # Opacity blink logic - Note: Some systems may not fully support window opacity
153
+ new_opacity = 0.3 if self.blink_state else 1.0
154
+ self.setWindowOpacity(new_opacity)
155
+
156
+ self.blink_state = not self.blink_state # Toggle state
157
+
158
+ def stop_blinking(self) -> None:
159
+ """Stop blinking, restore original style."""
160
+ self.blink_timer.stop()
161
+ self.setStyleSheet(self.origin_style) # Restore original style
162
+ self.setWindowOpacity(1.0) # Ensure opacity is restored
163
+
164
+ def closeEvent(self, event: QCloseEvent) -> None: # noqa: N802
165
+ """Override close event, ensure timer stops."""
166
+ self.stop_blinking()
167
+ super().closeEvent(event)
168
+
169
+
170
+ class AlarmClock(QMainWindow):
171
+ """Digital alarm clock GUI."""
172
+
173
+ def __init__(self) -> None:
174
+ super().__init__()
175
+ self.setWindowTitle(f"{conf.ALARM_CLOCK_TITLE} v{__version__}")
176
+ self.setGeometry(
177
+ QApplication.desktop().screenGeometry().center().x() - self.width() // 4,
178
+ QApplication.desktop().screenGeometry().center().y() - self.height() // 2,
179
+ self.width(),
180
+ self.height(),
181
+ )
182
+ self.adjustSize()
183
+
184
+ # Set window style
185
+ self.setStyleSheet("""
186
+ QMainWindow {
187
+ background-color: #2b2b2b;
188
+ }
189
+ QLabel {
190
+ color: #ffffff;
191
+ font-size: 14px;
192
+ }
193
+ QPushButton {
194
+ background-color: #3a3a3a;
195
+ color: white;
196
+ border: 1px solid #5a5a5a;
197
+ padding: 8px;
198
+ border-radius: 4px;
199
+ font-size: 14px;
200
+ }
201
+ QPushButton:hover {
202
+ background-color: #4a4a4a;
203
+ }
204
+ QPushButton:disabled {
205
+ background-color: #2a2a2a;
206
+ color: #6a6a6a;
207
+ }
208
+ QCheckBox {
209
+ color: white;
210
+ font-size: 14px;
211
+ }
212
+ QTimeEdit {
213
+ background-color: #3a3a3a;
214
+ color: white;
215
+ border: 1px solid #5a5a5a;
216
+ padding: 5px;
217
+ font-size: 14px;
218
+ }
219
+ """)
220
+
221
+ # Create central widget
222
+ central_widget = QWidget()
223
+ self.setCentralWidget(central_widget)
224
+
225
+ # Create layout
226
+ main_layout = QVBoxLayout()
227
+ main_layout.setSpacing(20)
228
+ central_widget.setLayout(main_layout)
229
+
230
+ # Cool digital clock display
231
+ self.digital_clock = DigitalClock(parent=self)
232
+ main_layout.addWidget(self.digital_clock)
233
+
234
+ # Alarm time setting
235
+ time_layout = QHBoxLayout()
236
+ time_label = QLabel("Alarm Time:")
237
+ time_label.setStyleSheet("color: white; font-size: 16px;")
238
+ self.alarm_time_edit = QTimeEdit()
239
+ self.alarm_time_edit.setDisplayFormat("HH:mm:ss")
240
+ self.alarm_time_edit.setTime(
241
+ QTime.currentTime().addSecs(conf.DELAY_STEPS[0] * 60),
242
+ )
243
+
244
+ time_layout.addWidget(time_label)
245
+ time_layout.addWidget(self.alarm_time_edit)
246
+ main_layout.addLayout(time_layout)
247
+
248
+ delay_layout = QHBoxLayout()
249
+ delay_label = QLabel("Delay (min):")
250
+ delay_label.setStyleSheet("color: white; font-size: 16px;")
251
+ delay_layout.addWidget(delay_label)
252
+ for minutes in conf.DELAY_STEPS:
253
+ button = QPushButton(str(minutes))
254
+ button.setStyleSheet("color: white; font-size: 16px;")
255
+ button.clicked.connect(partial(self.set_delay, minutes)) # type: ignore
256
+ delay_layout.addWidget(button)
257
+ main_layout.addLayout(delay_layout)
258
+
259
+ # Repeat option
260
+ self.repeat_checkbox = QCheckBox("Repeat")
261
+ main_layout.addWidget(self.repeat_checkbox)
262
+
263
+ # Control buttons
264
+ button_layout = QHBoxLayout()
265
+ self.set_alarm_button = QPushButton("Set Alarm")
266
+ self.set_alarm_button.clicked.connect(self.set_alarm) # type: ignore
267
+ self.cancel_alarm_button = QPushButton("Cancel Alarm")
268
+ self.cancel_alarm_button.clicked.connect(self.cancel_alarm) # type: ignore
269
+ self.cancel_alarm_button.setEnabled(False)
270
+ button_layout.addWidget(self.set_alarm_button)
271
+ button_layout.addWidget(self.cancel_alarm_button)
272
+ main_layout.addLayout(button_layout)
273
+
274
+ # Status display
275
+ self.status_label = QLabel("Alarm not set")
276
+ self.status_label.setAlignment(Qt.AlignCenter) # type: ignore
277
+ self.status_label.setStyleSheet("color: #aaaaaa; font-size: 16px;")
278
+ main_layout.addWidget(self.status_label)
279
+
280
+ # Alarm timer
281
+ self.alarm_timer = QTimer()
282
+ self.alarm_timer.timeout.connect(self.check_alarm) # type: ignore
283
+
284
+ # Alarm state
285
+ self.alarm_set = False
286
+ self.alarm_time: QTime = QTime() # Explicit type
287
+
288
+ def set_delay(self, minutes: int) -> None:
289
+ """Set delay alarm."""
290
+ self.alarm_time_edit.setTime(
291
+ QTime.currentTime().addSecs(minutes * 60),
292
+ )
293
+
294
+ def set_alarm(self) -> None:
295
+ """Set alarm."""
296
+ self.alarm_time = self.alarm_time_edit.time()
297
+ self.alarm_set = True
298
+ self.alarm_timer.start(1000) # Check every second
299
+ self.set_alarm_button.setEnabled(False)
300
+ self.cancel_alarm_button.setEnabled(True)
301
+ self.status_label.setText(
302
+ f"Alarm set: {self.alarm_time.toString('HH:mm:ss')}",
303
+ )
304
+ self.status_label.setStyleSheet(
305
+ "color: #00ff00; font-size: 16px; font-weight: bold;",
306
+ )
307
+
308
+ def cancel_alarm(self) -> None:
309
+ """Cancel alarm."""
310
+ self.alarm_set = False
311
+ self.alarm_timer.stop()
312
+ self.set_alarm_button.setEnabled(True)
313
+ self.cancel_alarm_button.setEnabled(False)
314
+ self.status_label.setText("Alarm cancelled")
315
+ self.status_label.setStyleSheet("color: #aaaaaa; font-size: 16px;")
316
+
317
+ def check_alarm(self) -> None:
318
+ """Check if alarm time has arrived."""
319
+ if not self.alarm_set:
320
+ return
321
+
322
+ current_time = QTime.currentTime()
323
+ if (
324
+ current_time.hour() == self.alarm_time.hour()
325
+ and current_time.minute() == self.alarm_time.minute()
326
+ and current_time.second() == self.alarm_time.second()
327
+ ):
328
+ # Show reminder message
329
+ dialog = BlinkDialog()
330
+ dialog.exec_()
331
+
332
+ self.status_label.setText("⏰ Alarm Rang! ⏰")
333
+ self.status_label.setStyleSheet(
334
+ "color: #ff5555; font-size: 18px; font-weight: bold;",
335
+ )
336
+
337
+ # Add blink effect
338
+ self.status_label.setStyleSheet("""
339
+ color: #ff0000;
340
+ font-size: 18px;
341
+ font-weight: bold;
342
+ background-color: #330000;
343
+ border-radius: 5px;
344
+ padding: 5px;
345
+ """)
346
+
347
+ # Cancel alarm if not repeating
348
+ if not self.repeat_checkbox.isChecked():
349
+ self.cancel_alarm()
350
+
351
+
352
+ def main() -> None:
353
+ parser = argparse.ArgumentParser(
354
+ prog="alarmclock", description="Digital Alarm Clock"
355
+ )
356
+ parser.add_argument("-d", "--debug", action="store_true", help="Enable debug mode")
357
+ parser.add_argument("-v", "--version", action="version", version=f"{__version__}")
358
+
359
+ args = parser.parse_args()
360
+ if args.debug:
361
+ logger.setLevel(logging.DEBUG)
362
+
363
+ app = QApplication(sys.argv)
364
+ app.setStyleSheet(qdarkstyle.load_stylesheet_pyside2())
365
+ window = AlarmClock()
366
+ window.show()
367
+ sys.exit(app.exec_())
@@ -0,0 +1,3 @@
1
+ """Bumpversion - Automated version number management tool."""
2
+
3
+ __version__ = "0.1.7"