prkit 0.3.1__tar.gz → 0.3.2__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.
- {prkit-0.3.1 → prkit-0.3.2}/PKG-INFO +6 -4
- {prkit-0.3.1 → prkit-0.3.2}/README.md +5 -3
- {prkit-0.3.1 → prkit-0.3.2}/pyproject.toml +1 -1
- {prkit-0.3.1 → prkit-0.3.2}/pyproject.toml.orig +1 -1
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/__init__.py +1 -1
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/app.py +37 -6
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/language.py +5 -0
- prkit-0.3.2/src/prkit/message_dialog.py +87 -0
- prkit-0.3.2/src/prkit/single_instance.py +23 -0
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/tray.py +1 -3
- {prkit-0.3.1 → prkit-0.3.2}/LICENSE +0 -0
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/__main__.py +0 -0
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/autostart.py +0 -0
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/capslock.py +0 -0
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/cursor_watchdog.py +0 -0
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/icon.py +0 -0
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/input_source.py +0 -0
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/keyboard.py +0 -0
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/keys.py +0 -0
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/mac_capslock.py +0 -0
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/mac_keyboard.py +0 -0
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/overlays.py +0 -0
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/platform.py +0 -0
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/py.typed +0 -0
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/settings.py +0 -0
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/spotlight.py +0 -0
- {prkit-0.3.1 → prkit-0.3.2}/src/prkit/windows_cursor.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: prkit
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: A cross-platform system-tray presentation toolkit
|
|
5
5
|
Author: meebox
|
|
6
6
|
Author-email: meebox <meebox@gmail.com>
|
|
@@ -23,6 +23,8 @@ Description-Content-Type: text/markdown
|
|
|
23
23
|
|
|
24
24
|
跨平台的系統匣簡報輔助工具。程式不會開啟主視窗,所有設定都在系統匣圖示的右鍵選單中完成。
|
|
25
25
|
|
|
26
|
+
同一使用者只能執行一份 PRKit;再次啟動時會提示已有程式在執行,不會建立第二個系統匣圖示。
|
|
27
|
+
|
|
26
28
|
## 功能
|
|
27
29
|
|
|
28
30
|
- 聚光燈游標:可調整顏色、透明度與 48–320 px 圓圈大小;Windows 使用真正的系統游標,macOS/Linux 使用不攔截點擊的透明覆蓋層。
|
|
@@ -38,8 +40,8 @@ Description-Content-Type: text/markdown
|
|
|
38
40
|
|
|
39
41
|
| 平台 | 下載檔案 | 啟動方式 |
|
|
40
42
|
| --- | --- | --- |
|
|
41
|
-
| Windows x64 | `PRKit-0.3.
|
|
42
|
-
| macOS Apple Silicon | `PRKit-0.3.
|
|
43
|
+
| Windows x64 | `PRKit-0.3.2-windows-x64.zip` | 解壓完整資料夾後執行 `PRKit/PRKit.exe` |
|
|
44
|
+
| macOS Apple Silicon | `PRKit-0.3.2-macos-arm64.dmg` | 開啟 DMG,將 `PRKit.app` 拖到「Applications」捷徑後啟動 |
|
|
43
45
|
|
|
44
46
|
Windows ZIP 包含完整執行環境與游標復原 watchdog;請保留整個 `PRKit` 資料夾。macOS 目前僅提供 Apple Silicon 版。程式啟動後顯示在系統匣/選單列,不會開啟主視窗。若啟用了開機自動啟動,搬移 Windows 資料夾後請在選單取消勾選再重新勾選,以更新啟動路徑。
|
|
45
47
|
|
|
@@ -72,7 +74,7 @@ uv tool install git+https://github.com/codemee/prkit.git@latest
|
|
|
72
74
|
prkit
|
|
73
75
|
```
|
|
74
76
|
|
|
75
|
-
若要固定特定版本,將 `latest` 改成版本標籤,例如 `v0.3.
|
|
77
|
+
若要固定特定版本,將 `latest` 改成版本標籤,例如 `v0.3.2`。
|
|
76
78
|
|
|
77
79
|
## 授權
|
|
78
80
|
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
跨平台的系統匣簡報輔助工具。程式不會開啟主視窗,所有設定都在系統匣圖示的右鍵選單中完成。
|
|
6
6
|
|
|
7
|
+
同一使用者只能執行一份 PRKit;再次啟動時會提示已有程式在執行,不會建立第二個系統匣圖示。
|
|
8
|
+
|
|
7
9
|
## 功能
|
|
8
10
|
|
|
9
11
|
- 聚光燈游標:可調整顏色、透明度與 48–320 px 圓圈大小;Windows 使用真正的系統游標,macOS/Linux 使用不攔截點擊的透明覆蓋層。
|
|
@@ -19,8 +21,8 @@
|
|
|
19
21
|
|
|
20
22
|
| 平台 | 下載檔案 | 啟動方式 |
|
|
21
23
|
| --- | --- | --- |
|
|
22
|
-
| Windows x64 | `PRKit-0.3.
|
|
23
|
-
| macOS Apple Silicon | `PRKit-0.3.
|
|
24
|
+
| Windows x64 | `PRKit-0.3.2-windows-x64.zip` | 解壓完整資料夾後執行 `PRKit/PRKit.exe` |
|
|
25
|
+
| macOS Apple Silicon | `PRKit-0.3.2-macos-arm64.dmg` | 開啟 DMG,將 `PRKit.app` 拖到「Applications」捷徑後啟動 |
|
|
24
26
|
|
|
25
27
|
Windows ZIP 包含完整執行環境與游標復原 watchdog;請保留整個 `PRKit` 資料夾。macOS 目前僅提供 Apple Silicon 版。程式啟動後顯示在系統匣/選單列,不會開啟主視窗。若啟用了開機自動啟動,搬移 Windows 資料夾後請在選單取消勾選再重新勾選,以更新啟動路徑。
|
|
26
28
|
|
|
@@ -53,7 +55,7 @@ uv tool install git+https://github.com/codemee/prkit.git@latest
|
|
|
53
55
|
prkit
|
|
54
56
|
```
|
|
55
57
|
|
|
56
|
-
若要固定特定版本,將 `latest` 改成版本標籤,例如 `v0.3.
|
|
58
|
+
若要固定特定版本,將 `latest` 改成版本標籤,例如 `v0.3.2`。
|
|
57
59
|
|
|
58
60
|
## 授權
|
|
59
61
|
|
|
@@ -10,9 +10,11 @@ from PySide6.QtWidgets import QApplication, QMessageBox
|
|
|
10
10
|
|
|
11
11
|
from prkit import autostart
|
|
12
12
|
from prkit.capslock import CapsLockMonitor
|
|
13
|
+
from prkit.icon import tray_icon
|
|
13
14
|
from prkit.input_source import InputSourceMonitor
|
|
14
15
|
from prkit.keyboard import KeyboardMonitor
|
|
15
16
|
from prkit.language import effective_language, tr
|
|
17
|
+
from prkit.message_dialog import MessageDialog
|
|
16
18
|
from prkit.overlays import CapsLockOverlay, KeystrokeOverlay
|
|
17
19
|
from prkit.settings import SettingsStore, SettingsStoreProtocol
|
|
18
20
|
from prkit.spotlight import SpotlightBackend, create_spotlight
|
|
@@ -344,18 +346,47 @@ def create_application(arguments: Sequence[str] | None = None) -> QApplication:
|
|
|
344
346
|
app = QApplication(list(arguments) if arguments is not None else sys.argv)
|
|
345
347
|
app.setOrganizationName("prkit")
|
|
346
348
|
app.setApplicationName("簡報瑞士刀")
|
|
349
|
+
app.setWindowIcon(tray_icon())
|
|
347
350
|
app.setQuitOnLastWindowClosed(False)
|
|
348
351
|
return app
|
|
349
352
|
|
|
350
353
|
|
|
354
|
+
def show_message(app: QApplication, icon: QMessageBox.Icon, language: str, message: str) -> None:
|
|
355
|
+
dialog = MessageDialog(app, icon, language, message)
|
|
356
|
+
dialog.exec()
|
|
357
|
+
|
|
358
|
+
|
|
351
359
|
def main() -> int:
|
|
352
360
|
app = create_application()
|
|
353
|
-
|
|
361
|
+
from prkit.single_instance import acquire
|
|
362
|
+
|
|
363
|
+
try:
|
|
364
|
+
instance_lock = acquire()
|
|
365
|
+
except OSError as error:
|
|
354
366
|
language = effective_language(SettingsStore().load().language)
|
|
355
|
-
|
|
356
|
-
|
|
367
|
+
show_message(
|
|
368
|
+
app,
|
|
369
|
+
QMessageBox.Icon.Critical,
|
|
370
|
+
language,
|
|
371
|
+
tr("無法檢查是否已有執行中的 PRKit:{error}", language).format(error=error),
|
|
357
372
|
)
|
|
358
373
|
return 1
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
374
|
+
if instance_lock is None:
|
|
375
|
+
language = effective_language(SettingsStore().load().language)
|
|
376
|
+
show_message(app, QMessageBox.Icon.Information, language, tr("PRKit 已在執行。", language))
|
|
377
|
+
return 0
|
|
378
|
+
try:
|
|
379
|
+
if not TrayController.is_available():
|
|
380
|
+
language = effective_language(SettingsStore().load().language)
|
|
381
|
+
show_message(
|
|
382
|
+
app,
|
|
383
|
+
QMessageBox.Icon.Critical,
|
|
384
|
+
language,
|
|
385
|
+
tr("此桌面環境沒有可用的系統匣。", language),
|
|
386
|
+
)
|
|
387
|
+
return 1
|
|
388
|
+
controller = AppController(app)
|
|
389
|
+
controller.start()
|
|
390
|
+
return app.exec()
|
|
391
|
+
finally:
|
|
392
|
+
instance_lock.unlock()
|
|
@@ -32,6 +32,7 @@ ENGLISH = {
|
|
|
32
32
|
"英文": "English",
|
|
33
33
|
"中文(注音)": "Chinese (Zhuyin)",
|
|
34
34
|
"結束": "Quit",
|
|
35
|
+
"確定": "OK",
|
|
35
36
|
"無法設定開機自動啟動:{error}": "Could not change start at login: {error}",
|
|
36
37
|
"無法顯示鍵盤狀態:{message}": "Could not display keyboard status: {message}",
|
|
37
38
|
"無法啟用聚光燈游標:{error}": "Could not enable spotlight cursor: {error}",
|
|
@@ -40,6 +41,10 @@ ENGLISH = {
|
|
|
40
41
|
"未知錯誤": "Unknown error",
|
|
41
42
|
"按鍵監聽已停止:{message}": "Keystroke monitoring stopped: {message}",
|
|
42
43
|
"此桌面環境沒有可用的系統匣。": "No system tray is available in this desktop environment.",
|
|
44
|
+
"PRKit 已在執行。": "PRKit is already running.",
|
|
45
|
+
"無法檢查是否已有執行中的 PRKit:{error}": (
|
|
46
|
+
"Could not check for a running PRKit instance: {error}"
|
|
47
|
+
),
|
|
43
48
|
}
|
|
44
49
|
|
|
45
50
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Compact, branded dialogs for startup messages."""
|
|
2
|
+
|
|
3
|
+
from PySide6.QtCore import Qt
|
|
4
|
+
from PySide6.QtWidgets import (
|
|
5
|
+
QApplication,
|
|
6
|
+
QDialog,
|
|
7
|
+
QHBoxLayout,
|
|
8
|
+
QLabel,
|
|
9
|
+
QMessageBox,
|
|
10
|
+
QPushButton,
|
|
11
|
+
QVBoxLayout,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from prkit.language import tr
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MessageDialog(QDialog):
|
|
18
|
+
def __init__(
|
|
19
|
+
self, app: QApplication, icon: QMessageBox.Icon, language: str, message: str
|
|
20
|
+
) -> None:
|
|
21
|
+
super().__init__()
|
|
22
|
+
self.setObjectName("prkitMessageDialog")
|
|
23
|
+
self.setWindowTitle(tr("簡報瑞士刀", language))
|
|
24
|
+
self.setWindowIcon(app.windowIcon())
|
|
25
|
+
self.setWindowFlag(Qt.WindowType.WindowContextHelpButtonHint, False)
|
|
26
|
+
|
|
27
|
+
critical = icon == QMessageBox.Icon.Critical
|
|
28
|
+
self.badge = QLabel("!" if critical else "i", self)
|
|
29
|
+
self.badge.setObjectName("statusBadge")
|
|
30
|
+
self.badge.setFixedSize(20, 20)
|
|
31
|
+
self.badge.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
32
|
+
|
|
33
|
+
self.message_label = QLabel(message, self)
|
|
34
|
+
self.message_label.setObjectName("messageText")
|
|
35
|
+
self.message_label.setWordWrap(True)
|
|
36
|
+
self.message_label.setMaximumWidth(360)
|
|
37
|
+
self.message_label.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
|
|
38
|
+
self.message_label.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
|
|
39
|
+
|
|
40
|
+
content = QHBoxLayout()
|
|
41
|
+
content.setSpacing(12)
|
|
42
|
+
content.addWidget(self.badge, alignment=Qt.AlignmentFlag.AlignVCenter)
|
|
43
|
+
content.addWidget(self.message_label, 1, Qt.AlignmentFlag.AlignVCenter)
|
|
44
|
+
|
|
45
|
+
self.confirm_button = QPushButton(tr("確定", language), self)
|
|
46
|
+
self.confirm_button.setObjectName("confirmButton")
|
|
47
|
+
self.confirm_button.setFixedSize(76, 32)
|
|
48
|
+
self.confirm_button.setDefault(True)
|
|
49
|
+
self.confirm_button.clicked.connect(self.accept)
|
|
50
|
+
|
|
51
|
+
actions = QHBoxLayout()
|
|
52
|
+
actions.addStretch()
|
|
53
|
+
actions.addWidget(self.confirm_button)
|
|
54
|
+
|
|
55
|
+
layout = QVBoxLayout(self)
|
|
56
|
+
layout.setContentsMargins(20, 20, 20, 18)
|
|
57
|
+
layout.setSpacing(22)
|
|
58
|
+
layout.addLayout(content)
|
|
59
|
+
layout.addLayout(actions)
|
|
60
|
+
|
|
61
|
+
badge_color = "#D64545" if critical else "#2563EB"
|
|
62
|
+
self.setStyleSheet(
|
|
63
|
+
"QDialog#prkitMessageDialog { background-color: #FFFFFF; }"
|
|
64
|
+
"QLabel#statusBadge { background-color: "
|
|
65
|
+
f"{badge_color}; color: #FFFFFF; border-radius: 10px; "
|
|
66
|
+
"font-size: 12px; font-weight: 600; }"
|
|
67
|
+
"QLabel#messageText { color: #263445; font-size: 13px; }"
|
|
68
|
+
"QPushButton#confirmButton { background-color: #2563EB; color: #FFFFFF; "
|
|
69
|
+
"border: 0; border-radius: 6px; font-size: 13px; }"
|
|
70
|
+
"QPushButton#confirmButton:hover { background-color: #1D4ED8; }"
|
|
71
|
+
"QPushButton#confirmButton:pressed { background-color: #1E40AF; }"
|
|
72
|
+
)
|
|
73
|
+
self.ensurePolished()
|
|
74
|
+
self.message_label.ensurePolished()
|
|
75
|
+
text_width = self.message_label.fontMetrics().horizontalAdvance(message)
|
|
76
|
+
margins = layout.contentsMargins()
|
|
77
|
+
non_text_width = margins.left() + self.badge.width() + content.spacing() + margins.right()
|
|
78
|
+
width = min(420, max(240, text_width + non_text_width))
|
|
79
|
+
text_height = self.message_label.heightForWidth(width - non_text_width)
|
|
80
|
+
height = (
|
|
81
|
+
margins.top()
|
|
82
|
+
+ max(self.badge.height(), text_height)
|
|
83
|
+
+ layout.spacing()
|
|
84
|
+
+ self.confirm_button.height()
|
|
85
|
+
+ margins.bottom()
|
|
86
|
+
)
|
|
87
|
+
self.setFixedSize(width, height)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Keep one GUI process per user across installed and frozen copies."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from PySide6.QtCore import QLockFile, QStandardPaths
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def acquire(directory: Path | None = None) -> QLockFile | None:
|
|
9
|
+
if directory is None:
|
|
10
|
+
location = QStandardPaths.writableLocation(
|
|
11
|
+
QStandardPaths.StandardLocation.AppLocalDataLocation
|
|
12
|
+
)
|
|
13
|
+
if not location:
|
|
14
|
+
raise OSError("No local application data directory is available")
|
|
15
|
+
directory = Path(location)
|
|
16
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
17
|
+
lock = QLockFile(str(directory / "prkit.lock"))
|
|
18
|
+
lock.setStaleLockTime(0)
|
|
19
|
+
if lock.tryLock(0):
|
|
20
|
+
return lock
|
|
21
|
+
if lock.error() == QLockFile.LockError.LockFailedError:
|
|
22
|
+
return None
|
|
23
|
+
raise OSError(f"Could not acquire the PRKit instance lock: {lock.error().name}")
|
|
@@ -336,9 +336,7 @@ class TrayController(QObject):
|
|
|
336
336
|
self.tray.hide()
|
|
337
337
|
|
|
338
338
|
def notify_warning(self, message: str) -> None:
|
|
339
|
-
self.tray.showMessage(
|
|
340
|
-
self.tray.toolTip(), message, QSystemTrayIcon.MessageIcon.Warning, 5000
|
|
341
|
-
)
|
|
339
|
+
self.tray.showMessage(self.tray.toolTip(), message, self.tray.icon(), 5000)
|
|
342
340
|
|
|
343
341
|
def connect_settings(
|
|
344
342
|
self,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|