-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsingleton_app.py
More file actions
146 lines (121 loc) · 5.36 KB
/
singleton_app.py
File metadata and controls
146 lines (121 loc) · 5.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import logging
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon
from PyQt5.QtNetwork import QLocalSocket, QLocalServer, QAbstractSocket
from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QMenu, QAction
from shadow_window import ShadowWindow
from config.config_manager import ConfigManager
from utils.init_logging import setup_logging
class SingleApplication(QApplication):
def __init__(self, argv):
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
super().__init__(argv)
self.shadow_window = None
self.local_server = None
self.is_running = False
self.tray_icon = None
self.server_name = "aiassistant"
# 初始化本地连接
self.init_local_connection()
# 如果应用已经在运行,直接激活窗口并退出
if self.is_running:
self.activate_running_instance()
sys.exit(0)
# 只有首次运行才创建托盘和主窗口
self.create_tray_icon()
self.shadow_window = ShadowWindow()
self.shadow_window.window_visible = True
def init_local_connection(self):
try:
socket = QLocalSocket()
socket.connectToServer(self.server_name)
if socket.waitForConnected(500):
self.is_running = True
logging.info("检测到已有实例运行")
else:
self.is_running = False
self.local_server = QLocalServer()
self.local_server.newConnection.connect(self.new_local_connection)
# 监听,如果监听失败,可能是之前程序崩溃时残留进程服务导致的,移除残留进程
if not self.local_server.listen(self.server_name):
if self.local_server.serverError() == QAbstractSocket.AddressInUseError:
QLocalServer.removeServer(self.server_name)
self.local_server.listen(self.server_name)
except Exception as e:
logging.error(f"本地连接初始化失败: {str(e)}")
def activate_running_instance(self):
"""激活已运行的实例"""
socket = QLocalSocket()
socket.connectToServer(self.server_name)
if socket.waitForConnected(1000):
socket.write(b"activate")
socket.flush()
socket.waitForBytesWritten(1000)
socket.close()
def new_local_connection(self):
client_connection = self.local_server.nextPendingConnection()
client_connection.readyRead.connect(self.handle_local_message)
def handle_local_message(self):
"""处理来自新实例的消息"""
try:
client_connection = self.local_server.nextPendingConnection()
if client_connection is None: # 空指针检查
logging.warning("Received invalid client connection")
return
# 设置超时并检查是否可读
if not client_connection.waitForReadyRead(1000):
logging.warning("Client connection timeout")
client_connection.close()
return
# 读取消息
msg = client_connection.readAll().data()
if msg == b"activate":
self.show_shadow_window()
except Exception as e:
logging.error(f"Error handling local message: {str(e)}")
finally:
if client_connection and client_connection.state() == QLocalSocket.ConnectedState:
client_connection.close()
def create_tray_icon(self):
open_action = QAction(f"打开{ConfigManager().app_config['name']}", self)
open_action.triggered.connect(self.show_shadow_window)
exit_action = QAction("退出", self)
exit_action.triggered.connect(self.quit_application)
# 右键菜单栏
tray_menu = QMenu()
tray_menu.addAction(open_action)
tray_menu.addAction(exit_action)
self.tray_icon = QSystemTrayIcon(QIcon(ConfigManager().app_config['logo']), self)
self.tray_icon.setContextMenu(tray_menu)
self.tray_icon.activated.connect(self.tray_icon_clicked)
self.tray_icon.show()
def tray_icon_clicked(self, reason):
if reason == QSystemTrayIcon.Trigger: # 左键
if self.shadow_window.window_visible:
self.shadow_window.hide()
else:
self.show_shadow_window()
self.shadow_window.window_visible = not self.shadow_window.window_visible
def show_shadow_window(self):
if self.shadow_window is None:
self.shadow_window = ShadowWindow()
self.shadow_window.window_visible = True
self.shadow_window.show()
self.shadow_window.raise_()
self.shadow_window.activateWindow()
def quit_application(self):
try:
if self.local_server:
self.local_server.close()
QLocalServer.removeServer(self.server_name)
finally:
self.tray_icon.hide()
QApplication.quit()
if __name__ == '__main__':
setup_logging()
QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
app = SingleApplication(sys.argv)
if not app.is_running:
app.shadow_window.show()
sys.exit(app.exec_())