first commit

This commit is contained in:
2026-04-03 21:50:36 +08:00
commit 10843a5bc5
43 changed files with 3958 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
import sys
import time
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QApplication
from db.database import init_db
from ui.dock import PanelWindow, _set_autostart
from ui.ball import FloatBall, BALL_SIZE
import ui.theme as theme
from db import database
def _wake_existing_or_exit() -> bool:
"""
Windows 单实例:
- 若已有进程在运行:唤醒已有窗口并返回 False(当前进程退出)
- 若无已有进程:返回 True(继续正常启动)
"""
if sys.platform != "win32":
return True
import ctypes
import ctypes.wintypes
mutex_name = r"Global\CleanDesktopOrganizerSingleton"
title = "牛马软件柜"
kernel32 = ctypes.windll.kernel32
user32 = ctypes.windll.user32
ERROR_ALREADY_EXISTS = 183
h_mutex = kernel32.CreateMutexW(None, False, mutex_name)
if not h_mutex:
return True
last_err = kernel32.GetLastError()
if last_err == ERROR_ALREADY_EXISTS:
# 轮询一小段时间,避免首次窗口尚未创建
hwnd = None
for _ in range(8): # ~2.4s
hwnd = user32.FindWindowW(None, title)
if hwnd:
break
time.sleep(0.3)
if hwnd:
# SW_SHOW = 5
user32.ShowWindow(hwnd, 5)
user32.SetForegroundWindow(hwnd)
return False
return True
def main():
# 必须在创建 QApplication 之前:不按各显示器缩放,逻辑像素固定(跨分辨率/跨屏拖动宽高保持一致)
if not _wake_existing_or_exit():
return
QApplication.setAttribute(Qt.ApplicationAttribute.AA_Use96Dpi, True)
# 这个策略同样要求在创建 QApplication 前设置
try:
QApplication.setHighDpiScaleFactorRoundingPolicy(
Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
)
except Exception:
pass
app = QApplication(sys.argv)
app.setQuitOnLastWindowClosed(False)
init_db()
theme.load() # 从数据库读取上次主题
_set_autostart(True)
panel = PanelWindow()
ball = FloatBall()
panel._ball_ref = ball
ball.clicked.connect(lambda: panel.show_near(ball.pos(), BALL_SIZE))
ball.right_clicked.connect(lambda pos: panel.tray.contextMenu().exec(pos))
# 判断是否有保存的位置:有则直接在原位显示,没有则用悬浮球旁边
has_saved = bool(database.get_setting("panel_x", ""))
if has_saved:
# _restore_geometry 已在 PanelWindow.__init__ 里执行,直接 show
panel.setWindowOpacity(0)
panel.show()
panel.raise_()
if hasattr(panel, "_ball_ref"):
panel._ball_ref.hide()
# 淡入
from PyQt6.QtCore import QPropertyAnimation
anim = QPropertyAnimation(panel, b"windowOpacity")
anim.setDuration(180)
anim.setStartValue(0.0)
anim.setEndValue(1.0)
anim.start()
panel._anim = anim
else:
ball._place_default()
panel.show_near(ball.pos(), BALL_SIZE)
sys.exit(app.exec())
if __name__ == "__main__":
main()