Skip to content

Commit 9499e36

Browse files
committed
新增 **快捷键设置**,支持打开点名/抽奖页面、使用闪抽、增减人数、开始抽取
1 parent 787c296 commit 9499e36

11 files changed

Lines changed: 774 additions & 43 deletions

File tree

CHANGELOG/v2.1.0/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ v2.0 - Koharu(小鸟游星野) release 2
77
- 新增 **关于页面横幅**,添加横幅图片展示
88
- 新增 **闪抽抽取人数**,独立抽取人数设置
99
- 新增 **无焦点模式**,通知窗口显示时不抢占焦点
10+
- 新增 **快捷键设置**,支持打开点名/抽奖页面、使用闪抽、增减人数、开始抽取
1011

1112
## 💡 功能优化
1213

app/Language/modules/more_settings.py

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,48 @@
165165
},
166166
}
167167

168-
# 调试设置语言配置
169-
debug = {
170-
"ZH_CN": {"title": {"name": "调试设置", "description": "调试功能设置"}},
171-
"EN_US": {"title": {"name": "Debug settings", "description": "Debug settings"}},
168+
# 快捷键设置
169+
shortcut_settings = {
170+
"ZH_CN": {
171+
"title": {"name": "快捷键设置", "description": "设置快捷键功能"},
172+
"function": {"name": "功能"},
173+
"shortcut": {"name": "快捷键"},
174+
"press_shortcut": {"name": "点击此处设置快捷键"},
175+
"enable_shortcut": {
176+
"name": "启用快捷键",
177+
"description": "启用后可使用快捷键快速操作",
178+
"switchbutton_name": {"enable": "启用", "disable": "禁用"},
179+
},
180+
"open_roll_call_page": {"name": "打开点名页面"},
181+
"use_quick_draw": {"name": "使用闪抽"},
182+
"open_lottery_page": {"name": "打开抽奖页面"},
183+
"increase_roll_call_count": {"name": "增加点名人数"},
184+
"decrease_roll_call_count": {"name": "减少点名人数"},
185+
"increase_lottery_count": {"name": "增加抽奖人数"},
186+
"decrease_lottery_count": {"name": "减少抽奖人数"},
187+
"start_roll_call": {"name": "开始点名"},
188+
"start_lottery": {"name": "开始抽奖"},
189+
},
190+
"EN_US": {
191+
"title": {"name": "Shortcut settings", "description": "Shortcut settings"},
192+
"function": {"name": "Function"},
193+
"shortcut": {"name": "Shortcut"},
194+
"press_shortcut": {"name": "Click here to set shortcut"},
195+
"enable_shortcut": {
196+
"name": "Enable shortcut",
197+
"description": "Enable shortcuts for quick operations",
198+
"switchbutton_name": {"enable": "Enable", "disable": "Disable"},
199+
},
200+
"open_roll_call_page": {"name": "Open roll call page"},
201+
"use_quick_draw": {"name": "Use quick draw"},
202+
"open_lottery_page": {"name": "Open lottery page"},
203+
"increase_roll_call_count": {"name": "Increase roll call count"},
204+
"decrease_roll_call_count": {"name": "Decrease roll call count"},
205+
"increase_lottery_count": {"name": "Increase lottery count"},
206+
"decrease_lottery_count": {"name": "Decrease lottery count"},
207+
"start_roll_call": {"name": "Start roll call"},
208+
"start_lottery": {"name": "Start lottery"},
209+
},
172210
}
173211

174212
# 关于语言配置

app/common/IPC_URL/csharp_ipc_handler.py

Lines changed: 66 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
try:
1111
# 导入 Python.NET
1212
from pythonnet import load
13+
1314
load("coreclr", runtime_config=get_data_path("dlls", "dotnet.runtimeconfig.json"))
1415

1516
# 加载 .NET CoreCLR 程序集
1617
import clr
18+
1719
clr.AddReference("ClassIsland.Shared.IPC")
1820
clr.AddReference("SecRandom4Ci.Interface")
1921

@@ -32,8 +34,10 @@
3234

3335

3436
if CSHARP_AVAILABLE:
37+
3538
class CSharpIPCHandler:
3639
"""C# dotnetCampus.Ipc 处理器,用于连接 ClassIsland 实例"""
40+
3741
_instance: Optional["CSharpIPCHandler"] = None
3842

3943
def __new__(cls):
@@ -56,7 +60,7 @@ def __init__(self):
5660
self.ipc_client: Optional[IpcClient] = None
5761
self.client_thread: Optional[threading.Thread] = None
5862
self.is_running = False
59-
63+
6064
def start_ipc_client(self) -> bool:
6165
"""
6266
启动 C# IPC 客户端
@@ -68,7 +72,9 @@ def start_ipc_client(self) -> bool:
6872
return True
6973

7074
try:
71-
self.client_thread = threading.Thread(target=self._run_client, daemon=False)
75+
self.client_thread = threading.Thread(
76+
target=self._run_client, daemon=False
77+
)
7278
self.client_thread.start()
7379
self.is_running = True
7480
return True
@@ -81,39 +87,52 @@ def stop_ipc_client(self):
8187
self.is_running = False
8288
if self.client_thread and self.client_thread.is_alive():
8389
self.client_thread.join(timeout=1)
84-
90+
8591
def send_notification(
86-
self,
87-
class_name,
88-
selected_students,
89-
draw_count=1,
90-
settings=None,
91-
settings_group=None
92-
) -> bool:
92+
self,
93+
class_name,
94+
selected_students,
95+
draw_count=1,
96+
settings=None,
97+
settings_group=None,
98+
) -> bool:
9399
"""发送提醒"""
94100

95101
if settings:
96102
display_duration = settings.get("notification_display_duration", 5)
97103
else:
98104
display_duration = 5
99-
105+
100106
randomService = GeneratedIpcFactory.CreateIpcProxy[ISecRandomService](
101-
self.ipc_client.Provider, self.ipc_client.PeerProxy)
102-
result = self.convert_to_call_result(class_name, selected_students, draw_count, display_duration)
107+
self.ipc_client.Provider, self.ipc_client.PeerProxy
108+
)
109+
result = self.convert_to_call_result(
110+
class_name, selected_students, draw_count, display_duration
111+
)
103112
randomService.NotifyResult(result)
104113

105114
return True
106115

107116
def is_breaking(self) -> bool:
108117
"""是否处于下课时间"""
109118
lessonSc = GeneratedIpcFactory.CreateIpcProxy[IPublicLessonsService](
110-
self.ipc_client.Provider, self.ipc_client.PeerProxy)
111-
state = lessonSc.CurrentState in [getattr(TimeState, "None"), TimeState.PrepareOnClass, TimeState.Breaking, TimeState.AfterSchool]
112-
logger.debug(f"获取到的 ClassIsland 时间状态: {lessonSc.CurrentState} 是否下课: {state}")
119+
self.ipc_client.Provider, self.ipc_client.PeerProxy
120+
)
121+
state = lessonSc.CurrentState in [
122+
getattr(TimeState, "None"),
123+
TimeState.PrepareOnClass,
124+
TimeState.Breaking,
125+
TimeState.AfterSchool,
126+
]
127+
logger.debug(
128+
f"获取到的 ClassIsland 时间状态: {lessonSc.CurrentState} 是否下课: {state}"
129+
)
113130
return state
114131

115132
@staticmethod
116-
def convert_to_call_result(class_name: str, selected_students, draw_count: int, display_duration=5.0) -> CallResult:
133+
def convert_to_call_result(
134+
class_name: str, selected_students, draw_count: int, display_duration=5.0
135+
) -> CallResult:
117136
result = CallResult()
118137
result.ClassName = class_name
119138
result.DrawCount = draw_count
@@ -128,8 +147,11 @@ def convert_to_call_result(class_name: str, selected_students, draw_count: int,
128147

129148
def _on_class_test(self):
130149
lessonSc = GeneratedIpcFactory.CreateIpcProxy[IPublicLessonsService](
131-
self.ipc_client.Provider, self.ipc_client.PeerProxy)
132-
logger.debug(f"上课 {lessonSc.CurrentSubject.Name} 时间: {lessonSc.CurrentTimeLayoutItem}")
150+
self.ipc_client.Provider, self.ipc_client.PeerProxy
151+
)
152+
logger.debug(
153+
f"上课 {lessonSc.CurrentSubject.Name} 时间: {lessonSc.CurrentTimeLayoutItem}"
154+
)
133155

134156
def _run_client(self):
135157
"""运行 C# IPC 客户端"""
@@ -138,24 +160,29 @@ async def client():
138160
"""异步客户端"""
139161

140162
self.ipc_client = IpcClient()
141-
self.ipc_client.JsonIpcProvider.AddNotifyHandler(IpcRoutedNotifyIds.OnClassNotifyId, Action(lambda: self._on_class_test()))
163+
self.ipc_client.JsonIpcProvider.AddNotifyHandler(
164+
IpcRoutedNotifyIds.OnClassNotifyId,
165+
Action(lambda: self._on_class_test()),
166+
)
142167

143168
task = self.ipc_client.Connect()
144169
await loop.run_in_executor(None, lambda: task.Wait())
145-
170+
146171
while self.is_running:
147172
await asyncio.sleep(1)
148-
173+
149174
self.ipc_client = None
150-
175+
151176
# 启动新的 asyncio 事件循环
152177
loop = asyncio.new_event_loop()
153178
asyncio.set_event_loop(loop)
154179
loop.run_until_complete(client())
155180
loop.close()
156181
else:
182+
157183
class CSharpIPCHandler:
158184
"""C# dotnetCampus.Ipc 处理器,用于连接 ClassIsland 实例"""
185+
159186
_instance: Optional["CSharpIPCHandler"] = None
160187

161188
def __new__(cls):
@@ -170,15 +197,15 @@ def instance(cls):
170197
if cls._instance is None:
171198
cls._instance = cls()
172199
return cls._instance
173-
200+
174201
def __init__(self):
175202
"""
176203
初始化 C# IPC 处理器
177204
"""
178205
self.ipc_client = None
179206
self.client_thread = None
180207
self.is_running = False
181-
208+
182209
def start_ipc_client(self) -> bool:
183210
"""
184211
启动 C# IPC 客户端
@@ -187,33 +214,35 @@ def start_ipc_client(self) -> bool:
187214
启动成功返回True,失败返回False
188215
"""
189216
return False
190-
217+
191218
def stop_ipc_client(self):
192219
"""停止 C# IPC 客户端"""
193220
pass
194-
221+
195222
def send_notification(
196-
self,
197-
class_name,
198-
selected_students,
199-
draw_count=1,
200-
settings=None,
201-
settings_group=None
202-
) -> bool:
223+
self,
224+
class_name,
225+
selected_students,
226+
draw_count=1,
227+
settings=None,
228+
settings_group=None,
229+
) -> bool:
203230
"""发送提醒"""
204231
return False
205232

206233
def is_breaking(self) -> bool:
207234
"""是否处于下课时间"""
208235
return False
209-
236+
210237
@staticmethod
211-
def convert_to_call_result(class_name: str, selected_students, draw_count: int, display_duration=5.0) -> object:
238+
def convert_to_call_result(
239+
class_name: str, selected_students, draw_count: int, display_duration=5.0
240+
) -> object:
212241
return object
213242

214243
def _on_class_test(self):
215244
pass
216-
245+
217246
def _run_client(self):
218247
"""运行 C# IPC 客户端"""
219248
pass

app/common/notification/notification_service.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -959,7 +959,9 @@ def send_to_classisland(
959959

960960
try:
961961
cs_ipc = CSharpIPCHandler.instance()
962-
status = cs_ipc.send_notification(class_name, selected_students, draw_count, settings, settings_group)
962+
status = cs_ipc.send_notification(
963+
class_name, selected_students, draw_count, settings, settings_group
964+
)
963965
if status:
964966
logger.info("成功发送通知到ClassIsland,结果未知")
965967
else:

app/common/shortcut/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from app.common.shortcut.shortcut_manager import ShortcutManager
2+
3+
__all__ = ["ShortcutManager"]

0 commit comments

Comments
 (0)