class MyUI:
    def __init__(self):
        self.d_packets_counts = {"TCP": 0, "UDP": 0, "ICMP": 0, "ARP": 0, "Others": 0, "Total": 0}
        self.d_configuration = OrderedDict()
        self.d_configuration["TCP"], self.d_configuration["UDP"], self.d_configuration["ICMP"], self.d_configuration[
            "ARP"], self.d_configuration["Others"], self.d_configuration["Promisc"] = True, True, True, True, True, True

        # 嗅探线程
        self.my_sniffer_thread = MySnifferThread()

        ### 主窗口
        self.root = tkinter.Tk()
        self._initialize_root(self.root)

        ## 菜单栏
        menu_frame = tkinter.Frame(self.root)
        self._initialize_menu(menu_frame)

        ## 功能实现区 Start
        # 列表帧, 包括包数量列表, 包列表区域 #TODO: 本来第一个是 IP 列表区域的,待定
        packets_frame = tkinter.LabelFrame(self.root)
        self._initialize_list_frame(packets_frame)

        # 内容显示帧, 包括图形化显示/其余功能选项区域, 内容显示区域
        content_frame = tkinter.LabelFrame(self.root)
        self._initialize_content_frame(content_frame)
        ## 功能实现区 End

        ## 状态栏, TODO: 没时间写这东西®
        # state_frame = tkinter.Frame(self.root)

        self.root.mainloop()

    def _counts_packets_type(self, packet):
        # 用来更新包数量统计的, 不过这里写得太丑了, 待有序字典和迭代器改写
        if packet.haslayer("TCP"):
            self.d_packets_counts["TCP"] += 1
        elif packet.haslayer("UDP"):
            self.d_packets_counts["UDP"] += 1
        elif packet.haslayer("ICMP"):
            self.d_packets_counts["ICMP"] += 1
        elif packet.haslayer("ARP"):
            self.d_packets_counts["ARP"] += 1
        else:
            self.d_packets_counts["Others"] += 1
        self.d_packets_counts["Total"] += 1

    def _update_packets_num_listbox(self):
        """
        更新左上角的各种包的统计信息
        :return:
        """
        self.packets_num_listbox.delete(0, tkinter.END)  # 先清除原先的数据
        # 下面这个写太丑了, 应该用有序链表搭配迭代器来写的
        self.packets_num_listbox.insert(tkinter.END, "TCP:{}".format(self.d_packets_counts["TCP"]))
        self.packets_num_listbox.insert(tkinter.END, "UDP:{}".format(self.d_packets_counts["UDP"]))
        self.packets_num_listbox.insert(tkinter.END, "ICMP:{}".format(self.d_packets_counts["ICMP"]))
        self.packets_num_listbox.insert(tkinter.END, "ARP:{}".format(self.d_packets_counts["ARP"]))
        self.packets_num_listbox.insert(tkinter.END, "Others:{}".format(self.d_packets_counts["Others"]))
        self.packets_num_listbox.insert(tkinter.END, "Total:{}".format(self.d_packets_counts["Total"]))

    def _update_packets_list(self, listbox):
        # 保存包列表到类变量中
        self.l_packets = list()

        while True:
            # 检查是否有抓到新的包
            if len(l_packets) > len(self.l_packets):
                ## 更新包列表 Start
                # 有新的包, 保存进类变量里, 并且打印到图形界面的包列表区域中
                for index in range(len(self.l_packets), len(l_packets)):
                    print_data = "{:<10}{:<}".format(index, l_packets[index].summary())
                    self.l_packets.append(l_packets[index])
                    listbox.insert(tkinter.END, print_data)
                    # listbox.yview(tkinter.END)  # 自动滚动到最下面
                    ## 更新包列表 End

                    ## 更新包数量列表 Start
                    self._counts_packets_type(l_packets[index])
                    self._update_packets_num_listbox()
                    ## 更新包数量列表 End
                    # listbox.update_idletasks()# 更新组件

    def _stop_sniff_thread(self):
        """
        控制当用户按下 stop capture packets 按钮时的交互
        :return:
        """
        res = self.my_sniffer_thread.is_stopped()
        if res is True:
            tkinter.messagebox.showerror(title="Stop capturing packets", message="嗅探早就停止了")
        elif res is False:
            self.my_sniffer_thread.stop()
            tkinter.messagebox.showinfo(title="Stop capturing packets", message="停止嗅探ing, 可以按 Restart 继续嗅探")
            self.buttons["restart"].configure(state="normal")
        elif res is None:
            tkinter.messagebox.showerror(title="Stop capturing packets", message="并没有嗅探过")

    def _lfilter_func(self, packet):
        """
        根据 self.d_configuration, 配置 sniff 所需要的 lfilter 函数
        :return:
        """
        # TODO: 这里写丑了, 居然需要专门弄个条件分支给 Promisc
        for protocol in self.d_configuration:
            if protocol != "Others":
                if packet.haslayer(protocol) and self.d_configuration[protocol]:
                    return True
                elif packet.haslayer(protocol) and self.d_configuration[protocol]:
                    return False
            elif self.d_configuration["Others"]:
                return True
            elif protocol == "Promisc":
                continue
        return False

    def _start_sniff_thread(self):
        """
        控制当用户按下 start 按钮时的交互
        :return:
        """
        # TODO: 为了减小本人的工作量, 只允许进行一次配置, 且只能在没开始嗅探之前配置...
        self.buttons["options"].configure(state="disabled")

        self.my_sniffer_thread.configurate(lfilter=self._lfilter_func, promisc=int(self.d_configuration["Promisc"]))
        self._initialize_functions_listbox()

        self.my_sniffer_thread.start()
        self.buttons["start"].configure(state="disabled")  # 使得按钮无法再按

    def _restart_sniff_thread(self):
        """
        控制当用户按下 restart 按钮时的交互
        :return:
        """
        self.my_sniffer_thread.restart()
        self.buttons["restart"].configure(state="disabled")

    def _checkbutton_callback(self, name):
        """
        功能是实现配置选项, 如果选中变为未选中,则令对应配置值变为 False
        例如, 原来 self.d_configuration["TCP"] = True, 执行完这个函数之后, self.d_configuration["TCP"] = False
        :param name: "TCP"
        :return:
        """
        self.d_configuration[name] = not self.d_configuration[name]

    def _set_checkbuttons(self):
        """
        放置配置窗口中的各个按钮
        :return:
        """
        for each_button in self.checkbuttons:
            if self.d_configuration[each_button]:
                self.checkbuttons[each_button].select()  # 默认为选中状态
            else:
                self.checkbuttons[each_button].deselect()  # 未选中状态
            self.checkbuttons[each_button].pack()

    def _initialize_configuration_buttons(self, frame):
        """
        初始化配置窗口中的过滤器中的包类型
        :param frame: 包选择过滤帧
        :return:
        """
        self.checkbuttons = OrderedDict()

        # TODO: 这下面这堆写丑了
        ## 过滤协议的选项 Start
        tkinter.Label(frame, text="选择所要保留的包类型以及是否开启混杂模式", anchor="w").pack()
        self.checkbuttons["TCP"] = tkinter.Checkbutton(frame, text="TCP",
                                                       command=lambda: self._checkbutton_callback("TCP"))
        self.checkbuttons["UDP"] = tkinter.Checkbutton(frame, text="UDP",
                                                       command=lambda: self._checkbutton_callback("UDP"))
        self.checkbuttons["ICMP"] = tkinter.Checkbutton(frame, text="ICMP",
                                                        command=lambda: self._checkbutton_callback("ICMP"))
        self.checkbuttons["ARP"] = tkinter.Checkbutton(frame, text="ARP",
                                                       command=lambda: self._checkbutton_callback("ARP"))
        self.checkbuttons["Others"] = tkinter.Checkbutton(frame, text="Others",
                                                          command=lambda: self._checkbutton_callback("Others"))
        ## 过滤协议的选项 End

        ## 其他选项 Start
        # 配置混杂模式选项
        self.checkbuttons["Promisc"] = tkinter.Checkbutton(frame, text="Promisc",
                                                           command=lambda: self._checkbutton_callback("Promisc"))
        self.checkbuttons["Promisc"].pack()
        ## 其他选项 End

        self._set_checkbuttons()  # 放置按钮

    def _configure_options(self):
        # 通过子窗口来进行配置
        sub_window = tkinter.Toplevel()
        sub_window.title("配置窗口")

        ## 包过滤选择帧 Start
        configurations_labelframe = tkinter.LabelFrame(sub_window)

        # 初始化选项复选框
        self._initialize_configuration_buttons(configurations_labelframe)

        configurations_labelframe.pack()
        ## 包过滤选择帧 End

        sub_window.grid()

    def _initialize_menu(self, frame):
        self.buttons = OrderedDict()

        # 创建各种按钮
        self.buttons["start"] = tkinter.Button(frame, text="Start capturing packets", fg="red",
                                               command=self._start_sniff_thread)
        self.buttons["stop"] = tkinter.Button(frame, text="Stop capturing packets", fg="blue",
                                              command=self._stop_sniff_thread)
        self.buttons["restart"] = tkinter.Button(frame, text="Restart current capture", fg="orange", state="disabled",
                                                 command=self._restart_sniff_thread)
        self.buttons["options"] = tkinter.Button(frame, text="Capture options", fg="green",
                                                 command=self._configure_options)

        # 放置控件
        column = 0
        for each_button in self.buttons:
            self.buttons[each_button].grid(row=0, column=column)
            column += 1
        frame.pack()

    def _initialize_root(self, root):
        # 设置窗口大小
        root.geometry("800x640")

        # 设置标题
        root.title("Sniffer By w@tch")

        # 不允许更改窗口大小
        root.resizable(height=False, width=False)

    def _initialize_functions_listbox(self):
        # TODO: 本来想写成功能区的,暂时没时间,所以写成配置显示区好了
        self.functions_listbox.delete(0, tkinter.END)
        for protocol in self.d_configuration:
            self.functions_listbox.insert(tkinter.END, "{}:{}".format(protocol, self.d_configuration[protocol]))

    def _initialize_content_frame(self, frame):
        # 图形化显示/其余功能选项帧
        self.functions_listbox = tkinter.Listbox(frame)
        self.functions_listbox.pack(side=tkinter.LEFT, fill=tkinter.Y)

        ## 内容显示帧 Start
        content_width = 90
        content_frame = tkinter.LabelFrame(frame)

        # 十六进制帧 Start
        hex_text_frame = tkinter.LabelFrame(content_frame)
        self.hex_text = tkinter.Text(hex_text_frame, width=content_width, height=10)
        self.hex_text.bind("<1>", lambda event: self.hex_text.focus_set())  # 使之变为可读且可复制
        self.hex_text.configure(state="disabled")  # 使得用户无法输入
        self.hex_text.grid()
        hex_text_frame.pack(side=tkinter.BOTTOM, fill=tkinter.Y)
        # 十六进制帧 End

        # 包内容详细打印 Start
        contents_text_frame = tkinter.LabelFrame(content_frame)
        self.contents_text = tkinter.Text(contents_text_frame, width=content_width, height=10)
        self.contents_text.bind("<1>", lambda event: self.contents_text.focus_set())  # 使之变为可读且可复制
        self.contents_text.configure(state="disabled")  # 使得用户无法输入
        self.contents_text.grid()
        contents_text_frame.pack(side=tkinter.TOP, fill=tkinter.Y)
        # 包内容详细打印 End

        content_frame.pack(side=tkinter.RIGHT, fill=tkinter.Y)
        ## 内容显示帧 End

        frame.pack()

    def _initialize_packets_area_labels(self, frame):
        labels = dict()
        label_width = 8

        # justify与anchor的区别了:一个用于控制多行的对齐;另一个用于控制整个文本块在Label中的位置
        labels["0"] = tkinter.Label(frame, text="序号", anchor="w", width=label_width)
        labels["1"] = tkinter.Label(frame, text="包概要", anchor="w", width=label_width * 9)
        # 增加一个按钮,要不然实在得自己滚到最底,不方便
        button = tkinter.Button(frame, text="滑至底部", command=lambda: self.packets_listbox.yview(tkinter.END))

        for label in labels:
            labels[label].grid(row=0, column=int(label))

        button.grid(row=0, column=len(labels) - 1)
        frame.grid(row=0, column=0, columnspan=len(labels))

    def _handler_adaptor(self, fun, **kwds):
        # 参考 http://blog.csdn.net/tinym87/article/details/6957438
        '''事件处理函数的适配器,相当于中介,那个event是从那里来的呢,我也纳闷,这也许就是python的伟大之处吧'''
        return lambda event, fun=fun, kwds=kwds: fun(event, **kwds)

    @staticmethod
    def _hexdump(x):
        """
        改造 scapy.utils 库中的 hexdump 为我所用(NND 的库函数要是跟写个返回值我就不用手改了)
        还有另外一种方式,参考 show() 函数,直接打印到字符串了
        :param x:
        :return:
        """
        result = ""
        if type(x) is not str and type(x) is not bytes:
            try:
                x = bytes(x)
            except:
                x = str(x)
        l = len(x)
        i = 0
        while i < l:
            result += "%04x  " % i + " "
            for j in range(16):
                if i + j < l:
                    result += "%02X" % orb(x[i + j]) + " "
                else:
                    result += "  " + " "
                if j % 16 == 7:
                    result += "" + " "
            result += " " + " "
            result += sane_color(x[i:i + 16]) + "\n"
            i += 16
        return result

    def _print_packet_contents(self, event, listbox):
        packet = self.l_packets[listbox.curselection()[0]]
        hex_contents = MyUI._hexdump(packet)

        ## 十六进制面板 Start
        # state="normal",使得我们可以输入内容, 输完内容后改 state 为 disabled 使用户无法输入
        self.hex_text.configure(state="normal")
        self.hex_text.delete("1.0", tkinter.END)  # 清空原先的内容
        self.hex_text.insert(tkinter.END, hex_contents)
        self.hex_text.configure(state="disabled")
        ## 十六进制面板 End

        ## 包详细内容面板 Start
        # 将 show 结果 print 打印到字符串, 重定向 stdout 到字符串
        with io.StringIO() as buf, redirect_stdout(buf):
            packet.show()
            show_str = buf.getvalue()

        self.contents_text.configure(state="normal")
        self.contents_text.delete("1.0", tkinter.END)  # 清空原先的内容
        self.contents_text.insert(tkinter.END, show_str)
        self.contents_text.configure(state="disabled")
        ## 包详细内容面板 End

    def _initialize_packets_area_listbox(self, frame):
        self.packets_listbox = tkinter.Listbox(frame, width=80)

        # 给 listbox 组件添加滚动条
        scrollbar = tkinter.Scrollbar(frame)
        scrollbar.pack(side=tkinter.RIGHT, fill=tkinter.Y)
        self.packets_listbox.config(yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.packets_listbox.yview)

        # 更新包的线程
        packets_update_thread = threading.Thread(target=self._update_packets_list, args=(self.packets_listbox,))
        packets_update_thread.start()

        # 单击后就显示内容
        self.packets_listbox.bind('<Double-Button-1>',
                                  self._handler_adaptor(self._print_packet_contents, listbox=self.packets_listbox))

        self.packets_listbox.pack(side=tkinter.RIGHT, fill=tkinter.Y)
        frame.grid(row=1)

    def _initialize_list_frame(self, frame):
        ## 包数量列表 Start TODO: 本来想写成 IP 列表的, 待定
        self.packets_num_listbox = tkinter.Listbox(frame)
        self.packets_num_listbox.pack(side=tkinter.LEFT, fill=tkinter.Y)
        self._update_packets_num_listbox()
        ## 包数量列表 End

        ## 包 区域 Start
        packets_area_frame = tkinter.LabelFrame(frame)

        # 标签帧 Start
        labels_frame = tkinter.LabelFrame(packets_area_frame)
        self._initialize_packets_area_labels(labels_frame)
        # 标签帧 End

        # 包列表帧 Start
        packets_frame = tkinter.Frame(packets_area_frame)
        self._initialize_packets_area_listbox(packets_frame)
        # 包列表帧 End

        packets_area_frame.pack(side=tkinter.RIGHT, fill=tkinter.Y)
        ## 包 区域 End

        frame.pack()
class MyUI:
    def __init__(self):
        self.d_packets_counts = {
            "TCP": 0,
            "UDP": 0,
            "ICMP": 0,
            "ARP": 0,
            "Others": 0,
            "Total": 0
        }
        self.d_configuration = OrderedDict()
        self.d_configuration["TCP"], self.d_configuration[
            "UDP"], self.d_configuration["ICMP"], self.d_configuration[
                "ARP"], self.d_configuration["Others"], self.d_configuration[
                    "Promisc"] = True, True, True, True, True, True

        # 嗅探线程
        self.my_sniffer_thread = MySnifferThread()

        ### 主窗口
        self.root = tkinter.Tk()
        self._initialize_root(self.root)

        ## 菜单栏
        menu_frame = tkinter.Frame(self.root)
        self._initialize_menu(menu_frame)

        ## 功能实现区 Start
        # 列表帧, 包括包数量列表, 包列表区域 #TODO: 本来第一个是 IP 列表区域的,待定
        packets_frame = tkinter.LabelFrame(self.root)
        self._initialize_list_frame(packets_frame)

        # 内容显示帧, 包括图形化显示/其余功能选项区域, 内容显示区域
        content_frame = tkinter.LabelFrame(self.root)
        self._initialize_content_frame(content_frame)
        ## 功能实现区 End

        ## 状态栏, TODO: 没时间写这东西®
        # state_frame = tkinter.Frame(self.root)

        self.root.mainloop()

    def _counts_packets_type(self, packet):
        # 用来更新包数量统计的, 不过这里写得太丑了, 待有序字典和迭代器改写
        if packet.haslayer("TCP"):
            self.d_packets_counts["TCP"] += 1
        elif packet.haslayer("UDP"):
            self.d_packets_counts["UDP"] += 1
        elif packet.haslayer("ICMP"):
            self.d_packets_counts["ICMP"] += 1
        elif packet.haslayer("ARP"):
            self.d_packets_counts["ARP"] += 1
        else:
            self.d_packets_counts["Others"] += 1
        self.d_packets_counts["Total"] += 1

    def _update_packets_num_listbox(self):
        """
        更新左上角的各种包的统计信息
        :return:
        """
        self.packets_num_listbox.delete(0, tkinter.END)  # 先清除原先的数据
        # 下面这个写太丑了, 应该用有序链表搭配迭代器来写的
        self.packets_num_listbox.insert(
            tkinter.END, "TCP:{}".format(self.d_packets_counts["TCP"]))
        self.packets_num_listbox.insert(
            tkinter.END, "UDP:{}".format(self.d_packets_counts["UDP"]))
        self.packets_num_listbox.insert(
            tkinter.END, "ICMP:{}".format(self.d_packets_counts["ICMP"]))
        self.packets_num_listbox.insert(
            tkinter.END, "ARP:{}".format(self.d_packets_counts["ARP"]))
        self.packets_num_listbox.insert(
            tkinter.END, "Others:{}".format(self.d_packets_counts["Others"]))
        self.packets_num_listbox.insert(
            tkinter.END, "Total:{}".format(self.d_packets_counts["Total"]))

    def _update_packets_list(self, listbox):
        # 保存包列表到类变量中
        self.l_packets = list()

        while True:
            # 检查是否有抓到新的包
            if len(l_packets) > len(self.l_packets):
                ## 更新包列表 Start
                # 有新的包, 保存进类变量里, 并且打印到图形界面的包列表区域中
                for index in range(len(self.l_packets), len(l_packets)):
                    print_data = "{:<10}{:<}".format(
                        index, l_packets[index].summary())
                    self.l_packets.append(l_packets[index])
                    listbox.insert(tkinter.END, print_data)
                    # listbox.yview(tkinter.END)  # 自动滚动到最下面
                    ## 更新包列表 End

                    ## 更新包数量列表 Start
                    self._counts_packets_type(l_packets[index])
                    self._update_packets_num_listbox()
                    ## 更新包数量列表 End
                    # listbox.update_idletasks()# 更新组件

    def _stop_sniff_thread(self):
        """
        控制当用户按下 stop capture packets 按钮时的交互
        :return:
        """
        res = self.my_sniffer_thread.is_stopped()
        if res is True:
            tkinter.messagebox.showerror(title="Stop capturing packets",
                                         message="嗅探早就停止了")
        elif res is False:
            self.my_sniffer_thread.stop()
            tkinter.messagebox.showinfo(title="Stop capturing packets",
                                        message="停止嗅探ing, 可以按 Restart 继续嗅探")
            self.buttons["restart"].configure(state="normal")
        elif res is None:
            tkinter.messagebox.showerror(title="Stop capturing packets",
                                         message="并没有嗅探过")

    def _lfilter_func(self, packet):
        """
        根据 self.d_configuration, 配置 sniff 所需要的 lfilter 函数
        :return:
        """
        # TODO: 这里写丑了, 居然需要专门弄个条件分支给 Promisc
        for protocol in self.d_configuration:
            if protocol != "Others":
                if packet.haslayer(
                        protocol) and self.d_configuration[protocol]:
                    return True
                elif packet.haslayer(
                        protocol) and self.d_configuration[protocol]:
                    return False
            elif self.d_configuration["Others"]:
                return True
            elif protocol == "Promisc":
                continue
        return False

    def _start_sniff_thread(self):
        """
        控制当用户按下 start 按钮时的交互
        :return:
        """
        # TODO: 为了减小本人的工作量, 只允许进行一次配置, 且只能在没开始嗅探之前配置...
        self.buttons["options"].configure(state="disabled")

        self.my_sniffer_thread.configurate(
            lfilter=self._lfilter_func,
            promisc=int(self.d_configuration["Promisc"]))
        self._initialize_functions_listbox()

        self.my_sniffer_thread.start()
        self.buttons["start"].configure(state="disabled")  # 使得按钮无法再按

    def _restart_sniff_thread(self):
        """
        控制当用户按下 restart 按钮时的交互
        :return:
        """
        self.my_sniffer_thread.restart()
        self.buttons["restart"].configure(state="disabled")

    def _checkbutton_callback(self, name):
        """
        功能是实现配置选项, 如果选中变为未选中,则令对应配置值变为 False
        例如, 原来 self.d_configuration["TCP"] = True, 执行完这个函数之后, self.d_configuration["TCP"] = False
        :param name: "TCP"
        :return:
        """
        self.d_configuration[name] = not self.d_configuration[name]

    def _set_checkbuttons(self):
        """
        放置配置窗口中的各个按钮
        :return:
        """
        for each_button in self.checkbuttons:
            if self.d_configuration[each_button]:
                self.checkbuttons[each_button].select()  # 默认为选中状态
            else:
                self.checkbuttons[each_button].deselect()  # 未选中状态
            self.checkbuttons[each_button].pack()

    def _initialize_configuration_buttons(self, frame):
        """
        初始化配置窗口中的过滤器中的包类型
        :param frame: 包选择过滤帧
        :return:
        """
        self.checkbuttons = OrderedDict()

        # TODO: 这下面这堆写丑了
        ## 过滤协议的选项 Start
        tkinter.Label(frame, text="选择所要保留的包类型以及是否开启混杂模式", anchor="w").pack()
        self.checkbuttons["TCP"] = tkinter.Checkbutton(
            frame,
            text="TCP",
            command=lambda: self._checkbutton_callback("TCP"))
        self.checkbuttons["UDP"] = tkinter.Checkbutton(
            frame,
            text="UDP",
            command=lambda: self._checkbutton_callback("UDP"))
        self.checkbuttons["ICMP"] = tkinter.Checkbutton(
            frame,
            text="ICMP",
            command=lambda: self._checkbutton_callback("ICMP"))
        self.checkbuttons["ARP"] = tkinter.Checkbutton(
            frame,
            text="ARP",
            command=lambda: self._checkbutton_callback("ARP"))
        self.checkbuttons["Others"] = tkinter.Checkbutton(
            frame,
            text="Others",
            command=lambda: self._checkbutton_callback("Others"))
        ## 过滤协议的选项 End

        ## 其他选项 Start
        # 配置混杂模式选项
        self.checkbuttons["Promisc"] = tkinter.Checkbutton(
            frame,
            text="Promisc",
            command=lambda: self._checkbutton_callback("Promisc"))
        self.checkbuttons["Promisc"].pack()
        ## 其他选项 End

        self._set_checkbuttons()  # 放置按钮

    def _configure_options(self):
        # 通过子窗口来进行配置
        sub_window = tkinter.Toplevel()
        sub_window.title("配置窗口")

        ## 包过滤选择帧 Start
        configurations_labelframe = tkinter.LabelFrame(sub_window)

        # 初始化选项复选框
        self._initialize_configuration_buttons(configurations_labelframe)

        configurations_labelframe.pack()
        ## 包过滤选择帧 End

        sub_window.grid()

    def _initialize_menu(self, frame):
        self.buttons = OrderedDict()

        # 创建各种按钮
        self.buttons["start"] = tkinter.Button(
            frame,
            text="Start capturing packets",
            fg="red",
            command=self._start_sniff_thread)
        self.buttons["stop"] = tkinter.Button(frame,
                                              text="Stop capturing packets",
                                              fg="blue",
                                              command=self._stop_sniff_thread)
        self.buttons["restart"] = tkinter.Button(
            frame,
            text="Restart current capture",
            fg="orange",
            state="disabled",
            command=self._restart_sniff_thread)
        self.buttons["options"] = tkinter.Button(
            frame,
            text="Capture options",
            fg="green",
            command=self._configure_options)

        # 放置控件
        column = 0
        for each_button in self.buttons:
            self.buttons[each_button].grid(row=0, column=column)
            column += 1
        frame.pack()

    def _initialize_root(self, root):
        # 设置窗口大小
        root.geometry("800x640")

        # 设置标题
        root.title("Sniffer By w@tch")

        # 不允许更改窗口大小
        root.resizable(height=False, width=False)

    def _initialize_functions_listbox(self):
        # TODO: 本来想写成功能区的,暂时没时间,所以写成配置显示区好了
        self.functions_listbox.delete(0, tkinter.END)
        for protocol in self.d_configuration:
            self.functions_listbox.insert(
                tkinter.END, "{}:{}".format(protocol,
                                            self.d_configuration[protocol]))

    def _initialize_content_frame(self, frame):
        # 图形化显示/其余功能选项帧
        self.functions_listbox = tkinter.Listbox(frame)
        self.functions_listbox.pack(side=tkinter.LEFT, fill=tkinter.Y)

        ## 内容显示帧 Start
        content_width = 90
        content_frame = tkinter.LabelFrame(frame)

        # 十六进制帧 Start
        hex_text_frame = tkinter.LabelFrame(content_frame)
        self.hex_text = tkinter.Text(hex_text_frame,
                                     width=content_width,
                                     height=10)
        self.hex_text.bind(
            "<1>", lambda event: self.hex_text.focus_set())  # 使之变为可读且可复制
        self.hex_text.configure(state="disabled")  # 使得用户无法输入
        self.hex_text.grid()
        hex_text_frame.pack(side=tkinter.BOTTOM, fill=tkinter.Y)
        # 十六进制帧 End

        # 包内容详细打印 Start
        contents_text_frame = tkinter.LabelFrame(content_frame)
        self.contents_text = tkinter.Text(contents_text_frame,
                                          width=content_width,
                                          height=10)
        self.contents_text.bind(
            "<1>", lambda event: self.contents_text.focus_set())  # 使之变为可读且可复制
        self.contents_text.configure(state="disabled")  # 使得用户无法输入
        self.contents_text.grid()
        contents_text_frame.pack(side=tkinter.TOP, fill=tkinter.Y)
        # 包内容详细打印 End

        content_frame.pack(side=tkinter.RIGHT, fill=tkinter.Y)
        ## 内容显示帧 End

        frame.pack()

    def _initialize_packets_area_labels(self, frame):
        labels = dict()
        label_width = 8

        # justify与anchor的区别了:一个用于控制多行的对齐;另一个用于控制整个文本块在Label中的位置
        labels["0"] = tkinter.Label(frame,
                                    text="序号",
                                    anchor="w",
                                    width=label_width)
        labels["1"] = tkinter.Label(frame,
                                    text="包概要",
                                    anchor="w",
                                    width=label_width * 9)
        # 增加一个按钮,要不然实在得自己滚到最底,不方便
        button = tkinter.Button(
            frame,
            text="滑至底部",
            command=lambda: self.packets_listbox.yview(tkinter.END))

        for label in labels:
            labels[label].grid(row=0, column=int(label))

        button.grid(row=0, column=len(labels) - 1)
        frame.grid(row=0, column=0, columnspan=len(labels))

    def _handler_adaptor(self, fun, **kwds):
        # 参考 http://blog.csdn.net/tinym87/article/details/6957438
        '''事件处理函数的适配器,相当于中介,那个event是从那里来的呢,我也纳闷,这也许就是python的伟大之处吧'''
        return lambda event, fun=fun, kwds=kwds: fun(event, **kwds)

    @staticmethod
    def _hexdump(x):
        """
        改造 scapy.utils 库中的 hexdump 为我所用(NND 的库函数要是跟写个返回值我就不用手改了)
        还有另外一种方式,参考 show() 函数,直接打印到字符串了
        :param x:
        :return:
        """
        result = ""
        if type(x) is not str and type(x) is not bytes:
            try:
                x = bytes(x)
            except:
                x = str(x)
        l = len(x)
        i = 0
        while i < l:
            result += "%04x  " % i + " "
            for j in range(16):
                if i + j < l:
                    result += "%02X" % orb(x[i + j]) + " "
                else:
                    result += "  " + " "
                if j % 16 == 7:
                    result += "" + " "
            result += " " + " "
            result += sane_color(x[i:i + 16]) + "\n"
            i += 16
        return result

    def _print_packet_contents(self, event, listbox):
        packet = self.l_packets[listbox.curselection()[0]]
        hex_contents = MyUI._hexdump(packet)

        ## 十六进制面板 Start
        # state="normal",使得我们可以输入内容, 输完内容后改 state 为 disabled 使用户无法输入
        self.hex_text.configure(state="normal")
        self.hex_text.delete("1.0", tkinter.END)  # 清空原先的内容
        self.hex_text.insert(tkinter.END, hex_contents)
        self.hex_text.configure(state="disabled")
        ## 十六进制面板 End

        ## 包详细内容面板 Start
        # 将 show 结果 print 打印到字符串, 重定向 stdout 到字符串
        with io.StringIO() as buf, redirect_stdout(buf):
            packet.show()
            show_str = buf.getvalue()

        self.contents_text.configure(state="normal")
        self.contents_text.delete("1.0", tkinter.END)  # 清空原先的内容
        self.contents_text.insert(tkinter.END, show_str)
        self.contents_text.configure(state="disabled")
        ## 包详细内容面板 End

    def _initialize_packets_area_listbox(self, frame):
        self.packets_listbox = tkinter.Listbox(frame, width=80)

        # 给 listbox 组件添加滚动条
        scrollbar = tkinter.Scrollbar(frame)
        scrollbar.pack(side=tkinter.RIGHT, fill=tkinter.Y)
        self.packets_listbox.config(yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.packets_listbox.yview)

        # 更新包的线程
        packets_update_thread = threading.Thread(
            target=self._update_packets_list, args=(self.packets_listbox, ))
        packets_update_thread.start()

        # 单击后就显示内容
        self.packets_listbox.bind(
            '<Double-Button-1>',
            self._handler_adaptor(self._print_packet_contents,
                                  listbox=self.packets_listbox))

        self.packets_listbox.pack(side=tkinter.RIGHT, fill=tkinter.Y)
        frame.grid(row=1)

    def _initialize_list_frame(self, frame):
        ## 包数量列表 Start TODO: 本来想写成 IP 列表的, 待定
        self.packets_num_listbox = tkinter.Listbox(frame)
        self.packets_num_listbox.pack(side=tkinter.LEFT, fill=tkinter.Y)
        self._update_packets_num_listbox()
        ## 包数量列表 End

        ## 包 区域 Start
        packets_area_frame = tkinter.LabelFrame(frame)

        # 标签帧 Start
        labels_frame = tkinter.LabelFrame(packets_area_frame)
        self._initialize_packets_area_labels(labels_frame)
        # 标签帧 End

        # 包列表帧 Start
        packets_frame = tkinter.Frame(packets_area_frame)
        self._initialize_packets_area_listbox(packets_frame)
        # 包列表帧 End

        packets_area_frame.pack(side=tkinter.RIGHT, fill=tkinter.Y)
        ## 包 区域 End

        frame.pack()