android statusbar add button key

本文详细解析了Android系统UI中状态栏与通知栏的XML布局文件,包括导航栏按钮如返回键、主页键等的具体配置参数,以及日期显示、设置按钮和清除所有通知按钮的位置布局。

http://blog.youkuaiyun.com/androiddeveloper_lee/article/details/8900005

http://blog.youkuaiyun.com/zhou123shao/article/details/7721364

(没有图标,我将recentapp当作音量减)

framework/base/packages/systemUI/res/layout/startus_bar.xml


        <!-- navigation controls in statusbar by baihui-->
        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
            android:layout_width="@dimen/navigation_key_width"
            android:layout_height="match_parent"
            android:src="@drawable/ic_sysbar_back"
            systemui:keyCode="4"
            android:contentDescription="@string/accessibility_back"
            systemui:glowBackground="@drawable/ic_sysbar_highlight"
            />
                <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
                    android:layout_width="@dimen/navigation_key_width"
                    android:layout_height="match_parent"
                    android:src="@drawable/ic_sysbar_home"
                    systemui:keyCode="3"
                    android:contentDescription="@string/accessibility_home"
                    systemui:glowBackground="@drawable/ic_sysbar_highlight"
                    />
                <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps"
                    android:layout_width="@dimen/navigation_key_width"
                    android:layout_height="match_parent"
                    android:src="@drawable/ic_sysbar_recent"
            systemui:keyCode="25"
                    android:contentDescription="@string/accessibility_recent"
                    systemui:glowBackground="@drawable/ic_sysbar_highlight"
                    />
                <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
                    android:layout_width="@dimen/navigation_menu_key_width"
                    android:layout_height="match_parent"
                    android:src="@drawable/ic_sysbar_menu"
                    systemui:keyCode="82"
                    android:visibility="visible"
                    android:contentDescription="@string/accessibility_menu"
                    systemui:glowBackground="@drawable/ic_sysbar_highlight"

                    />




recentapp的keycode是187。音量加是24


    <RelativeLayout android:background="@drawable/notification_header_bg" android:paddingTop="3.0dip" android:paddingRight="3.0dip" android:paddingBottom="5.0dip" android:layout_width="fill_parent" android:layout_height="52.0dip">
        <com.android.systemui.statusbar.policy.DateView 
        android:textAppearance="@style/TextAppearance.StatusBar.Date" 
        android:gravity="left|center" android:id="@id/date" 
        android:paddingLeft="16.0dip" android:layout_width="wrap_content" 
        android:layout_height="fill_parent" android:singleLine="true" 
        android:layout_alignParentLeft="true" />
        <ImageView android:id="@id/settings_button" android:paddingLeft="8.0dip" 
       android:paddingRight="8.0dip" android:layout_width="wrap_content" 
            android:layout_height="fill_parent" android:src="@drawable/ic_notify_quicksettings" android:layout_toRightOf="@id/date" android:contentDescription="@string/accessibility_settings_button" />
        <ImageView android:id="@id/clear_all_button" android:paddingLeft="8.0dip" android:paddingRight="8.0dip" android:layout_width="wrap_content" android:layout_height="fill_parent" android:src="@drawable/ic_notify_clear" android:layout_alignParentRight="true" android:contentDescription="@string/accessibility_clear_all" />
    </RelativeLayout>

    <RelativeLayout android:background="@drawable/notification_header_bg" android:paddingTop="3.0dip" android:paddingRight="3.0dip" android:paddingBottom="5.0dip" android:layout_width="fill_parent" android:layout_height="52.0dip">
        <com.android.systemui.statusbar.policy.DateView android:textAppearance="@style/TextAppearance.StatusBar.Date" android:gravity="left|center" android:id="@id/date" android:paddingLeft="16.0dip" android:layout_width="wrap_content" android:layout_height="fill_parent" android:singleLine="true" android:layout_alignParentLeft="true" />
        <ImageView android:id="@id/settings_button" android:paddingLeft="8.0dip" android:paddingRight="4.0dip" android:layout_width="wrap_content" android:layout_height="fill_parent" android:src="@drawable/ic_notify_quicksettings" android:layout_toRightOf="@id/date" android:contentDescription="@string/accessibility_settings_button" />
        <com.android.systemui.statusbar.policy.KeyButtonView android:paddingLeft="4.0dip" android:paddingRight="4.0dip" android:id="@id/recent_apps" android:layout_width="wrap_content" android:layout_height="fill_parent" android:src="@drawable/ic_sysbar_recent" android:layout_toRightOf="@id/settings_button" android:contentDescription="@string/accessibility_recent" systemui:keyCode="187" systemui:glowBackground="@drawable/ic_sysbar_highlight" />
        <ImageView android:id="@id/clear_all_button" android:paddingLeft="8.0dip" android:paddingRight="8.0dip" android:layout_width="wrap_content" android:layout_height="fill_parent" android:src="@drawable/ic_notify_clear" android:layout_alignParentRight="true" android:contentDescription="@string/accessibility_clear_all" />

<think>这个错误提示表明在设置状态栏文本时,使用的索引超出了状态栏字段的数量。在代码中,我们创建了一个有5个字段的状态栏,但是在更新状态栏时,我们可能尝试设置超过5个字段的文本(索引必须从0到4)。 查看代码中创建状态栏的部分: self.status_bar = self.CreateStatusBar(5) # 5个状态栏字段 self.status_bar.SetStatusWidths([-1, 100, 100, 100, 100]) 然后,在update_status方法中,我们为每个Tab生成一个状态文本,并且还有一个额外的“Ready”状态(放在索引0)。由于有5个Tab,所以我们生成了6个状态文本(索引0到5),但是状态栏只有5个字段(索引0到4)。因此,当循环设置状态文本时,当i=5会触发错误,因为状态栏字段索引最大为4。 解决方案:我们需要确保状态文本的个数不超过状态栏的字段数。在我们的设计中,状态栏字段数等于Tab数量(5个)加1(第一个字段用于显示通用状态)?实际上,我们创建了5个字段,第一个字段用于显示“Ready”,后面4个字段分别对应5个Tab?这显然不对,因为5个字段对应5个Tab,但第一个字段已经被“Ready”占用,那么后面的4个字段无法对应5个Tab。 实际上,我们设计的状态栏字段数量是5个,而我们有5个Tab,所以我们可以这样分配: 第0个字段:固定显示“Ready”等信息 第1-4个字段:分别对应5个Tab中的4个?不对,5个Tab需要5个字段。 因此,我们应该将第一个字段也用于一个Tab,这样5个字段对应5个Tab。但是这样就没有地方显示“Ready”了。或者我们可以改变设计:只显示5个Tab的统计信息,不显示“Ready”。或者我们使用6个字段,第一个字段用于通用信息,后面5个用于Tab。 但是,在创建状态栏时,我们指定了5个字段,并且设置了每个字段的宽度: self.status_bar.SetStatusWidths([-1, 100, 100, 100, 100]) 这意味着我们希望第一个字段宽度自适应,后面4个字段宽度100。但我们需要5个Tab的统计信息,因此我们需要6个字段?不对,因为我们的Tab只有5个,所以我们需要5个字段显示统计信息,再加上一个字段显示状态,总共6个字段。 因此,修改如下: 1. 创建状态栏时,指定6个字段: self.status_bar = self.CreateStatusBar(6) self.status_bar.SetStatusWidths([-1, 100, 100, 100, 100, 100]) # 6个字段 2. 在update_status方法中,我们生成6个状态文本(第一个是“Ready”,后面5个是每个Tab的统计),然后循环设置6个字段。 但是,注意我们之前代码中: status_texts = ["Ready"] for i, port in enumerate(self.tabs.keys()): ... # 添加5个统计文本 这样status_texts就有6个元素:索引0到5。 然后循环设置: for i, text in enumerate(status_texts): self.status_bar.SetStatusText(text, i) 这样i从0到5,而状态栏字段索引0到5,共6个,就不会越界。 但是,我们代码中原来只有5个字段,所以需要修改创建状态栏的代码。另外,在设置状态栏宽度时也需要6个值。 然而,我们也可以改变思路:不显示“Ready”,而是把5个字段全部用于Tab统计。这样就不需要第一个字段了。那么我们可以: status_texts = [] for port in self.tabs.keys(): ... # 为每个Tab生成统计文本,共5个 for i, text in enumerate(status_texts): self.status_bar.SetStatusText(text, i) 然后,创建状态栏时还是5个字段,宽度设置也保持5个值。 根据需求,状态栏显示每个Tab的收发统计,所以我们可以不用“Ready”状态。因此,我选择第二种方式:去掉“Ready”,只显示5个Tab的统计。 修改update_status方法如下: status_texts = [] for port in self.tabs.keys(): if port == 8005: stat = f"Tx:{self.tx_count} Rx:{self.rx_stats.get(8005,0)}" else: stat = f"Rx:{self.rx_stats.get(port,0)}" status_texts.append(stat) for i, text in enumerate(status_texts): if i < self.status_bar.GetFieldsCount(): # 确保索引不越界 self.status_bar.SetStatusText(text, i) 同时,在创建状态栏的时候,我们创建5个字段: self.status_bar = self.CreateStatusBar(5) self.status_bar.SetStatusWidths([-1, 100, 100, 100, 100]) # 5个字段,第一个自适应,后面4个100宽度 但是注意,我们可能有5个字段,但是我们的status_texts也是5个元素,所以循环i从0到4,不会越界。 另外,为了使布局合理,我们将第一个字段设置为自适应宽度(-1),后面的字段宽度100。这样第一个Tab的统计信息会占据更多空间(因为可能有更长的文本),而后面四个固定宽度。 但是,我们也可以根据实际需要调整宽度,比如所有字段都自适应,或者固定宽度。 修改后的完整update_status方法: def update_status(self, event): status_texts = [] # 按照Tab的顺序(端口号顺序)生成统计文本 ports = list(self.tabs.keys()) # 顺序为[8001,8002,8003,8004,8005] for port in ports: if port == 8005: stat = f"Port{port}: Tx:{self.tx_count} Rx:{self.rx_stats.get(port,0)}" else: stat = f"Port{port}: Rx:{self.rx_stats.get(port,0)}" status_texts.append(stat) # 设置状态栏文本 for i in range(self.status_bar.GetFieldsCount()): if i < len(status_texts): self.status_bar.SetStatusText(status_texts[i], i) else: self.status_bar.SetStatusText("", i) 这样修改后,就不会出现越界错误。 但是,我们还需要注意:状态栏的字段数量必须和status_texts的数量一致(即5个)。由于我们在创建状态栏时指定了5个字段,而status_texts也是5个元素,所以没有问题。 然而,为了代码健壮性,我们在设置状态栏文本时,使用循环字段数,并用条件判断防止访问status_texts越界。 下面我们修改代码,并重新运行测试。 另外,状态栏的字段宽度设置:第一个字段我们设置为-1(自适应),后面四个100。如果第一个字段的文本太长,可能会挤压后面的空间。我们可以考虑将每个字段都设置为固定宽度,或者根据内容调整。这里为了简单,我们保持原样。 修改后的代码部分: # 状态栏 self.status_bar = self.CreateStatusBar(5) # 5个状态栏字段 self.status_bar.SetStatusWidths([-1, 100, 100, 100, 100]) 注意:SetStatusWidths的参数是一个包含5个整数的列表。 如果仍然出现字段索引错误,请检查status_texts的长度是否大于字段数(5),或者循环索引是否超过4。 由于我们修改了update_status方法,去掉了第一个“Ready”状态,并且只生成5个状态文本,所以应该不会再出现越界。 如果问题仍然存在,可以在循环中加入判断: for i in range(self.status_bar.GetFieldsCount()): if i < len(status_texts): self.status_bar.SetStatusText(status_texts[i], i) else: self.status_bar.SetStatusText("", i) 这样即使status_texts少于字段数,多余的字段会被清空。 另外,在初始化状态栏时,我们也可以设置每个字段的初始文本: for i in range(5): self.status_bar.SetStatusText("", i) 这样初始状态为空。 修改后的完整update_status方法: def update_status(self, event): ports = list(self.tabs.keys()) # 顺序为[8001,8002,8003,8004,8005] status_texts = [] for port in ports: if port == 8005: text = f"Port{port}: Tx:{self.tx_count} Rx:{self.rx_stats.get(port,0)}" else: text = f"Port{port}: Rx:{self.rx_stats.get(port,0)}" status_texts.append(text) # 确保状态栏每个字段都有文本(不超过字段数量) for i in range(self.status_bar.GetFieldsCount()): if i < len(status_texts): self.status_bar.SetStatusText(status_texts[i], i) else: # 如果状态字段多于Tab,则清空多余字段 self.status_bar.SetStatusText("", i) 现在,我们重新运行程序,应该不会出现断言错误。 如果问题依旧,请仔细检查tab的数量和状态栏字段数量的一致性。我们创建了5个Tab(端口8001-8005),状态栏字段也是5个。 注意:在创建状态栏字段时,我们用了5个字段,而status_texts也是5个元素,所以循环i从0到4,设置状态栏字段0到4,不会越界。 因此,最终的修改方案如上。 由于我们无法在此环境中运行,请根据实际情况调整。如果还有其他错误,请提供详细信息。 为了完整起见,下面给出修改后的UDPMonitorFrame类的__init__方法中关于状态栏的部分,以及update_status方法: 在__init__方法中: # 状态栏 self.status_bar = self.CreateStatusBar(5) # 5个状态栏字段 self.status_bar.SetStatusWidths([-1, 100, 100, 100, 100]) 以及update_status方法: def update_status(self, event): """更新状态栏统计信息""" ports = list(self.tabs.keys()) # 获取5个端口号,顺序为初始化时的顺序[8001,8002,8003,8004,8005] status_texts = [] for port in ports: if port == 8005: # 对于8005端口,同时显示发送和接收统计 stat_text = f"Port{port}: Tx:{self.tx_count} Rx:{self.rx_stats.get(port,0)}" else: stat_text = f"Port{port}: Rx:{self.rx_stats.get(port,0)}" status_texts.append(stat_text) # 设置状态栏文本,确保不超过字段数 for i in range(self.status_bar.GetFieldsCount()): if i < len(status_texts): self.status_bar.SetStatusText(status_texts[i], i) else: self.status_bar.SetStatusText("", i) 这样修改后,应该可以避免上述错误。 </think>这个错误是由于状态栏字段索引超出范围导致的。问题在于状态栏字段数量与代码中尝试设置的字段索引不匹配。让我修复这个问题并提供完整的解决方案: ```python import wx import socket import threading import time import struct import binascii from collections import deque, defaultdict class ProtocolDecoder: BIT_LEVEL = 0 TV_FORMAT = 1 TLV_FORMAT = 2 def decode(self, data, format_type): try: if format_type == self.BIT_LEVEL: return self._bit_level_decode(data) elif format_type == self.TV_FORMAT: return self._tv_decode(data) elif format_type == self.TLV_FORMAT: return self._tlv_decode(data) except: return {"Error": "Decoding failed"} return {} def _bit_level_decode(self, data): decoded = {} if len(data) >= 1: byte_val = data[0] if isinstance(data[0], int) else ord(data[0]) decoded['Version'] = (byte_val & 0xF0) >> 4 decoded['Type'] = byte_val & 0x0F return decoded def _tv_decode(self, data): decoded = {} if len(data) >= 5: t = data[0] if isinstance(data[0], int) else ord(data[0]) v = int.from_bytes(data[1:5], byteorder='big') decoded[f'Type-{t}'] = v return decoded def _tlv_decode(self, data): decoded = {} pos = 0 while pos < len(data): if pos + 2 > len(data): break tag = data[pos] if isinstance(data[pos], int) else ord(data[pos]) length = data[pos+1] if isinstance(data[pos+1], int) else ord(data[pos+1]) pos += 2 if pos + length > len(data): break value = data[pos:pos+length] decoded[f'Tag-{tag}'] = value.hex() pos += length return decoded class UDPMonitorTab(wx.Panel): def __init__(self, parent, port, max_lines=200): super().__init__(parent) self.port = port self.max_lines = max_lines self.message_queue = deque(maxlen=max_lines) self.paused = False self.rx_count = 0 sizer = wx.BoxSizer(wx.HORIZONTAL) self.msg_text = wx.TextCtrl(self, style=wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL|wx.TE_RICH2) sizer.Add(self.msg_text, 1, wx.EXPAND|wx.ALL, 5) self.tree = wx.TreeCtrl(self, style=wx.TR_DEFAULT_STYLE|wx.TR_HIDE_ROOT) sizer.Add(self.tree, 1, wx.EXPAND|wx.ALL, 5) self.SetSizer(sizer) self.decoder = ProtocolDecoder() self.root = self.tree.AddRoot("Root") self.msg_text.Bind(wx.EVT_LEFT_DOWN, self.on_pause) self.msg_text.Bind(wx.EVT_SET_FOCUS, self.on_focus) def on_pause(self, event): self.paused = not self.paused event.Skip() def on_focus(self, event): """当文本框获得焦点时自动滚动到底部""" if not self.paused: self.msg_text.SetInsertionPointEnd() event.Skip() def add_message(self, message, direction="RX"): timestamp = time.strftime('%H:%M:%S') colored_msg = f"[{timestamp}] [{direction}] {message}" self.message_queue.append((direction, colored_msg)) if not self.paused: self.refresh_display() self.rx_count += 1 def refresh_display(self): self.msg_text.Clear() default_style = wx.TextAttr() for direction, msg in self.message_queue: self.msg_text.SetDefaultStyle(default_style) if direction == "TX": self.msg_text.SetDefaultStyle(wx.TextAttr(wx.Colour(0, 0, 255))) # 蓝色表示发送 elif direction == "RX": self.msg_text.SetDefaultStyle(wx.TextAttr(wx.Colour(0, 128, 0))) # 绿色表示接收 self.msg_text.AppendText(msg + '\n') # 滚动到底部 if not self.paused: self.msg_text.SetInsertionPointEnd() def parse_message(self, data): self.tree.DeleteChildren(self.root) # 尝试所有解码格式 formats = [self.decoder.BIT_LEVEL, self.decoder.TV_FORMAT, self.decoder.TLV_FORMAT] found_valid = False for fmt in formats: decoded = self.decoder.decode(data, fmt) if decoded: found_valid = True format_name = ["Bit-Level", "TV Format", "TLV Format"][fmt] fmt_node = self.tree.AppendItem(self.root, f"Format: {format_name}") for key, value in decoded.items(): child = self.tree.AppendItem(fmt_node, f"{key}: {value}") break if not found_valid: error_node = self.tree.AppendItem(self.root, "Unable to decode message") hex_node = self.tree.AppendItem(error_node, f"Hex: {data.hex()}") len_node = self.tree.AppendItem(error_node, f"Length: {len(data)} bytes") self.tree.ExpandAll() class UDPMonitorFrame(wx.Frame): def __init__(self): super().__init__(None, title="UDP Monitor", size=(1200, 800)) self.tx_count = 0 self.rx_stats = defaultdict(int) # 创建发送socket self.send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.send_sock.bind(('0.0.0.0', 8000)) # 创建接收socket self.recv_socks = {} self.recv_threads = {} self.ports = [8001, 8002, 8003, 8004, 8005] # 创建UI self.notebook = wx.Notebook(self) self.tabs = {} for port in self.ports: tab = UDPMonitorTab(self.notebook, port) self.tabs[port] = tab self.notebook.AddPage(tab, f"Port {port}") # 控制面板 control_panel = wx.Panel(self) grid = wx.GridBagSizer(10, 10) # IP和端口输入 grid.Add(wx.StaticText(control_panel, label="Destination IP:"), (0, 0), flag=wx.ALIGN_CENTER_VERTICAL) self.ip_entry = wx.TextCtrl(control_panel, value="127.0.0.1") grid.Add(self.ip_entry, (0, 1)) grid.Add(wx.StaticText(control_panel, label="Port:"), (0, 2), flag=wx.ALIGN_CENTER_VERTICAL) self.port_entry = wx.TextCtrl(control_panel, value="8000", size=(80, -1)) grid.Add(self.port_entry, (0, 3)) # 消息输入 grid.Add(wx.StaticText(control_panel, label="Message:"), (1, 0), flag=wx.ALIGN_CENTER_VERTICAL) self.msg_entry = wx.TextCtrl(control_panel, style=wx.TE_PROCESS_ENTER) grid.Add(self.msg_entry, (1, 1), (1, 3), wx.EXPAND) # 按钮 btn_sizer = wx.BoxSizer(wx.HORIZONTAL) self.send_btn = wx.Button(control_panel, label="Send Once") self.start_btn = wx.Button(control_panel, label="Start Periodic") self.stop_btn = wx.Button(control_panel, label="Stop", enabled=False) self.interval_entry = wx.TextCtrl(control_panel, value="1000", size=(60, -1)) btn_sizer.Add(self.send_btn, 0, wx.RIGHT, 5) btn_sizer.Add(self.start_btn, 0, wx.RIGHT, 5) btn_sizer.Add(self.stop_btn, 0, wx.RIGHT, 10) btn_sizer.Add(wx.StaticText(control_panel, label="Interval (ms):"), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 5) btn_sizer.Add(self.interval_entry, 0) grid.Add(btn_sizer, (2, 0), (1, 4), wx.EXPAND|wx.TOP, 5) control_panel.SetSizer(grid) # 修复状态栏问题:确保字段数量匹配 self.status_bar = self.CreateStatusBar(len(self.ports)) status_widths = [-1] * len(self.ports) self.status_bar.SetStatusWidths(status_widths) # 主布局 main_sizer = wx.BoxSizer(wx.VERTICAL) main_sizer.Add(self.notebook, 1, wx.EXPAND) main_sizer.Add(control_panel, 0, wx.EXPAND|wx.ALL, 10) self.SetSizer(main_sizer) # 绑定事件 self.send_btn.Bind(wx.EVT_BUTTON, self.on_send) self.start_btn.Bind(wx.EVT_BUTTON, self.on_start_periodic) self.stop_btn.Bind(wx.EVT_BUTTON, self.on_stop_periodic) self.msg_entry.Bind(wx.EVT_TEXT_ENTER, self.on_send) self.notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.on_tab_change) self.Bind(wx.EVT_CLOSE, self.on_close) # 启动接收线程 for port in self.ports: self.start_receiver(port) # 定时器 self.timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.on_periodic_send, self.timer) # 状态更新定时器 self.stats_timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.update_status, self.stats_timer) self.stats_timer.Start(500) def start_receiver(self, port): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind(('0.0.0.0', port)) sock.settimeout(0.1) # 设置超时防止永久阻塞 self.recv_socks[port] = sock def listener(): while hasattr(self, 'recv_socks') and port in self.recv_socks: try: data, addr = sock.recvfrom(4096) wx.CallAfter(self.process_data, port, data, addr) except socket.timeout: continue except: break thread = threading.Thread(target=listener, daemon=True) thread.start() self.recv_threads[port] = thread def process_data(self, port, data, addr): try: message = data.decode(errors='replace') except: message = data.hex() self.rx_stats[port] += 1 self.tabs[port].add_message(f"{addr[0]}:{addr[1]} - {message}", "RX") # 8005标签页显示所有接收和发送的消息 if port == 8005: self.tabs[8005].add_message(f"{addr[0]}:{addr[1]} - {message}", "RX") # 自动解析当前激活标签页的消息 current_page = self.notebook.GetPage(self.notebook.GetSelection()) if current_page.port == port: current_page.parse_message(data) def on_send(self, event): self.send_message() def on_start_periodic(self, event): try: interval = int(self.interval_entry.GetValue()) if interval < 100: interval = 100 self.timer.Start(interval) self.start_btn.Enable(False) self.stop_btn.Enable(True) except ValueError: wx.MessageBox("Please enter a valid interval value", "Error", wx.OK|wx.ICON_ERROR) def on_stop_periodic(self, event): self.timer.Stop() self.start_btn.Enable(True) self.stop_btn.Enable(False) def on_periodic_send(self, event): self.send_message() def send_message(self): ip = self.ip_entry.GetValue().strip() if not ip: return try: port = int(self.port_entry.GetValue()) message = self.msg_entry.GetValue() if not message: return self.send_sock.sendto(message.encode(), (ip, port)) self.tx_count += 1 # 更新8005标签页 timestamp = time.strftime('%H:%M:%S') self.tabs[8005].add_message(f"{ip}:{port} - {message}", "TX") except Exception as e: wx.LogError(f"Send error: {str(e)}") def on_tab_change(self, event): """Tab切换时恢复滚动并解析第一条消息""" new_idx = event.GetSelection() port = self.ports[new_idx] self.tabs[port].paused = False # 尝试解析当前标签的最后一条消息 if self.tabs[port].message_queue: _, last_msg = self.tabs[port].message_queue[-1] # 这里需要实际的数据,但为了示例我们跳过 def update_status(self, event): """修复状态栏字段匹配问题""" status_texts = [] for idx, port in enumerate(self.ports): if port == 8005: text = f"Port{port}: Tx:{self.tx_count} Rx:{self.rx_stats.get(port,0)}" else: text = f"Port{port}: Rx:{self.rx_stats.get(port,0)}" status_texts.append(text) # 安全设置状态文本 if idx < self.status_bar.GetFieldsCount(): self.status_bar.SetStatusText(text, idx) def on_close(self, event): """关闭时清理资源""" if self.timer.IsRunning(): self.timer.Stop() self.stats_timer.Stop() # 关闭所有socket for sock in self.recv_socks.values(): try: sock.close() except: pass try: self.send_sock.close() except: pass self.Destroy() if __name__ == "__main__": app = wx.App(False) frame = UDPMonitorFrame() frame.Show() app.MainLoop() ``` ### 关键修复说明 1. **状态栏字段匹配问题修复**: ```python # 创建与端口数量匹配的状态栏字段 self.status_bar = self.CreateStatusBar(len(self.ports)) status_widths = [-1] * len(self.ports) self.status_bar.SetStatusWidths(status_widths) # 更新状态时确保索引在范围内 if idx < self.status_bar.GetFieldsCount(): self.status_bar.SetStatusText(text, idx) ``` 2. **增强功能改进**: - 发送消息间隔可配置 - 消息颜色区分(蓝色发送/绿色接收) - 自动尝试多种解码格式 - 更健壮的错误处理 - 消息自动滚动优化 - 十六进制显示无法解码的消息 3. **其他优化**: - 添加了Socket超时处理 - 改进消息解析逻辑 - 添加关闭事件处理 - 更友好的用户界面 - 更好的错误反馈 ### 使用说明 1. **状态栏**: - 每个端口对应一个状态栏字段 - 8005端口显示发送(Tx)和接收(Rx)统计 - 其他端口只显示接收统计 2. **消息显示**: - 点击消息区域暂停/恢复自动滚动 - 切换Tab自动恢复滚动 - 发送消息显示为蓝色,接收为绿色 3. **协议解析**: - 自动尝试Bit/TV/TLV格式解析 - 无法解析时显示十六进制和长度 - 树形结构展示解析结果 4. **发送控制**: - 单次发送:点击"Send Once"或按回车 - 周期发送:设置间隔后点击"Start Periodic" - 停止发送:点击"Stop" ### 扩展建议 1. **解码器扩展**: 继承`ProtocolDecoder`并重写`decode`方法添加自定义协议 2. **消息过滤**: 在`process_data`方法中添加过滤逻辑 3. **持久化**: 添加消息保存功能,使用SQLite或文件存储
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值