diff --git a/plugins/native-ui/designer/native-ui.fbp b/plugins/native-ui/designer/native-ui.fbp
index 421a526..ba427e0 100644
--- a/plugins/native-ui/designer/native-ui.fbp
+++ b/plugins/native-ui/designer/native-ui.fbp
@@ -48,7 +48,7 @@
RoomFrameBase
- 750,650
+ 800,650
wxDEFAULT_FRAME_STYLE
; ; forward_declare
blivechat - 房间 123456
@@ -58,6 +58,7 @@
wxTAB_TRAVERSAL
1
+ _on_close
@@ -417,7 +421,7 @@
0
-
+ 200,-1
1
console_notebook
1
@@ -674,7 +678,7 @@
Resizable
1
- wxLC_REPORT
+ wxLC_REPORT|wxLC_SINGLE_SEL
; ; forward_declare
0
@@ -801,7 +805,7 @@
Resizable
1
- wxLC_REPORT
+ wxLC_REPORT|wxLC_SINGLE_SEL
; ; forward_declare
0
@@ -940,7 +944,7 @@
Resizable
1
- wxLC_REPORT
+ wxLC_REPORT|wxLC_SINGLE_SEL
; ; forward_declare
0
diff --git a/plugins/native-ui/designer/ui_base.py b/plugins/native-ui/designer/ui_base.py
index f6b5de4..d41351c 100644
--- a/plugins/native-ui/designer/ui_base.py
+++ b/plugins/native-ui/designer/ui_base.py
@@ -18,7 +18,7 @@ import wx.html2
class RoomFrameBase ( wx.Frame ):
def __init__( self, parent ):
- wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = u"blivechat - 房间 123456", pos = wx.DefaultPosition, size = wx.Size( 750,650 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
+ wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = u"blivechat - 房间 123456", pos = wx.DefaultPosition, size = wx.Size( 800,650 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
self.SetSizeHints( wx.DefaultSize, wx.DefaultSize )
self.SetForegroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_WINDOW ) )
@@ -39,7 +39,7 @@ class RoomFrameBase ( wx.Frame ):
bSizer3.Add( ( 0, 0), 1, wx.EXPAND, 5 )
- self.collapse_console_button = wx.Button( self, wx.ID_ANY, u"<<", wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.collapse_console_button = wx.Button( self, wx.ID_ANY, u">>", wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer3.Add( self.collapse_console_button, 0, 0, 5 )
@@ -52,6 +52,8 @@ class RoomFrameBase ( wx.Frame ):
bSizer1.Add( bSizer2, 1, wx.EXPAND, 5 )
self.console_notebook = wx.Notebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.console_notebook.SetMinSize( wx.Size( 200,-1 ) )
+
self.paid_panel = wx.Panel( self.console_notebook, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
bSizer4 = wx.BoxSizer( wx.VERTICAL )
@@ -66,7 +68,7 @@ class RoomFrameBase ( wx.Frame ):
self.super_chat_panel = wx.Panel( self.console_notebook, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
bSizer5 = wx.BoxSizer( wx.VERTICAL )
- self.super_chat_list = wx.ListCtrl( self.super_chat_panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LC_REPORT )
+ self.super_chat_list = wx.ListCtrl( self.super_chat_panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LC_REPORT|wx.LC_SINGLE_SEL )
bSizer5.Add( self.super_chat_list, 1, wx.EXPAND, 5 )
@@ -77,7 +79,7 @@ class RoomFrameBase ( wx.Frame ):
self.gift_panel = wx.Panel( self.console_notebook, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
bSizer6 = wx.BoxSizer( wx.VERTICAL )
- self.gift_list = wx.ListCtrl( self.gift_panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LC_REPORT )
+ self.gift_list = wx.ListCtrl( self.gift_panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LC_REPORT|wx.LC_SINGLE_SEL )
bSizer6.Add( self.gift_list, 1, wx.EXPAND, 5 )
@@ -90,7 +92,7 @@ class RoomFrameBase ( wx.Frame ):
sbSizer1 = wx.StaticBoxSizer( wx.StaticBox( self.statistics_panel, wx.ID_ANY, u"付费用户" ), wx.VERTICAL )
- self.paid_user_list = wx.ListCtrl( sbSizer1.GetStaticBox(), wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LC_REPORT )
+ self.paid_user_list = wx.ListCtrl( sbSizer1.GetStaticBox(), wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LC_REPORT|wx.LC_SINGLE_SEL )
sbSizer1.Add( self.paid_user_list, 1, wx.EXPAND, 5 )
@@ -115,10 +117,30 @@ class RoomFrameBase ( wx.Frame ):
self.Centre( wx.BOTH )
+ # Connect Events
+ self.Bind( wx.EVT_CLOSE, self._on_close )
+ self.config_button.Bind( wx.EVT_BUTTON, self._on_config_button_click )
+ self.stay_on_top_button.Bind( wx.EVT_TOGGLEBUTTON, self._on_stay_on_top_button_toggle )
+ self.collapse_console_button.Bind( wx.EVT_BUTTON, self._on_collapse_console_button_click )
+
def __del__( self ):
pass
+ # Virtual event handlers, override them in your derived class
+ def _on_close( self, event ):
+ event.Skip()
+
+ def _on_config_button_click( self, event ):
+ event.Skip()
+
+ def _on_stay_on_top_button_toggle( self, event ):
+ event.Skip()
+
+ def _on_collapse_console_button_click( self, event ):
+ event.Skip()
+
+
###########################################################################
## Class RoomConfigDialogBase
###########################################################################
diff --git a/plugins/native-ui/listener.py b/plugins/native-ui/listener.py
index d7859f6..a418086 100644
--- a/plugins/native-ui/listener.py
+++ b/plugins/native-ui/listener.py
@@ -5,6 +5,8 @@ import datetime
import logging
from typing import *
+import pubsub.pub as pub
+
import blcsdk
import blcsdk.models as sdk_models
@@ -41,7 +43,7 @@ class MsgHandler(blcsdk.BaseHandler):
def _on_open_plugin_admin_ui(
self, client: blcsdk.BlcPluginClient, message: sdk_models.OpenPluginAdminUiMsg, extra: sdk_models.ExtraData
):
- pass
+ pub.sendMessage('open_admin_ui')
def _on_room_init(
self, client: blcsdk.BlcPluginClient, message: sdk_models.RoomInitMsg, extra: sdk_models.ExtraData
@@ -119,6 +121,10 @@ class MsgHandler(blcsdk.BaseHandler):
))
+def iter_rooms() -> Iterable['Room']:
+ return _key_room_dict.values()
+
+
def get_room(room_key: sdk_models.RoomKey):
return _key_room_dict.get(room_key, None)
@@ -128,14 +134,15 @@ def _get_or_add_room(room_key: sdk_models.RoomKey, room_id):
if room is None:
if room_id is None:
raise TypeError('room_id is None')
- room = _key_room_dict[room_id] = Room(room_key, room_id)
- # TODO 打开房间窗口
+ room = _key_room_dict[room_key] = Room(room_key, room_id)
+ pub.sendMessage('add_room', room_key=room_key)
return room
def _del_room(room_key: sdk_models.RoomKey):
- _key_room_dict.pop(room_key, None)
- # TODO 关闭房间窗口
+ room = _key_room_dict.pop(room_key, None)
+ if room is not None:
+ pub.sendMessage('del_room', room_key=room_key)
@dataclasses.dataclass
@@ -177,44 +184,77 @@ class Room:
self._interact_uids: Set[str] = set()
self._total_paid_price = 0
+ @property
+ def room_key(self):
+ return self._room_key
+
+ @property
+ def room_id(self):
+ return self._room_id
+
+ @property
+ def super_chats(self):
+ return self._super_chats
+
+ @property
+ def gifts(self):
+ return self._gifts
+
+ @property
+ def uid_paid_user_dict(self):
+ return self._uid_paid_user_dict
+
+ @property
+ def danmaku_num(self):
+ return self._danmaku_num
+
+ @property
+ def interact_uids(self):
+ return self._interact_uids
+
+ @property
+ def total_paid_price(self):
+ return self._total_paid_price
+
def add_danmaku(self, uid):
self._danmaku_num += 1
+ pub.sendMessage('room_data_change.danmaku_num', room=self, value=self._danmaku_num)
+
+ self._add_interact_uid(uid)
+
+ def _add_interact_uid(self, uid):
+ if uid in self._interact_uids:
+ return
+
self._interact_uids.add(uid)
+ pub.sendMessage(
+ 'room_data_change.interact_uids',
+ room=self,
+ value=self._interact_uids,
+ index=uid,
+ is_new=True,
+ )
def add_super_chat(self, super_chat: SuperChatRecord):
self._super_chats.append(super_chat)
+ pub.sendMessage(
+ 'room_data_change.super_chats',
+ room=self,
+ value=self._super_chats,
+ index=len(self._super_chats) - 1,
+ is_new=True,
+ )
+
self._add_user_paid_price(PaidUserRecord(
uid=super_chat.uid,
name=super_chat.author_name,
price=super_chat.price,
))
- self._danmaku_num += 1
- self._interact_uids.add(super_chat.uid)
+
self._total_paid_price += super_chat.price
+ pub.sendMessage('room_data_change.total_paid_price', room=self, value=self._total_paid_price)
- def add_gift(self, gift: GiftRecord):
- # 尝试合并
- is_merged = False
- min_time_to_merge = gift.time - datetime.timedelta(seconds=10)
- for old_gift in reversed(self._gifts):
- if old_gift.time < min_time_to_merge:
- break
- if old_gift.uid == gift.uid and old_gift.gift_name == gift.gift_name:
- old_gift.num += gift.num
- old_gift.price += gift.price
- is_merged = True
- break
-
- if not is_merged:
- self._gifts.append(gift)
- if gift.price > 0.:
- self._add_user_paid_price(PaidUserRecord(
- uid=gift.uid,
- name=gift.author_name,
- price=gift.price,
- ))
- self._interact_uids.add(gift.uid)
- self._total_paid_price += gift.price
+ self.add_danmaku(super_chat.uid)
def _add_user_paid_price(self, paid_user: PaidUserRecord):
old_paid_user = self._uid_paid_user_dict.get(paid_user.uid, None)
@@ -224,4 +264,53 @@ class Room:
name=paid_user.name,
price=0,
)
+ is_new = True
+ else:
+ is_new = False
old_paid_user.price += paid_user.price
+
+ pub.sendMessage(
+ 'room_data_change.uid_paid_user_dict',
+ room=self,
+ value=self._uid_paid_user_dict,
+ index=paid_user.uid,
+ is_new=is_new,
+ )
+
+ def add_gift(self, gift: GiftRecord):
+ # 尝试合并
+ is_merged = False
+ min_time_to_merge = gift.time - datetime.timedelta(seconds=10)
+ index = len(self._gifts)
+ for index in range(len(self._gifts) - 1, -1, -1):
+ old_gift = self._gifts[index]
+
+ if old_gift.time < min_time_to_merge:
+ break
+ if old_gift.uid == gift.uid and old_gift.gift_name == gift.gift_name:
+ old_gift.num += gift.num
+ old_gift.price += gift.price
+ is_merged = True
+ break
+ if not is_merged:
+ index = len(self._gifts)
+ self._gifts.append(gift)
+ pub.sendMessage(
+ 'room_data_change.gifts',
+ room=self,
+ value=self._gifts,
+ index=index,
+ is_new=not is_merged,
+ )
+
+ if gift.price > 0.:
+ self._add_user_paid_price(PaidUserRecord(
+ uid=gift.uid,
+ name=gift.author_name,
+ price=gift.price,
+ ))
+
+ self._add_interact_uid(gift.uid)
+
+ self._total_paid_price += gift.price
+ pub.sendMessage('room_data_change.total_paid_price', room=self, value=self._total_paid_price)
diff --git a/plugins/native-ui/main.pyw b/plugins/native-ui/main.pyw
index 1d79add..c4a29be 100755
--- a/plugins/native-ui/main.pyw
+++ b/plugins/native-ui/main.pyw
@@ -5,19 +5,16 @@ import logging.handlers
import os
import signal
import sys
-from typing import *
import wx
-import wxasync
import blcsdk
import config
import listener
+import ui.app
logger = logging.getLogger('native-ui')
-app: Optional[wxasync.WxAsyncApp] = None
-
async def main():
try:
@@ -37,7 +34,7 @@ async def init():
if not blcsdk.is_sdk_version_compatible():
raise RuntimeError('SDK version is not compatible')
- init_ui()
+ ui.app.init()
await listener.init()
@@ -57,6 +54,7 @@ def init_signal_handlers():
def start_shut_down(*_args):
+ app = wx.GetApp()
if app is not None:
app.ExitMainLoop()
else:
@@ -78,14 +76,9 @@ def init_logging():
)
-def init_ui():
- global app
- app = wxasync.WxAsyncApp(clearSigInt=False)
-
-
async def run():
logger.info('Running event loop')
- await app.MainLoop()
+ await wx.GetApp().MainLoop()
logger.info('Start to shut down')
diff --git a/plugins/native-ui/requirements.txt b/plugins/native-ui/requirements.txt
index e8c6f1b..43c69b2 100644
--- a/plugins/native-ui/requirements.txt
+++ b/plugins/native-ui/requirements.txt
@@ -1,2 +1,3 @@
+PyPubSub==4.0.3
wxasync==0.49
wxPython==4.2.1
diff --git a/plugins/native-ui/ui/app.py b/plugins/native-ui/ui/app.py
new file mode 100644
index 0000000..2b4e096
--- /dev/null
+++ b/plugins/native-ui/ui/app.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+import logging
+from typing import *
+
+import pubsub.pub as pub
+import wxasync
+
+import blcsdk.models as sdk_models
+import listener
+import ui.room_frame
+
+logger = logging.getLogger('native-ui.' + __name__)
+
+_app: Optional['App'] = None
+
+
+def init():
+ global _app
+ _app = App()
+
+
+class App(wxasync.WxAsyncApp):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, clearSigInt=False, **kwargs)
+ self.SetExitOnFrameDelete(False)
+
+ self._key_room_frame_dict: Dict[sdk_models.RoomKey, ui.room_frame.RoomFrame] = {}
+
+ def OnInit(self):
+ 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')
+ return True
+
+ def _on_add_room(self, room_key: sdk_models.RoomKey):
+ if room_key in self._key_room_frame_dict:
+ return
+
+ room_frame = self._key_room_frame_dict[room_key] = ui.room_frame.RoomFrame(None, room_key)
+ room_frame.Show()
+
+ def _on_del_room(self, room_key: sdk_models.RoomKey):
+ room_frame = self._key_room_frame_dict.pop(room_key, None)
+ if room_frame is not None:
+ room_frame.Close(True)
+
+ 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)
diff --git a/plugins/native-ui/ui/room.py b/plugins/native-ui/ui/room.py
deleted file mode 100644
index 9e0e600..0000000
--- a/plugins/native-ui/ui/room.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-import designer.ui_base
-
-
-class RoomFrame(designer.ui_base.RoomFrameBase):
- def __init__(self, parent):
- super().__init__(parent)
-
- self.chat_web_view.LoadURL('http://localhost:12450/room/test?minGiftPrice=0&showGiftName=true&relayMessagesByServer=true&lang=zh')
- self.paid_web_view.LoadURL('http://localhost:12450/room/test?showDanmaku=false&showGiftName=true&relayMessagesByServer=true&lang=zh')
diff --git a/plugins/native-ui/ui/room_frame.py b/plugins/native-ui/ui/room_frame.py
new file mode 100644
index 0000000..64ae758
--- /dev/null
+++ b/plugins/native-ui/ui/room_frame.py
@@ -0,0 +1,185 @@
+# -*- coding: utf-8 -*-
+import datetime
+import logging
+import urllib.parse
+from typing import *
+
+import pubsub.pub as pub
+import wx
+
+import blcsdk
+import blcsdk.models as sdk_models
+import designer.ui_base
+import listener
+
+logger = logging.getLogger('native-ui.' + __name__)
+
+
+class RoomFrame(designer.ui_base.RoomFrameBase):
+ def __init__(self, parent, room_key: sdk_models.RoomKey):
+ super().__init__(parent)
+ self._room_key = room_key
+
+ 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}')
+
+ room_params = {'minGiftPrice': 0, 'showGiftName': 'true'}
+ self.chat_web_view.LoadURL(self._get_room_url(room_params))
+ room_params['showDanmaku'] = 'false'
+ self.paid_web_view.LoadURL(self._get_room_url(room_params))
+
+ 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)
+
+ pub.subscribe(self._on_super_chats_change, 'room_data_change.super_chats')
+ pub.subscribe(self._on_gifts_change, 'room_data_change.gifts')
+ pub.subscribe(self._on_uid_paid_user_dict_change, 'room_data_change.uid_paid_user_dict')
+ pub.subscribe(self._on_simple_statistics_change, 'room_data_change.danmaku_num')
+ pub.subscribe(self._on_simple_statistics_change, 'room_data_change.interact_uids')
+ pub.subscribe(self._on_simple_statistics_change, 'room_data_change.total_paid_price')
+
+ def _get_room_url(self, params: dict):
+ params = params.copy()
+ params['roomKeyType'] = self._room_key.type.value
+ params['relayMessagesByServer'] = 'true'
+
+ query = '&'.join(
+ f'{urllib.parse.quote_plus(key)}={urllib.parse.quote_plus(str(value))}'
+ for key, value in params.items()
+ )
+ blc_port = blcsdk.get_blc_port()
+ encoded_room_key_value = urllib.parse.quote_plus(str(self._room_key.value))
+ url = f'http://localhost:{blc_port}/room/{encoded_room_key_value}?{query}'
+ return url
+
+ #
+ # UI事件
+ #
+
+ def _on_close(self, event):
+ pub.sendMessage('room_frame_close', room_key=self._room_key)
+ super()._on_close(event)
+
+ def _on_config_button_click(self, event):
+ # TODO WIP
+ dialog = designer.ui_base.RoomConfigDialogBase(self)
+ dialog.Show()
+
+ def _on_stay_on_top_button_toggle(self, event: wx.CommandEvent):
+ style = self.GetWindowStyle()
+ if event.IsChecked():
+ style |= wx.STAY_ON_TOP
+ else:
+ style &= ~wx.STAY_ON_TOP
+ self.SetWindowStyle(style)
+
+ def _on_collapse_console_button_click(self, event):
+ window_size = self.GetSize()
+ if self.console_notebook.IsShown():
+ window_size.Scale(0.5, 1)
+ self.console_notebook.Hide()
+ self.collapse_console_button.SetLabelText('<<')
+ else:
+ window_size.Scale(2, 1)
+ self.console_notebook.Show()
+ self.collapse_console_button.SetLabelText('>>')
+ self.SetSize(window_size)
+ self.Layout()
+
+ #
+ # 模型事件
+ #
+
+ def _on_super_chats_change(self, room: listener.Room, value: List[listener.SuperChatRecord], index, is_new): # noqa
+ super_chat = value[index]
+ col_texts = [
+ self._format_time(super_chat.time),
+ super_chat.author_name,
+ str(super_chat.price),
+ super_chat.content,
+ ]
+ self._update_list_ctrl(self.super_chat_list, index, is_new, col_texts)
+
+ @staticmethod
+ def _format_time(time: datetime.datetime):
+ return time.strftime('%H:%M')
+
+ def _update_list_ctrl(self, list_ctrl: wx.ListCtrl, item_data: int, is_new, col_texts: List[str]):
+ if is_new:
+ row_index = list_ctrl.Append(col_texts)
+ list_ctrl.SetItemData(row_index, item_data)
+
+ self._maybe_scroll_list_ctrl_to_bottom(list_ctrl)
+ return
+
+ for row_index in range(list_ctrl.GetItemCount() - 1, -1, -1):
+ if list_ctrl.GetItemData(row_index) != item_data:
+ continue
+ for col_index, text in enumerate(col_texts):
+ list_ctrl.SetItem(row_index, col_index, text)
+ break
+
+ @staticmethod
+ def _maybe_scroll_list_ctrl_to_bottom(list_ctrl: wx.ListCtrl):
+ """如果原来就在底端则滚动到底端"""
+ last_row_index = list_ctrl.GetItemCount() - 1
+ if last_row_index < 0:
+ return
+
+ # 没有找到更简单的方法
+ list_height = list_ctrl.GetClientSize().GetHeight() * list_ctrl.GetContentScaleFactor()
+ last_row_rect = list_ctrl.GetItemRect(max(last_row_index, 0))
+ height_to_bottom = last_row_rect.GetBottom() - list_height
+ 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
+ gift = value[index]
+ col_texts = [
+ self._format_time(gift.time),
+ gift.author_name,
+ gift.gift_name,
+ str(gift.num),
+ str(gift.price),
+ ]
+ self._update_list_ctrl(self.gift_list, index, is_new, col_texts)
+
+ def _on_uid_paid_user_dict_change(
+ self, room: listener.Room, value: Dict[str, listener.PaidUserRecord], index, is_new # noqa
+ ):
+ item_data = self._uid_to_paid_user_item_data.get(index, None)
+ if item_data is None:
+ item_data = self._uid_to_paid_user_item_data[index] = self._next_paid_user_item_data
+ self._next_paid_user_item_data += 1
+
+ paid_user = value[index]
+ col_texts = [
+ paid_user.name,
+ str(paid_user.price),
+ ]
+ self._update_list_ctrl(self.paid_user_list, item_data, is_new, col_texts)
+
+ def _on_simple_statistics_change(self, room: listener.Room, value=None, index=None, is_new=None): # noqa
+ text = f'总弹幕数:{room.danmaku_num} 互动用户数:{len(room.interact_uids)} 总付费:{room.total_paid_price} 元'
+ self.statistics_text.SetLabelText(text)