115 lines
3.0 KiB
Python
115 lines
3.0 KiB
Python
import sys
|
||
import os
|
||
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
|
||
|
||
__VERSION__ = "0.0.2"
|
||
|
||
# ===================== 打包兼容核心函数 =====================
|
||
def get_resource_path(relative_path):
|
||
"""
|
||
打包 EXE 后获取资源路径,开发环境也能用
|
||
"""
|
||
if hasattr(sys, '_MEIPASS'):
|
||
return os.path.join(sys._MEIPASS, relative_path)
|
||
return os.path.join(os.path.abspath("."), relative_path)
|
||
# ==========================================================
|
||
|
||
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 = "牛马软件柜 v" + __VERSION__
|
||
|
||
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):
|
||
hwnd = user32.FindWindowW(None, title)
|
||
if hwnd:
|
||
break
|
||
time.sleep(0.3)
|
||
|
||
if hwnd:
|
||
user32.ShowWindow(hwnd, 5)
|
||
user32.SetForegroundWindow(hwnd)
|
||
return False
|
||
|
||
return True
|
||
|
||
|
||
def main():
|
||
if not _wake_existing_or_exit():
|
||
return
|
||
|
||
QApplication.setAttribute(Qt.ApplicationAttribute.AA_Use96Dpi, True)
|
||
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
|
||
panel._apply_pin_window_layer()
|
||
|
||
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:
|
||
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() |