GUI插件完成托盘图标

This commit is contained in:
John Smith 2024-03-17 09:54:55 +08:00
parent c285ba20cc
commit 644e36b4eb
7 changed files with 129 additions and 20 deletions

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
import os
BASE_PATH = os.path.realpath(os.getcwd())
BASE_PATH = os.path.dirname(os.path.realpath(__file__))
LOG_PATH = os.path.join(BASE_PATH, 'log')

View File

@ -8,7 +8,7 @@ import pubsub.pub as pub
logger = logging.getLogger('native-ui.' + __name__)
BASE_PATH = os.path.realpath(os.getcwd())
BASE_PATH = os.path.dirname(os.path.realpath(__file__))
LOG_PATH = os.path.join(BASE_PATH, 'log')
DATA_PATH = os.path.join(BASE_PATH, 'data')
@ -18,6 +18,8 @@ CONFIG_PATH_LIST = [
os.path.join(DATA_PATH, 'config.example.ini')
]
BLC_ICON_PATH = os.path.join(DATA_PATH, 'blivechat.ico')
_config: Optional['AppConfig'] = None

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

View File

@ -6,9 +6,9 @@ import pubsub.pub as pub
import wxasync
import blcsdk.models as sdk_models
import listener
import ui.room_config_dialog
import ui.room_frame
import ui.task_bar_icon
logger = logging.getLogger('native-ui.' + __name__)
@ -22,6 +22,8 @@ def init():
class App(wxasync.WxAsyncApp):
def __init__(self, *args, **kwargs):
self._task_bar_icon: Optional[ui.task_bar_icon.TaskBarIcon] = None
super().__init__(*args, clearSigInt=False, **kwargs)
self.SetExitOnFrameDelete(False)
@ -29,10 +31,12 @@ class App(wxasync.WxAsyncApp):
self._room_config_dialog: Optional[ui.room_config_dialog.RoomConfigDialog] = None
def OnInit(self):
self._task_bar_icon = ui.task_bar_icon.TaskBarIcon()
pub.subscribe(self._on_add_room, 'add_room')
pub.subscribe(self._on_del_room, 'del_room')
pub.subscribe(self._on_room_frame_close, 'room_frame_close')
pub.subscribe(self._on_open_admin_ui, 'open_admin_ui')
pub.subscribe(self._on_add_room, 'open_room')
pub.subscribe(self._on_open_room_config_dialog, 'open_room_config_dialog')
return True
@ -51,10 +55,6 @@ class App(wxasync.WxAsyncApp):
def _on_room_frame_close(self, room_key: sdk_models.RoomKey):
self._key_room_frame_dict.pop(room_key, None)
def _on_open_admin_ui(self):
for room in listener.iter_rooms():
self._on_add_room(room.room_key)
def _on_open_room_config_dialog(self):
if self._room_config_dialog is None or self._room_config_dialog.IsBeingDeleted():
self._room_config_dialog = ui.room_config_dialog.RoomConfigDialog(None)

View File

@ -25,31 +25,35 @@ class RoomFrame(designer.ui_base.RoomFrameBase):
room = listener.get_room(self._room_key)
room_str = str(room.room_id) if room is not None else str(self._room_key)
self.SetTitle(f'blivechat - 房间 {room_str}')
self._apply_config(True)
self.SetIcon(wx.Icon(config.BLC_ICON_PATH, wx.BITMAP_TYPE_ICO))
self.super_chat_list.AppendColumn('时间', width=50)
self.super_chat_list.AppendColumn('用户名', width=120)
self.super_chat_list.AppendColumn('金额', width=50)
self.super_chat_list.AppendColumn('内容', width=300)
for index in range(len(room.super_chats)):
self._on_super_chats_change(room, room.super_chats, index, True)
self.gift_list.AppendColumn('时间', width=50)
self.gift_list.AppendColumn('用户名', width=120)
self.gift_list.AppendColumn('礼物名', width=100)
self.gift_list.AppendColumn('数量', width=50)
self.gift_list.AppendColumn('总价', width=50)
for index in range(len(room.gifts)):
self._on_gifts_change(room, room.gifts, index, True)
# item_data只能存int这里做个映射
self._uid_to_paid_user_item_data: Dict[str, int] = {}
self._next_paid_user_item_data = 1
self.paid_user_list.AppendColumn('用户名', width=120)
self.paid_user_list.AppendColumn('总付费', width=60)
for index in room.uid_paid_user_dict:
self._on_uid_paid_user_dict_change(room, room.uid_paid_user_dict, index, True)
self._apply_config(True)
if room is not None:
for index in range(len(room.super_chats)):
self._on_super_chats_change(room, room.super_chats, index, True)
for index in range(len(room.gifts)):
self._on_gifts_change(room, room.gifts, index, True)
for index in room.uid_paid_user_dict:
self._on_uid_paid_user_dict_change(room, room.uid_paid_user_dict, index, True)
self._on_simple_statistics_change(room)
pub.subscribe(self._on_preview_room_opacity, 'preview_room_opacity')
pub.subscribe(self._on_room_config_dialog_cancel, 'room_config_dialog_cancel')
@ -195,7 +199,7 @@ class RoomFrame(designer.ui_base.RoomFrameBase):
# 模型事件
#
def _on_super_chats_change(self, room: listener.Room, value: List[listener.SuperChatRecord], index, is_new): # noqa
def _on_super_chats_change(self, room: listener.Room, value: List[listener.SuperChatRecord], index, is_new):
if room.room_key != self._room_key:
return
@ -243,7 +247,7 @@ class RoomFrame(designer.ui_base.RoomFrameBase):
if height_to_bottom < last_row_rect.GetHeight() * 3:
list_ctrl.Focus(last_row_index)
def _on_gifts_change(self, room: listener.Room, value: List[listener.GiftRecord], index, is_new): # noqa
def _on_gifts_change(self, room: listener.Room, value: List[listener.GiftRecord], index, is_new):
if room.room_key != self._room_key:
return
@ -260,7 +264,7 @@ class RoomFrame(designer.ui_base.RoomFrameBase):
]
def _on_uid_paid_user_dict_change(
self, room: listener.Room, value: Dict[str, listener.PaidUserRecord], index, is_new # noqa
self, room: listener.Room, value: Dict[str, listener.PaidUserRecord], index, is_new
):
if room.room_key != self._room_key:
return

View File

@ -0,0 +1,103 @@
# -*- coding: utf-8 -*-
import logging
import sys
import webbrowser
import pubsub.pub as pub
import wx.adv
import blcsdk
import config
import listener
if sys.platform == 'win32':
IS_WIN = True
# 懒得引入pywin32了
import ctypes
kernel32 = ctypes.windll.kernel32
user32 = ctypes.windll.user32
else:
IS_WIN = False
logger = logging.getLogger('native-ui.' + __name__)
class TaskBarIcon(wx.adv.TaskBarIcon):
def __init__(self):
super().__init__()
self.SetIcon(wx.Icon(config.BLC_ICON_PATH, wx.BITMAP_TYPE_ICO), 'blivechat')
self._menu = wx.Menu()
self._menu.Append(1, '打开所有房间窗口')
self._menu.Append(2, '打开主页')
if IS_WIN:
self._menu.Append(3, '隐藏/显示控制台')
self._menu.Append(wx.ID_SEPARATOR)
self._menu.Append(wx.ID_EXIT, '退出')
self.Bind(wx.adv.EVT_TASKBAR_LEFT_UP, self._on_open_all_rooms_click)
self.Bind(wx.EVT_MENU, self._on_open_all_rooms_click, id=1)
self.Bind(wx.EVT_MENU, self._on_open_browser_click, id=2)
self.Bind(wx.EVT_MENU, self._on_hide_console_click, id=3)
self.Bind(wx.EVT_MENU, self._on_exit_click, id=wx.ID_EXIT)
pub.subscribe(self._on_open_admin_ui, 'open_admin_ui')
def _on_open_admin_ui(self):
self.PopupMenu(self.GetPopupMenu())
def GetPopupMenu(self):
return self._menu
@staticmethod
def _on_open_browser_click(_event):
blc_port = blcsdk.get_blc_port()
url = 'http://localhost/' if blc_port == 80 else f'http://localhost:{blc_port}/'
webbrowser.open(url)
@staticmethod
def _on_open_all_rooms_click(_event):
room_keys = [room.room_key for room in listener.iter_rooms()]
if not room_keys:
wx.MessageBox('没有任何已连接的房间', '提示')
return
for room_key in room_keys:
pub.sendMessage('open_room', room_key=room_key)
def _on_hide_console_click(self, _event):
assert IS_WIN
console_window_handle = self._find_console_window()
if console_window_handle == 0:
logger.warning('Console window not found')
wx.MessageBox('找不到控制台窗口', '提示')
return
is_visible = user32.IsWindowVisible(console_window_handle)
show_param = 0 if is_visible else 5 # SW_HIDE SW_SHOW
user32.ShowWindowAsync(console_window_handle, show_param)
@staticmethod
def _find_console_window():
assert IS_WIN
console_window_handle: int = kernel32.GetConsoleWindow()
if console_window_handle == 0:
return 0
# 兼容Windows Terminalhttps://github.com/microsoft/terminal/issues/12464
while True:
parent_window_handle: int = user32.GetParent(console_window_handle)
if parent_window_handle == 0:
break
console_window_handle = parent_window_handle
return console_window_handle
def _on_exit_click(self, _event):
assert IS_WIN
# 先恢复控制台显示,防止退出后无法恢复
console_window_handle = self._find_console_window()
if console_window_handle != 0 and not user32.IsWindowVisible(console_window_handle):
user32.ShowWindowAsync(console_window_handle, 5) # SW_SHOW
kernel32.GenerateConsoleCtrlEvent(0, 0) # CTRL_C_EVENT

View File

@ -6,7 +6,7 @@ from typing import *
logger = logging.getLogger('text-to-speech.' + __name__)
BASE_PATH = os.path.realpath(os.getcwd())
BASE_PATH = os.path.dirname(os.path.realpath(__file__))
LOG_PATH = os.path.join(BASE_PATH, 'log')
DATA_PATH = os.path.join(BASE_PATH, 'data')