niumasoftware/main.py
2026-04-03 21:50:36 +08:00

107 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()