mirror of
https://github.com/xfgryujk/blivechat.git
synced 2025-01-13 22:00:15 +08:00
GUI插件添加导出Excel
This commit is contained in:
parent
5cac2bcb59
commit
abbbbb1154
@ -242,7 +242,7 @@
|
||||
</object>
|
||||
<object class="sizeritem" expanded="true">
|
||||
<property name="border">5</property>
|
||||
<property name="flag"></property>
|
||||
<property name="flag">wxLEFT</property>
|
||||
<property name="proportion">0</property>
|
||||
<object class="wxButton" expanded="true">
|
||||
<property name="BottomDockable">1</property>
|
||||
@ -443,7 +443,7 @@
|
||||
<object class="notebookpage" expanded="true">
|
||||
<property name="bitmap"></property>
|
||||
<property name="label">付费消息</property>
|
||||
<property name="select">1</property>
|
||||
<property name="select">0</property>
|
||||
<object class="wxPanel" expanded="true">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
@ -824,7 +824,7 @@
|
||||
<object class="notebookpage" expanded="true">
|
||||
<property name="bitmap"></property>
|
||||
<property name="label">统计</property>
|
||||
<property name="select">0</property>
|
||||
<property name="select">1</property>
|
||||
<object class="wxPanel" expanded="true">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
@ -1021,6 +1021,92 @@
|
||||
<property name="wrap">-1</property>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem" expanded="true">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxALL|wxEXPAND</property>
|
||||
<property name="proportion">0</property>
|
||||
<object class="wxBoxSizer" expanded="true">
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">bSizer11</property>
|
||||
<property name="orient">wxHORIZONTAL</property>
|
||||
<property name="permission">none</property>
|
||||
<object class="sizeritem" expanded="true">
|
||||
<property name="border">5</property>
|
||||
<property name="flag"></property>
|
||||
<property name="proportion">0</property>
|
||||
<object class="wxButton" expanded="true">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="auth_needed">0</property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="bitmap"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="current"></property>
|
||||
<property name="default">0</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="disabled"></property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="drag_accept_files">0</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="focus"></property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="label">导出Excel</property>
|
||||
<property name="margins"></property>
|
||||
<property name="markup">0</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">export_excel_button</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="position"></property>
|
||||
<property name="pressed"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="style"></property>
|
||||
<property name="subclass">; ; forward_declare</property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="validator_data_type"></property>
|
||||
<property name="validator_style">wxFILTER_NONE</property>
|
||||
<property name="validator_type">wxDefaultValidator</property>
|
||||
<property name="validator_variable"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<event name="OnButtonClick">_on_export_excel_button_click</event>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
@ -1959,7 +2045,7 @@
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="id">wxID_OK</property>
|
||||
<property name="label">确定</property>
|
||||
<property name="margins"></property>
|
||||
<property name="markup">0</property>
|
||||
@ -2033,7 +2119,7 @@
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="id">wxID_CANCEL</property>
|
||||
<property name="label">取消</property>
|
||||
<property name="margins"></property>
|
||||
<property name="markup">0</property>
|
||||
|
@ -40,7 +40,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 )
|
||||
bSizer3.Add( self.collapse_console_button, 0, 0, 5 )
|
||||
bSizer3.Add( self.collapse_console_button, 0, wx.LEFT, 5 )
|
||||
|
||||
|
||||
bSizer2.Add( bSizer3, 0, wx.ALL|wx.EXPAND, 5 )
|
||||
@ -64,7 +64,7 @@ class RoomFrameBase ( wx.Frame ):
|
||||
self.paid_panel.SetSizer( bSizer4 )
|
||||
self.paid_panel.Layout()
|
||||
bSizer4.Fit( self.paid_panel )
|
||||
self.console_notebook.AddPage( self.paid_panel, u"付费消息", True )
|
||||
self.console_notebook.AddPage( self.paid_panel, u"付费消息", False )
|
||||
self.super_chat_panel = wx.Panel( self.console_notebook, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
|
||||
bSizer5 = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
@ -103,11 +103,19 @@ class RoomFrameBase ( wx.Frame ):
|
||||
|
||||
bSizer7.Add( self.statistics_text, 0, wx.ALL, 5 )
|
||||
|
||||
bSizer11 = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
self.export_excel_button = wx.Button( self.statistics_panel, wx.ID_ANY, u"导出Excel", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
bSizer11.Add( self.export_excel_button, 0, 0, 5 )
|
||||
|
||||
|
||||
bSizer7.Add( bSizer11, 0, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
|
||||
self.statistics_panel.SetSizer( bSizer7 )
|
||||
self.statistics_panel.Layout()
|
||||
bSizer7.Fit( self.statistics_panel )
|
||||
self.console_notebook.AddPage( self.statistics_panel, u"统计", False )
|
||||
self.console_notebook.AddPage( self.statistics_panel, u"统计", True )
|
||||
|
||||
bSizer1.Add( self.console_notebook, 1, wx.EXPAND, 5 )
|
||||
|
||||
@ -122,6 +130,7 @@ class RoomFrameBase ( wx.Frame ):
|
||||
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 )
|
||||
self.export_excel_button.Bind( wx.EVT_BUTTON, self._on_export_excel_button_click )
|
||||
|
||||
def __del__( self ):
|
||||
pass
|
||||
@ -140,6 +149,9 @@ class RoomFrameBase ( wx.Frame ):
|
||||
def _on_collapse_console_button_click( self, event ):
|
||||
event.Skip()
|
||||
|
||||
def _on_export_excel_button_click( self, event ):
|
||||
event.Skip()
|
||||
|
||||
|
||||
###########################################################################
|
||||
## Class RoomConfigDialogBase
|
||||
|
@ -1,3 +1,4 @@
|
||||
PyPubSub==4.0.3
|
||||
wxasync==0.49
|
||||
wxPython==4.2.1
|
||||
XlsxWriter==3.2.0
|
||||
|
@ -6,6 +6,7 @@ from typing import *
|
||||
|
||||
import pubsub.pub as pub
|
||||
import wx
|
||||
import xlsxwriter.exceptions
|
||||
|
||||
import blcsdk
|
||||
import blcsdk.models as sdk_models
|
||||
@ -107,6 +108,59 @@ class RoomFrame(designer.ui_base.RoomFrameBase):
|
||||
self.SetSize(window_size)
|
||||
self.Layout()
|
||||
|
||||
def _on_export_excel_button_click(self, event):
|
||||
room = listener.get_room(self._room_key)
|
||||
room_str = str(room.room_id) if room is not None else str(self._room_key)
|
||||
cur_time = datetime.datetime.now()
|
||||
time_str = cur_time.strftime('%Y%m%d_%H%M%S')
|
||||
with wx.FileDialog(
|
||||
self,
|
||||
wildcard='Excel 文件 (*.xlsx)|*.xlsx',
|
||||
defaultFile=f'room_{room_str}-{time_str}.xlsx',
|
||||
style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
|
||||
name='导出Excel',
|
||||
) as dialog:
|
||||
if dialog.ShowModal() != wx.ID_OK:
|
||||
return
|
||||
path = dialog.GetPath()
|
||||
|
||||
try:
|
||||
with xlsxwriter.Workbook(path) as workbook:
|
||||
self._write_list_ctrl_to_workbook(self.super_chat_list, workbook, '醒目留言')
|
||||
self._write_list_ctrl_to_workbook(self.gift_list, workbook, '礼物&舰长')
|
||||
self._write_list_ctrl_to_workbook(self.paid_user_list, workbook, '付费用户')
|
||||
|
||||
if room is not None:
|
||||
sheet = workbook.add_worksheet('统计')
|
||||
row_texts = ['总弹幕数', '互动用户数', '总付费']
|
||||
sheet.write_column(0, 0, row_texts)
|
||||
row_texts = [str(room.danmaku_num), str(len(room.interact_uids)), f'{room.total_paid_price:.1f}']
|
||||
sheet.write_column(0, 1, row_texts)
|
||||
|
||||
sheet.set_column_pixels(0, 0, 120)
|
||||
|
||||
except (OSError, xlsxwriter.exceptions.XlsxWriterException) as e:
|
||||
logger.exception('Failed to save excel file:')
|
||||
wx.MessageBox(str(e), '导出Excel失败', wx.OK | wx.ICON_ERROR | wx.CENTRE, self)
|
||||
|
||||
def _write_list_ctrl_to_workbook(self, list_ctrl: wx.ListCtrl, workbook: xlsxwriter.Workbook, sheet_name):
|
||||
sheet = workbook.add_worksheet(sheet_name)
|
||||
for row, col_texts in enumerate(self._list_ctrl_to_col_texts(list_ctrl)):
|
||||
sheet.write_row(row, 0, col_texts)
|
||||
|
||||
col_num = list_ctrl.GetColumnCount()
|
||||
for col in range(col_num):
|
||||
sheet.set_column_pixels(col, col, list_ctrl.GetColumnWidth(col))
|
||||
# sheet.autofit()
|
||||
|
||||
@staticmethod
|
||||
def _list_ctrl_to_col_texts(list_ctrl: wx.ListCtrl):
|
||||
col_num = list_ctrl.GetColumnCount()
|
||||
row_num = list_ctrl.GetItemCount()
|
||||
yield [list_ctrl.GetColumn(col).GetText() for col in range(col_num)]
|
||||
for row in range(row_num):
|
||||
yield [list_ctrl.GetItemText(row, col) for col in range(col_num)]
|
||||
|
||||
#
|
||||
# 模型事件
|
||||
#
|
||||
@ -115,14 +169,16 @@ class RoomFrame(designer.ui_base.RoomFrameBase):
|
||||
if room.room_key != self._room_key:
|
||||
return
|
||||
|
||||
super_chat = value[index]
|
||||
col_texts = [
|
||||
col_texts = self._super_chat_to_col_texts(value[index])
|
||||
self._update_list_ctrl(self.super_chat_list, index, is_new, col_texts)
|
||||
|
||||
def _super_chat_to_col_texts(self, super_chat: listener.SuperChatRecord):
|
||||
return [
|
||||
self._format_time(super_chat.time),
|
||||
super_chat.author_name,
|
||||
f'{super_chat.price:.1f}',
|
||||
super_chat.content,
|
||||
]
|
||||
self._update_list_ctrl(self.super_chat_list, index, is_new, col_texts)
|
||||
|
||||
@staticmethod
|
||||
def _format_time(time: datetime.datetime):
|
||||
@ -161,15 +217,17 @@ class RoomFrame(designer.ui_base.RoomFrameBase):
|
||||
if room.room_key != self._room_key:
|
||||
return
|
||||
|
||||
gift = value[index]
|
||||
col_texts = [
|
||||
col_texts = self._gift_to_col_texts(value[index])
|
||||
self._update_list_ctrl(self.gift_list, index, is_new, col_texts)
|
||||
|
||||
def _gift_to_col_texts(self, gift: listener.GiftRecord):
|
||||
return [
|
||||
self._format_time(gift.time),
|
||||
gift.author_name,
|
||||
gift.gift_name,
|
||||
str(gift.num),
|
||||
f'{gift.price:.1f}',
|
||||
]
|
||||
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
|
||||
@ -182,12 +240,15 @@ class RoomFrame(designer.ui_base.RoomFrameBase):
|
||||
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 = [
|
||||
col_texts = self._paid_user_to_col_texts(value[index])
|
||||
self._update_list_ctrl(self.paid_user_list, item_data, is_new, col_texts)
|
||||
|
||||
@staticmethod
|
||||
def _paid_user_to_col_texts(paid_user: listener.PaidUserRecord):
|
||||
return [
|
||||
paid_user.name,
|
||||
f'{paid_user.price:.1f}',
|
||||
]
|
||||
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
|
||||
if room.room_key != self._room_key:
|
||||
|
Loading…
Reference in New Issue
Block a user