狂自私

导航

python_everything——UI版

单线程扫描模式

因为是io型程序,所以用线程是可以的

scan_module.py

'''
本类需要用到的模块信息
'''
import os
import string
import time
from copy import _reconstruct

'''
    scan_module.py
    本文件内容是扫描模块,作用是用于扫描和保存扫描结果。
    代码不再使用或大幅度减少函数式编程代码,改为使用对象编程的方法。
    本代码只适用于Windows系统
'''


class scan_module:
    '''
    扫描模块,实现扫描的核心代码和存储扫描出来的数据。
    '''
    __all_file_dict = {}

    def __init__(self):
        '''
        实例化对象的时候就初始化出需要扫描的根路径
        '''
        self.__paths_list = []  # 存储盘符,变量类型为列表

        self.__get_DriveLetter()  # 初始化盘符列表变量。

    def __get_DriveLetter(self):
        '''
        获取盘符列表,是通过猜测验证其是否存在。
        私有函数
        '''
        for i in string.ascii_uppercase:
            if os.path.exists(i + ':\\'):
                self.__paths_list.append(i + ':\\')
            # else的情况不考虑停止,实际上,用户的磁盘盘符完全可以不连续。

    def __get_fileList(self, initial_path):
        '''
        扫描,私有函数
        '''
        try:
            child_file_list = os.scandir(initial_path)
        except:
            # 遇到异常则直接终止函数
            # print(initial_path)
            return
        for file_ in child_file_list:
            file_stat = file_.stat(follow_symlinks=False)
            time_local = time.localtime(file_stat.st_ctime)
            # Ctiem,创建时间
            file_ctime = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
            # Mtime,最近的修改时间
            time_local = time.localtime(file_stat.st_mtime)
            file_mtime = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
            # atime,最近的访问时间
            time_local = time.localtime(file_stat.st_atime)
            file_atime = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
            # size,文件大小(以字节为单位),文件可以是常规文件或符号链接。符号链接的大小是它包含的路径的长度,不包括末尾的空字节。
            file_size = int(file_stat.st_size)
            isDir = 0
            if file_.is_dir():
                isDir = 1
            else:
                isDir = 0
            # isDir应该永远位于列表的最后一位
            if file_.name in self.__all_file_dict.keys():
                self.__all_file_dict[file_.name].append(
                    [initial_path, file_ctime, file_mtime, file_atime, file_size, file_stat.st_file_attributes , ])
            else:
                self.__all_file_dict[file_.name] = [
                    [initial_path, file_ctime, file_mtime, file_atime, file_size, file_stat.st_file_attributes , ], ]
            # 是否是目录
            if file_.is_dir():
                self.__get_fileList(file_.path)

    def get_fileList_v2(self):
        '''
        听说了一个os.walk库,专用于遍历文件存在的
        比我自己写的慢一倍。
        '''
        start_time = time.time()
        for path_i in self.__paths_list:
            for dirpath, dirnames, filenames in os.walk(path_i):
                for name in dirnames:
                    absPath = os.path.join(dirpath, name)
                    if name in self.__all_file_dict.keys():
                        self.__all_file_dict[name].append([absPath, ])
                    else:
                        self.__all_file_dict[name] = [[absPath, ], ]
                for name in filenames:
                    absPath = os.path.join(dirpath, name)
                    if name in self.__all_file_dict.keys():
                        self.__all_file_dict[name].append([absPath, ])
                    else:
                        self.__all_file_dict[name] = [[absPath, ], ]
        print("V2版本的扫描时间:{}".format(time.time() - start_time))

    def scan_console(self):
        '''
        调用扫描函数
        '''
        start_time = time.time()
        for path_i in self.__paths_list:
            self.__get_fileList(path_i)
        return time.time() - start_time
        #print("扫描花费时间:{}".format(time.time() - start_time))

    def formatted_output(self):
        '''
        将结果字典格式化输出到文件中存储。
        输出到用户家目录下存放,名称叫python_class_eveything_data.data
        '''
        out_file_path_str = os.environ['USERPROFILE'] + \
            '\\python_class_eveything_data.data'
        with open(out_file_path_str, 'w', encoding='utf-8')as fd:
            # 偶尔会发生在迭代期间字典大小发生变化的运行时错误
            # 之前在迭代期间字典大小发生变化的bug解决了,但是这里就这样吧。
            data = self.__my_deepcopy(self.__all_file_dict)
            for key, value in data.items():
                for value_i in value:
                    line_str = key
                    for value_i_i in value_i:
                        line_str += "\t{}".format(value_i_i)
                    fd.write(line_str + '\n')
            del data

    def __my_deepcopy(self, x):
        '''
        深拷贝,copy库的deepcopy性能不太行,所以在deepcopy的基础上改了一下
        这个深拷贝函数仅用于本脚本中,copy库中的deepcopy函数慢是有原因的,了解之后再使用。
        '''
        reductor = getattr(x, "__reduce_ex__", None)
        if reductor is not None:
            rv = reductor(4)
        y = _reconstruct(x, None, *rv)
        return y

    def get_data(self):
        """
        返回数据的副本,同时删除源数据
        """
        y = self.__my_deepcopy(self.__all_file_dict)
        self.__all_file_dict = {}
        return y

    def get_file_num(self):
        '''
        获取扫描的文件数量
        '''
        num = len(self.__all_file_dict)
        print("文件数量(只统计key):{}。".format(num))
        return num
if __name__ == "__main__":
    a = scan_module()
    a.scan_console()
    a.formatted_output()

 

 

UI.py

 

import ctypes  # 文件操作-剪贴板操作相关
import os
import stat
import re
import sys
import threading
import time
import tkinter
import signal
import tkinter.messagebox
import tkinter.ttk
import traceback
import subprocess
import pyperclip  # 字符串-剪贴板相关
import win32api
import win32clipboard  # 文件操作-剪贴板操作相关
from win32com.shell import shell, shellcon
from copy import _reconstruct   #深拷贝

import scan_module
import sysTrayIcon


class UI:
    '''
    绘制界面
    '''
    
    def __init__(self):
        self.__root = tkinter.Tk()
        self.__root.title("python_everything")
        self.__root.iconbitmap(r"python_everything.ico")
        self.__size_long = 1200     #
        self.__size_width = 500     #
        self.__root.geometry("{}x{}".format(self.__size_long,self.__size_width))
        self.__SysTrayIcon = None  # 值在Hidden_window函数中初始化
        # 根窗口大小改变的事件
        self.__root.bind("<Configure>", func=self.__table_cloumnSize)
        self.__root.protocol("WM_DELETE_WINDOW", self.Hidden_window)  # 重定义关闭事件
        # 绑定快捷键Ctrl+f,捕捉到这个事件后,使搜索框得到焦点
        self.__root.bind("<Control-f>", func=self.__searchBar_focus)
        # 数据
        self.__data = self.__get_data_fromFile()

        # 初始化一些变量
        self.__is_caseSensitive = False  # 是否区分大小写,FALSE表示不区分
        self.__max_showLine = 1000  # 最大显示的行数
        self.__sortFlag_col_size = False  # 列“大小”如何排序(False表示从大到小,True表示从小到大)
        self.__search_result = self.__data  # 搜索的结果
        self.__show_data = []  # 最终显示的结果
        self.__filter_condition = "all"  # 筛选条件,在显示数据前的最后一层条件
        self.__stratTime = time.time()    #记录操作开始的时间
        self.__map_dict__ = {"all":"所有文件","zip":"压缩文件","audio":"音频文件","video":"视频文件","picture":"图片文件","document":"文档文件","execute":"可执行文件","folder":"文件夹","lnk":"快捷方式",}
        self.__threading_Operating_condition = ""  # 存储子进程对象
        self.__scan = scan_module.scan_module()  # 扫描模块对象
        self.__scan_num = 0  # 扫描次数
        self.__scan_console()  # 启动扫描子进程
        self.__statusBar_right_TEXT = '不区分大小写;显示所有文件;'

        self.__pid = os.getpid()
        # print(self.__pid)

        self.__menu()
        self.__search_bar()
        self.__status_bar()
        self.__content()

    def __scan_console(self):
        """扫描以及数据获取"""
        def get_data_dict():
            """获取扫描数据的实际代码"""
            time_ = self.__scan.scan_console()
            self.__scan.formatted_output()
            self.__data = self.__scan.get_data()
            self.__scan_num += 1
            # 提示扫描完毕
            self.__statusBar.config(
                fg="black", text="扫描完毕,用时:{}秒。".format(time_))

        if self.__threading_Operating_condition == '' or self.__threading_Operating_condition.is_alive() == False:
            self.__threading_Operating_condition = threading.Thread(
                target=get_data_dict)
            self.__threading_Operating_condition.start()

    def __get_data_fromFile(self):
        """#从文件中获取数据"""
        in_file_path_str = os.environ['USERPROFILE'] + \
            '\\python_class_eveything_data.data'
        if(os.path.exists(in_file_path_str)):
            with open(in_file_path_str, "r", encoding='utf-8-sig') as in_fd:
                lines_list = in_fd.read().splitlines()
                lines_list_ = [i.split('\t') for i in lines_list]
                return lines_list_
        else:
            tkinter.messagebox.showinfo(
                title="提示", message="数据文件不存在,请等待扫描结果出来之后再进行搜索。")
            return []

    def __menu(self):
        """#建立菜单"""
        # 顶层菜单
        self.__ui_menu_top = tkinter.Menu(master=self.__root)
        # 文件菜单,且取消虚线和分离功能
        self.__ui_menu_file = tkinter.Menu(
            master=self.__ui_menu_top, tearoff=False)
        # 搜索菜单
        self.__ui_menu_search = tkinter.Menu(
            master=self.__ui_menu_top, tearoff=False)
        # 关联顶层菜单和文件菜单
        self.__ui_menu_top.add_cascade(label="文件", menu=self.__ui_menu_file)
        self.__ui_menu_top.add_cascade(label="搜索", menu=self.__ui_menu_search)
        # 文件菜单的菜单列表
        # self.ui_menu_file.add_command()
        #self.__ui_menu_file.add_command(label="新建窗口", command=lambda: print("ok"))
        self.__ui_menu_file.add_command(
            label="退出", command=self.exit_off)
        # 创建具有复选框的子项
        self.__demoStatus = tkinter.BooleanVar()
        self.__demoStatus.set(False)  # 初始状态为不区分大小写,__demoStatus是一个bool值记录器。
        self.__ui_menu_search.add_checkbutton(label="区分大小写", command=self.__case_sensitive, variable=self.__demoStatus)
        self.__ui_menu_search.add_separator()  # 添加分隔线

        self.__filterStatus_list = []
        [self.__filterStatus_list.append(
            tkinter.BooleanVar()) for i in range(9)]
        [self.__filterStatus_list[i].set(False) for i in range(9)]
        self.__filterStatus_list[0].set(True)  # 所有文件这个选择时默认勾选上的

        self.__ui_menu_search.add_checkbutton(
            label="所有文件", command=lambda cond="all", index=0: self.__filter_console(cond, index), variable=self.__filterStatus_list[0])
        self.__ui_menu_search.add_checkbutton(
            label="压缩文件", command=lambda cond="zip", index=1: self.__filter_console(cond, index), variable=self.__filterStatus_list[1])
        self.__ui_menu_search.add_checkbutton(
            label="音频文件", command=lambda cond="audio", index=2: self.__filter_console(cond, index), variable=self.__filterStatus_list[2])
        self.__ui_menu_search.add_checkbutton(
            label="视频文件", command=lambda cond="video", index=3: self.__filter_console(cond, index), variable=self.__filterStatus_list[3])
        self.__ui_menu_search.add_checkbutton(
            label="图片文件", command=lambda cond="picture", index=4: self.__filter_console(cond, index), variable=self.__filterStatus_list[4])
        self.__ui_menu_search.add_checkbutton(
            label="文档文件", command=lambda cond="document", index=5: self.__filter_console(cond, index), variable=self.__filterStatus_list[5])
        self.__ui_menu_search.add_checkbutton(
            label="可执行文件", command=lambda cond="execute", index=6: self.__filter_console(cond, index), variable=self.__filterStatus_list[6])
        self.__ui_menu_search.add_checkbutton(
            label="快捷方式", command=lambda cond="lnk", index=7: self.__filter_console(cond, index), variable=self.__filterStatus_list[7])
        self.__ui_menu_search.add_checkbutton(
            label="文件夹", command=lambda cond="folder", index=8: self.__filter_console(cond, index), variable=self.__filterStatus_list[8])
        #路径筛选
        self.__ui_menu_search.add_separator()  # 添加分隔线
        self.__path_filter_Status = tkinter.BooleanVar()
        self.__path_filter_Status_bool = False
        self.__path_filter_Status.set(False)
        self.__ui_menu_search.add_checkbutton(label="路径筛选", command=self.__pathFilter, variable=self.__path_filter_Status)
        # 显示菜单
        self.__root.config(menu=self.__ui_menu_top)

    def __case_sensitive(self):
        '''使得搜索区分大小写'''
        if self.__is_caseSensitive:
            # 已经勾选了要区分大小写,再次点击就是不区分大小写了
            self.__demoStatus.set(False)
            self.__is_caseSensitive = False
            self.__statusBar_right.config(text=self.__statusBar_right.cget(key="text").replace("区分","不区分"))
        else:
            self.__demoStatus.set(True)
            self.__is_caseSensitive = True
            self.__statusBar_right.config(text=self.__statusBar_right.cget(key="text").replace("不区分","区分"))
        

    def __status_bar(self):
        """状态栏,构建两个标签控件,左边的显示常规的信息,右边的显示当前的一些控制选项;"""
        #
        self.__statusBar_frame = tkinter.Frame(master=self.__root)
        self.__statusBar_frame.pack(side=tkinter.BOTTOM,fill=tkinter.X)

        self.__statusBar = tkinter.Label(master=self.__statusBar_frame, text="{}个对象".format(len(self.__data)), anchor=tkinter.W, relief=tkinter.RIDGE)
        self.__statusBar_right = tkinter.Label(master=self.__statusBar_frame, text=self.__statusBar_right_TEXT, anchor=tkinter.W, relief=tkinter.SOLID)
        self.__statusBar.pack(side=tkinter.LEFT, anchor=tkinter.W,expand=True,fill=tkinter.X)
        self.__statusBar_right.pack(side=tkinter.RIGHT, anchor=tkinter.SE)

    def __search_bar(self):
        """#搜索框"""
        self.__entryFrame = tkinter.Frame(master=self.__root)
        self.__entryFrame.pack(fill=tkinter.X)
        # exportselection=0:禁止自动复制选取的字符串,当任何击键更改小组件的内容时进行回调函数
        self.__searchBar = tkinter.Entry(master=self.__entryFrame, exportselection=0)
        self.__searchBar.bind("<Return>", self.__search_console)
        self.__searchBar.pack(side=tkinter.LEFT,fill=tkinter.X,expand=True)
        self.__pathBar = tkinter.Entry(master=self.__entryFrame, exportselection=0,width=60) #默认情况下不显示该控件
        self.__pathBar.bind("<Return>", self.__search_console)
        # 事件
        self.__searchBar.bind("<Button-3>", self.__searchBar_rightClik)

    def __searchBar_rightClik(self, event):
        '''搜索框右键,目前支持,复制,粘贴,剪切'''
        e = event.widget    # 获取发出事件的控件
        # 获取搜索框中的字符串
        searcStr = e.get()
        # 是否有选中字符
        is_checked = e.select_present()
        checked_str = ""
        start_index = 0
        end_index = 0
        if is_checked:
            # checked_str = e.select_get()    #获取选中的文本
            start_index = e.index(tkinter.ANCHOR)  # 开始选择时的index
            end_index = e.index(tkinter.INSERT)  # 结束选择时的index
            if start_index < end_index:
                # 从左向右选择
                checked_str = searcStr[start_index: end_index]
            elif start_index > end_index:
                # 从右向左选择
                checked_str = searcStr[end_index: start_index]
            else:
                checked_str = ""

        def copy_Str(Str):
            pyperclip.copy(Str)  # 将字符串发送到剪贴板上

        def cut_selectStr(Str, e,is_cut=True):
            if is_cut:
                pyperclip.copy(Str)  # 将字符串发送到剪贴板上
            # e.select_clear()        #清除所选字符串,妈的,竟然不管用
            new_str = ""
            if start_index < end_index:
                new_str = searcStr[0:start_index] + searcStr[end_index:]
                e.delete(0, tkinter.END)
                e.insert(0, new_str)
                e.icursor(end_index - len(checked_str))  # 设置光标位置
            elif start_index > end_index:
                # 从右向左选择
                new_str = searcStr[0:end_index] + searcStr[start_index:]
                e.delete(0, tkinter.END)
                e.insert(0, new_str)
                e.icursor(start_index - len(checked_str))  # 设置光标位置
            else:
                new_str = ""

        def cut_allStr(Str, e):
            pyperclip.copy(Str)  # 将字符串发送到剪贴板上
            e.delete(0, tkinter.END)  # 清空整个字符串

        def paste(e):
            #检查一下有没有选择字符
            if is_checked:
                cut_selectStr(cut_selectStr,e=e,is_cut=False)  #删除所选择的字符串
            str_ = pyperclip.paste()  # 获取剪贴板上的文字内容
            index_ = e.index(tkinter.INSERT)  # 获取光标当前的位置
            e.insert(index_, str_)

        def select_all(e):
            e.select_range(0, tkinter.END)

        def del_allStr(e):
            e.delete(0, tkinter.END)  # 清空整个字符串

        # 建立弹出式菜单

        popupMenu = tkinter.Menu(master=self.__root, tearoff=False)  # 隐藏虚线分割线
        if is_checked:
            popupMenu.add_command(
                label="复制所选文字", command=lambda: copy_Str(checked_str))
        popupMenu.add_command(
            label="复制全部文字", command=lambda: copy_Str(searcStr))
        if is_checked:
            popupMenu.add_command(
                label="剪切所选文字", command=lambda: cut_selectStr(checked_str, e,True))
        popupMenu.add_command(
            label="剪切全部文字", command=lambda: cut_allStr(searcStr, e))
        popupMenu.add_command(label="粘贴", command=lambda: paste(e))
        popupMenu.add_command(label="全选", command=lambda: select_all(e))
        if is_checked:
            popupMenu.add_command(
                label="删除所选文字", command=lambda: cut_selectStr(checked_str,e,False))
        popupMenu.add_command(label="删除全部文字", command=lambda: del_allStr(e))

        popupMenu.post(event.x_root, event.y_root)  # 显示菜单;
    def __my_deepcopy(self, x):
        '''
        深拷贝,copy库的deepcopy性能不太行,所以在deepcopy的基础上改了一下
        这个深拷贝函数仅用于本脚本中,copy库中的deepcopy函数慢是有原因的,了解之后再使用。
        '''
        reductor = getattr(x, "__reduce_ex__", None)
        if reductor is not None:
            rv = reductor(4)
        y = _reconstruct(x, None, *rv)
        return y
    def __search_console(self, event):
        """
        搜索行为控制,是从文件读取的内容中进行搜索还是从字典中进行搜索;
        每次搜索,都将调用扫描函数进行扫描获取数据

        从搜索框中获取文件名表达式和路径表达式:搜索的时候,同时要满足文件名表达式和路径表达式
        """
        self.__stratTime = time.time()
        
        self.__scan_console()       #扫描数据
        path_str = self.__pathBar.get() #不使用正则
        filename_regex = self.__searchBar.get()
        
        result_list = ''
        if filename_regex == "" or filename_regex == r'.*' or filename_regex == r'.+' or filename_regex == r'.':
            if self.__scan_num > 0:
                result_list = self.__dict_Conversion_list(self.__data)
            else:
                result_list = self.__data
        else:
            if self.__scan_num > 0:
                # 此时self.__data是字典类型
                result_list = self.__find_match_dict()
            else:
                # 此时self.__data是列表类型
                result_list = self.__find_match()
        #self.__search_result = result_list
        self.__search_result = []
        #存在路径字符串,且路径输入框显示出来的情况下
        if (len(path_str)>0 and (self.__path_filter_Status_bool)):
            if(path_str[-1]=='\\'):
                path_str = path_str[:-1]
            #极端情况下有点影响性能啊,比如查询出来的结果特别多的时候
            num = 0 #记录匹配到了多少个,不必查询出全部的值
            for result_list_i in result_list:
                if path_str.upper() in result_list_i[1].upper():
                    self.__search_result.append(result_list_i)
                    num += 1
                    if num >= self.__max_showLine:
                        break
        else:
            self.__search_result = result_list
        self.__type_filter(self.__filter_condition)
        self.__filling_init(self.__show_data)
    def __path_filter(self,event):
        '''路径筛选,是从搜索出来的结果中进行筛选,
            没有使用该函数        
        '''
        self.__stratTime = time.time()
        path_str = self.__pathBar.get() #不适用正则,但是也不区分大小写
        if(path_str[-1]=='\\'):
            path_str = path_str[:-1]
        path_filter_result = []
        if len(path_str) > 0:
            for result_list_i in self.__show_data:
                if path_str.upper() in result_list_i[1].upper():
                    path_filter_result.append(result_list_i)
            self.__filling_init(path_filter_result)
            self.__statusBar.config(text = "筛选出{}个结果。所用时间:{};当前显示文件类型:{}".format(len(path_filter_result),time.time()-self.__stratTime,self.__map_dict__[self.__filter_condition]),fg='green')
        else:
            self.__filling_init(self.__show_data)
            self.__statusBar.config(text = "取消筛选。",fg='block')
    def __find_match(self):
        '''
        查询函数,从列表中读取,没有进行性能优化,即没有聚合成大字符串,再一次性匹配出所有符合条件的值得处理。
        '''
        #print("regex_str = {}".format(regex_str))
        regex_str = self.__searchBar.get()
        if regex_str == "":
            self.__statusBar.config(text="搜索框中没有字符哦。", foreground="red")
            return [self.__data[i] for i in range(self.__max_showLine)]
        data = self.__data

        result_list = []
        for i in data:
            if self.__is_caseSensitive:
                result_regex = re.findall(regex_str, i[0])
            else:
                result_regex = re.findall(regex_str, i[0], re.I)
            if len(result_regex) != 0:
                result_list.append(i)
        return result_list

    def __regex_translate(self, regex_str):
        """表达式翻译处理"""
        if regex_str == '':
            return ""
        else:
            # 需要判断输入的正则表达式是否合法,不合法的话,就需要提示用户重新输入,之所以不纠正是因为不要猜测用户的意图。
            try:
                re.compile(regex_str)
            except:
                self.__statusBar.config(text="输入的正则表达式有误!", fg='red')
                return ''
            if regex_str[0] == '^':
                regex_str = regex_str[1:]
                replace_result_str = r"(?<=\t)"
            elif regex_str[0] == '.':  # 提升数倍的性能(6倍)
                replace_result_str = r"(?<=\t)"
            else:
                replace_result_str = r'(?<=\t)[^\t]*?'
            for index in range(len(regex_str)):
                if regex_str[index] == '.':
                    if index != 0 and regex_str[index - 1] == '\\':
                        replace_result_str += regex_str[index]
                    else:
                        replace_result_str += r'[^\t]'
                else:
                    replace_result_str += regex_str[index]
            if replace_result_str[-1] == "$":
                replace_result_str = replace_result_str[0:-1]
                replace_result_str += r'(?=\t)'
            else:
                replace_result_str += r'[^\t]*?(?=\t)'
            # print(replace_result_str)
            return replace_result_str

    def __find_match_dict(self):
        '''
        查询函数,从字典中读取,并生成结果列表类型变量,先将可以聚合成大字符串,在一次性匹配出所有符合条件的值。
        '''
        data = self.__data
        regex_str = self.__searchBar.get()
        regex_str = self.__regex_translate(regex_str)
        if regex_str == "":
            #self.__statusBar.config(text="搜索框中没有字符哦。", foreground="red")
            #这一步已经在上层检查过了,不会出现这样的情况,只是为了保险起见这样做
            return []

        result_list = []
        if type({}) == type(data):
            keys = data.keys()
            keys_str = "\t"
            keys_str += '\t'.join(keys)
            keys_str += '\t'
            if self.__is_caseSensitive:
                result_key = re.findall(regex_str, keys_str)
            else:
                result_key = re.findall(regex_str, keys_str, re.I)
            
            for key in result_key:
                temp_ = {}
                temp_[key] = data[key]
                result_list += self.__dict_Conversion_list(temp_)
        else:
            tkinter.messagebox.showinfo(title="异常", message="未传入字典类型变量!函数:__find_match_dict")
            return []
        
        return result_list

    def __dict_Conversion_list(self, dict_data):
        '''
        将字典键和值转变为列表
        '''
        if type({}) != type(dict_data):
            return []
        return_result = []
        for key, value in dict_data.items():
            for value_i in value:
                temp_list = []
                temp_list.append(key)
                for value_i_i in value_i:
                    temp_list.append(value_i_i)
                return_result.append(temp_list)
        return return_result

    def __content(self):
        """数据表格"""
        self.__tableView = tkinter.ttk.Treeview(master=self.__root, columns=(
            "路径", "创建时间", "修改时间", "访问时间", "大小","文件属性"), selectmode=tkinter.EXTENDED)
        # 建立栏标题
        self.__tableView.heading("#0", text="名称")
        self.__tableView.heading("#1", text="路径")
        self.__tableView.heading("#2", text="创建时间")
        self.__tableView.heading("#3", text="修改时间")
        self.__tableView.heading("#4", text="访问时间")
        self.__tableView.heading("#5", text="大小", command=lambda c=5: self.__tableView_sortColumn(c))
        self.__tableView.heading("#6", text="文件属性")
        # 固定列宽
        self.__table_cloumnSize(None)

        # 右对齐
        self.__tableView.column("大小", anchor=tkinter.E)

        # 滚动条
        self.__scroll_bar()  # 实例化滚动条对象
        self.__tableView.config(yscrollcommand=self.__scrollBar_y.set,
                                xscrollcommand=self.__scrollBar_x.set)  # 数据表格与滚动条做联动

        # 填充内容
        self.__type_filter(self.__filter_condition)
        self.__filling_init(self.__show_data)
        # self.__filling_init(self.__search_result)

        # 绑定函数
        self.__tableView.bind(
            "<Double-1>", self.__tableView_doubleClike)  # 双击事件
        self.__tableView.bind(
            "<Return>", self.__tableView_enterEvent)  # 回车键盘事件
        self.__tableView.bind(
            "<Delete>", self.__tableView_deleteEvent)  # 回车键盘事件
        self.__tableView.bind(
            "<Button-3>", self.__tableView_rightClick)  # 右键事件
        self.__tableView.bind("<Button-1>", self.__tableView_Click)  # 单击事件
        self.__tableView.bind(
            "<Control-c>", self.__copyFiles_toClip)  # 复制选中的文件到剪贴板
        self.__tableView.bind(
            "<Control-x>", self.__cutFiles_toClip)  # 剪切选中的文件到剪贴板

        # 显示
        self.__tableView.pack(fill=tkinter.BOTH, expand=1)

    def __content_copy(self, event):
        '''复制选中行和鼠标所在列交集处的内容'''
        event_widget = event.widget                 # 获取发出事件的控件
        selec_lins_ids = event_widget.selection()   # 返回选中的行ID元组
        column = event_widget.identify("column", event.x, event.y)  # 获取右击所在的列
        content_str = ""
        index = int(column[1:]) - 1
        if index < 0:
            for i in selec_lins_ids:
                content_str += "{}\n".format(event_widget.item(i, "text"))
        else:
            for i in selec_lins_ids:
                content_str += "{}\n".format(event_widget.item(i,
                                             "values")[index])
        content_str = content_str.strip()
        pyperclip.copy(content_str)  # 将字符串发送到剪贴板上

    def __tableView_Click(self, event):
        '''鼠标单击事件,主要是在状态栏上显示点击的文件信息'''
        e = event.widget  # 获取发出事件的控件
        column = e.identify("row", event.x, event.y)  # 返回单击所在的行ID
        values = e.item(column, "values")
        filePath = ''
        if len(values) > 0:
            filePath = values[0]
        else:
            return
        fileName = e.item(column, "text")
        fileABSpath = "{}\\{}".format(filePath, fileName)
        if fileABSpath == "\\":
            self.__statusBar.config(text="", fg="green")
            return
        file_info = "文件 {}".format(fileName)
        if os.path.exists(fileABSpath):
            file_info += "目前存在;{}".format(values[5])
            self.__statusBar.config(text=file_info, fg="green")
        else:
            file_info += "不存在;"
            self.__statusBar.config(text=file_info, fg="red")

    def __tableView_rightClick(self, event):
        '''右键事件'''
        def popUpMenu(openfile_name=None,copyfile_name=None,cutfile_name=None,copy_content_name=None,openfile_localtion_name=None):
            '''
            openfile_name:打开文件菜单的名称,用于显示
            copyfile_name:复制文件菜单的名称,用于显示
            cutfile_name:剪切文件菜单的名称,用于显示
            copy_content_name:复制点击处内容菜单的名称,用于显示
            openfile_localtion_name:打开文件所在位置菜单的名称,用于显示
            '''
            popupMenu = tkinter.Menu(
                master=self.__root, tearoff=False)  # 隐藏虚线分割线

            popupMenu.add_command(label= (lambda x: "打开文件" if x == None  else  x)(openfile_name) , command=lambda: self.__open_files(event))
            popupMenu.add_command(label=(lambda x: "复制文件" if x == None  else  x)(copyfile_name), command=lambda: self.__copyFiles_toClip(event))
            popupMenu.add_command(label=(lambda x: "剪切文件" if x == None  else  x)(cutfile_name), command=lambda: self.__cutFiles_toClip(event))
            popupMenu.add_command(label=(lambda x: "复制名称" if x == None  else  x)(copy_content_name), command=lambda: self.__content_copy(event))
            popupMenu.add_command(label=(lambda x: "打开文件所在位置" if x == None  else  x)(openfile_localtion_name), command=lambda: self.__open_fileLocation(event))

            popupMenu.post(event.x_root, event.y_root)  # 显示菜单;
        e = event.widget  # 获取发出事件的控件
        if len(e.get_children()) == 0:
            # 若是没有子项就不做处理
            return
        line_x = column = e.identify("row", event.x, event.y)  # 返回右键所在的行ID
        selec_x = e.selection()  # 返回选中的行ID元组
        if len(selec_x) == 0 or line_x not in selec_x:
            # 刷新
            def refresh(event):
                ''' 显示弹出式菜单:刷新
                    刷新作用有两个,一个是根据搜索框内容重新显示结果,二是触发扫描
                '''
                self.__search_console(event)
            refresh_popupMenu = tkinter.Menu(
                master=self.__root, tearoff=False)  # 隐藏虚线分割线
            refresh_popupMenu.add_command(
                label="刷新", command=lambda: refresh(event))
            refresh_popupMenu.post(event.x_root, event.y_root)  # 显示菜单;
            return
        column = e.identify("column", event.x, event.y)  # 获取右击所在的列
        filepaths_list = []
        [filepaths_list.append("{}\\{}".format(e.item(selec_x_i, "values")[
                               0], e.item(selec_x_i, "text"))) for selec_x_i in selec_x]
        if len(filepaths_list) == 1 and filepaths_list[0] == '\\':
            self.__statusBar.config(text="非法路径", fg="red")
            return
        if(column == "#0"):
            # 建立弹出式菜单
            # 打开,复制,剪切,重命名,复制名称
            popUpMenu()
        elif (column == "#1"):
            popUpMenu(copy_content_name='复制路径')
        elif (column == "#2"):
            popUpMenu(copy_content_name='复制创建时间')
        elif (column == "#3"):
            popUpMenu(copy_content_name='复制修改时间')
        elif (column == "#4"):
            popUpMenu(copy_content_name='复制访问时间')
        elif (column == "#5"):
            popUpMenu(copy_content_name='复制大小')
        else:
            pass

    def __tableView_deleteEvent(self, event):
        """delete事件,删除选择的项目"""
        event_widget = event.widget
        ids = event_widget.selection()
        for id in ids:
            path = event_widget.item(id, "values")[0]
            name = event_widget.item(id, "text")
            file_absPath = "{}\\{}".format(path, name)
            if file_absPath == '\\':
                self.__statusBar.config(text="非法路径", fg="red")
                continue
            if self.__del_file(file_absPath):
                event_widget.delete(id)
                self.__statusBar.config(
                    text="删除{}成功!".format(name), fg="green")
                # 源数据也需要删除的
                self.__sourceData_del(name, path)
            else:
                self.__statusBar.config(text="删除{}失败!".format(name), fg="red")

    def __tableView_enterEvent(self, event):
        '''数据区域内的回车事件,回车事件的动作为打开选中的文件'''
        event_widget = event.widget
        ids = event_widget.selection()
        if len(ids) > 5:
            choose_ret = tkinter.messagebox.askquestion(
                title="警告", message="你想要打开数量超过5个的文件集合,确定吗?")
            if choose_ret == 'yes':
                pass
            else:
                self.__statusBar.config(text="取消打开!", fg="black")
                return
        for id in ids:
            file_absPath = "{}\\{}".format(event_widget.item(id, "values")[
                                           0], event_widget.item(id, "text"))
            if file_absPath == '\\':
                self.__statusBar.config(text="非法路径", fg="red")
                continue
            self.__open_file(file_absPath)
            label_str = self.__statusBar.cget(key="text")  # 获取text的值
            # if ret:
            #     self.__statusBar.config(
            #         text="{};打开成功!".format(label_str), fg="green")
            # else:
            #     self.__statusBar.config(
            #         text="{};打开失败!".format(label_str), fg="red")

    def __table_cloumnSize(self, event):
        '''调整__tableView的列宽'''
        # 固定列宽
        self.__tableView.column("#0", width=350)
        self.__tableView.column("#1", width=350)
        self.__tableView.column("#2", width=130)
        self.__tableView.column("#3", width=130)
        self.__tableView.column("#4", width=130)
        self.__tableView.column("#5", width=50)
        self.__tableView.column("#6", width=130)

    def __sourceData_del(self, key, value):
        '''删除__data变量里面的相关数据'''
        if type({"a": 1}) == type(self.__data):
            for data_i in self.__data[key]:
                index = -1  # 存储下标
                is_flag = False  # 标志是否找到
                for list_i in data_i:
                    index += 1
                    if list_i[0] == value:
                        is_flag = True
                        break
                if is_flag:
                    if len(self.__data[key]) == 1:
                        del self.__data[key]
                    else:
                        del self.__data[key][index]
                else:
                    tkinter.messagebox.showinfo(
                        title="异常", message="没有在源数据中找到对应的需要删除的数据:{}\\{}".format(value, key))

    def __del_file(self, path_str):
        '''删除文件(夹),这里会做确认操作;但是若只是删除一个项目还好说,若是删除文件夹,连带着会删除很多项目,这样去显示的时候就比较麻烦
            考虑了一下,若是文件夹下面是空的,那么可以删除,若是文件夹下面有东西,那么不允许删除。
        '''
        if(os.path.exists(path_str)):
            if(os.path.isdir(path_str)):
                # 是文件夹
                try:
                    # os.rmdir(path_str)
                    shell.SHFileOperation((0, shellcon.FO_DELETE, path_str, None, shellcon.FOF_SILENT |
                                          shellcon.FOF_ALLOWUNDO | shellcon.FOF_NOCONFIRMATION, None, None))  # 删除文件到回收站
                except OSError:
                    tkinter.messagebox.showinfo(
                        title="失败", message="文件夹:{}  删除失败,原因是:\n目标文件夹非空".format(path_str))
                    return False
                return True
            else:
                try:
                    # os.remove(path_str)
                    shell.SHFileOperation((0, shellcon.FO_DELETE, path_str, None, shellcon.FOF_SILENT |
                                          shellcon.FOF_ALLOWUNDO | shellcon.FOF_NOCONFIRMATION, None, None))  # 删除文件到回收站
                except Exception as e:
                    tkinter.messagebox.showinfo(
                        title="失败", message="文件:{}  删除失败,原因是:\n{}".format(path_str, str(e)))
                    return False
                return True
        else:
            # 文件不存在了,这时候需要更新一下显示,所以仍然返回True。同时也做一个提示
            tkinter.messagebox.showinfo(
                title="提示", message="文件(夹):{}  不存在".format(path_str))
            return True

    def __scroll_bar(self):
        """滚动条"""
        # Y轴
        self.__scrollBar_y = tkinter.Scrollbar(
            master=self.__tableView, command=self.__tableView.yview)
        self.__scrollBar_y.pack(side=tkinter.RIGHT, fill=tkinter.Y)
        # X轴
        self.__scrollBar_x = tkinter.Scrollbar(
            orient=tkinter.HORIZONTAL, master=self.__tableView, command=self.__tableView.xview)
        self.__scrollBar_x.pack(side=tkinter.BOTTOM, fill=tkinter.X)

    def __filling_init(self, data):
        """
        将扫描的数据填充到控件中,仅填充前1000行(由self.__max_showLine控制)
        """
        def size_show(size):
            '''友好化文件大小字符串'''
            unit_dict = {0: "B", 1: "KB", 2: "MB", 3: "GB", 4: "TB", 5: "PB"}
            size = int(size)
            size_ = size
            unit_flag_int = 0
            while size_ > 1024:
                size_ = size_ / 1024
                unit_flag_int += 1
            ret_size = "{}{}".format(round(size_, 2), unit_dict[unit_flag_int])
            return ret_size
        if data != None and len(data) != 0:
            num = 0
            # 清空
            [self.__tableView.delete(id) for id in self.__tableView.get_children()]
            for line in data:
                num += 1
                try:
                    self.__tableView.insert("", index=tkinter.END, text=line[0], value=( line[1], line[2], line[3], line[4], size_show(line[5]),self.__attributes_analysis(line[6])))
                except tkinter.TclError:
                    #这里因为tk的缺陷,导致有些utf8字符是不能再tk里面显示的,比如:“Dusk DJ REMIX �🔥 女”,这个火的符号,是不能显示的。
                    print(line[0])
                if num > self.__max_showLine:
                    break
            self.__statusBar.config(text="查询出{}个结果;查询花费:{}秒。当前显示文件类型:{}".format(len(data), time.time() - self.__stratTime,self.__map_dict__[self.__filter_condition]), fg="black")
        else:
            # 清空
            [self.__tableView.delete(id) for id in self.__tableView.get_children()]
            self.__statusBar.config(text="查询出{}个结果;查询花费:{}秒。当前显示文件类型:{}".format(len(data), time.time() - self.__stratTime,self.__map_dict__[self.__filter_condition]), fg="red")
        
        text_result = re.findall(r"(?<=结果数量:)\d+",self.__statusBar_right_TEXT)
        if len(text_result) == 0:
            self.__statusBar_right_TEXT += "结果数量:{};".format(len(data))
        else:
            self.__statusBar_right_TEXT = re.sub(r'\d+','{}'.format(len(data)),self.__statusBar_right_TEXT)
        self.__statusBar_right.config(text = self.__statusBar_right_TEXT)
        # 填充一行空行,使得原本不能被显示完全的最后一行显示完全。同是避免搜索结果为空时,不可刷新
        self.__tableView.insert("", index=tkinter.END,text="", value=("", "", "", "", ""))
    def __attributes_analysis(self,attributes):
        '''
        解析文件属性,返回字符串
        '''
        attributes = int(attributes)
        ret_str = ""
        if attributes & stat.FILE_ATTRIBUTE_READONLY == stat.FILE_ATTRIBUTE_READONLY:
            ret_str += '只读、'
        if attributes & stat.FILE_ATTRIBUTE_HIDDEN == stat.FILE_ATTRIBUTE_HIDDEN:
            ret_str += '隐藏、'
        if attributes & stat.FILE_ATTRIBUTE_SYSTEM == stat.FILE_ATTRIBUTE_SYSTEM:
            ret_str += '操作系统占用、'
        if attributes & stat.FILE_ATTRIBUTE_NORMAL == stat.FILE_ATTRIBUTE_NORMAL:
            ret_str += '无属性、'
        if attributes & stat.FILE_ATTRIBUTE_TEMPORARY == stat.FILE_ATTRIBUTE_TEMPORARY:
            ret_str += '临时、'
        if attributes & stat.FILE_ATTRIBUTE_SPARSE_FILE == stat.FILE_ATTRIBUTE_SPARSE_FILE: 
            #稀疏文件:其中很多数据为零的文件被称为包含 稀疏数据集。 例如,文件通常非常大,如包含要处理的图像数据的文件,或高速数据库内的矩阵。 包含稀疏数据集的文件的问题在于,大多数文件都不包含有用的数据,因此,它们对磁盘空间的使用效率低下。
            #https://docs.microsoft.com/zh-cn/windows/win32/fileio/sparse-files
            ret_str += '稀疏、'
        if attributes & stat.FILE_ATTRIBUTE_COMPRESSED == stat.FILE_ATTRIBUTE_COMPRESSED:
            ret_str += '压缩、'
        if attributes & stat.FILE_ATTRIBUTE_OFFLINE == stat.FILE_ATTRIBUTE_OFFLINE:
            ret_str += '脱机、' #文件的数据无法立即使用。 此属性指示文件数据以物理方式移动到脱机存储。 此属性由远程存储存储,即分层存储管理软件。 应用程序不应任意更改此属性。
        if attributes & stat.FILE_ATTRIBUTE_NOT_CONTENT_INDEXED == stat.FILE_ATTRIBUTE_NOT_CONTENT_INDEXED:
            ret_str += '不被索引的、'
        if attributes & stat.FILE_ATTRIBUTE_ENCRYPTED == stat.FILE_ATTRIBUTE_ENCRYPTED:
            ret_str += '加密、'
        if attributes & stat.FILE_ATTRIBUTE_VIRTUAL == stat.FILE_ATTRIBUTE_VIRTUAL:
            ret_str += '系统保留、'
        if attributes & stat.FILE_ATTRIBUTE_INTEGRITY_STREAM == stat.FILE_ATTRIBUTE_INTEGRITY_STREAM:
            ret_str += '完整性保护、'   #该属性值只在refs文件系统上受支持,这是苹果设备上的东西(为了方便将Windows移植到苹果上),ntfs文件系统不会有这玩意。
        if attributes & stat.FILE_ATTRIBUTE_REPARSE_POINT == stat.FILE_ATTRIBUTE_REPARSE_POINT:
            ret_str += '符号链接、' #非快捷方式
        if attributes & stat.FILE_ATTRIBUTE_DEVICE == stat.FILE_ATTRIBUTE_DEVICE:
            ret_str += '系统设备文件、'
        if attributes & stat.FILE_ATTRIBUTE_DIRECTORY == stat.FILE_ATTRIBUTE_DIRECTORY:
            ret_str += '目录、'
        if attributes & stat.FILE_ATTRIBUTE_ARCHIVE == stat.FILE_ATTRIBUTE_ARCHIVE:
            ret_str += '普通文件、'
        if ret_str[-1] == '':
            return ret_str[0:-1]


    def __tableView_doubleClike(self, event):
        """数据区域的双击事件,根据双击的位置做出不同的响应"""
        e = event.widget  # 获取发出事件的控件
        if len(e.get_children()) == 0:
            # 若是没有子项就不做处理
            return
        column = e.identify("column", event.x, event.y)  # 获取双击所在的列
        id_ = e.identify("item", event.x, event.y)

        ret = True  # 是否打开成功
        if(column == "#1"):
            # 双击的路径,打开文件资源管理器,定位到这个路径下
            path_str = e.item(id_, "values")[0]
            ret = self.__open_fileLocation_run(path_str)
        else:
            # 其他地方
            path_str = "{}\\{}".format(e.item(id_, "values")[
                                       0], e.item(id_, "text"))
            if path_str == '\\':
                self.__statusBar.config(text="非法路径", fg="red")
                return
            ret = self.__open_file(path_str)
        label_str = self.__statusBar.cget(key="text")  # 获取text的值
        if ret:
            self.__statusBar.config(
                text="{};打开成功!".format(label_str), fg="green")
        else:
            self.__statusBar.config(
                text="{};打开失败!".format(label_str), fg="red")

    def __open_file(self, filepath):
        '''
        打开指定的文件,使用系统默认方式;
        打开之前会检查文件/目录是否存在。
        成功返回True,不成功返回False
        '''
        if os.path.exists(filepath):
            try:
                os.startfile(filepath)
            except Exception as e:
                errInfo = str(e)
                self.__statusBar.config(text=errInfo)
                return False
            return True
        else:
            self.__statusBar.config(text="该文件已经不存在了。", fg="red")
            return False

    def __open_fileLocation_run(self, filepath):
        '''打开文件所在的位置,执行代码,主要是使得目标文件在资源管理器中被选定。'''
        if os.path.exists(filepath):
            try:
                # os.startfile(filepath)
                subprocess.run("Explorer.exe /select,{}".format(filepath))
            except OSError as msg:
                traceback.print_exc()
                return False
            return True
        else:
            self.__statusBar.config(text="该文件已经不存在了。", fg="red")
            return False

    def __open_files(self, event):
        '''多文件打开'''
        e = event.widget  # 获取发出事件的控件
        selec_line_id = e.selection()  # 返回选中的行ID元组
        column = e.identify("column", event.x, event.y)  # 获取右击所在的列
        filepaths_list = []
        [filepaths_list.append("{}\\{}".format(e.item(selec_x_i, "values")[
                               0], e.item(selec_x_i, "text"))) for selec_x_i in selec_line_id]
        if len(filepaths_list) == 1 and filepaths_list[0] == '\\':
            self.__statusBar.config(text="非法路径", fg="red")
            return
        if len(filepaths_list) > 5:
            choose_ret = tkinter.messagebox.askquestion(
                title="警告", message="你想要打开数量超过5个的文件集合,确定吗?")
            if choose_ret == 'yes':
                pass
            else:
                self.__statusBar.config(text="取消打开!", fg="black")
                return
        for filepath in filepaths_list:
            ret = self.__open_file(filepath)
            label_str = self.__statusBar.cget(key="text")  # 获取text的值
            if ret:
                self.__statusBar.config(
                    text="{};打开成功!".format(label_str), fg="green")
            else:
                self.__statusBar.config(
                    text="{};打开失败!".format(label_str), fg="red")

    def __copyFiles_toClip(self, event):
        '''复制文件到Windows剪贴板'''
        # 参考:https://chowdera.com/2021/10/20211031055535475l.html
        # 这其实是一个结构体,用以记录文件的各种信息。
        class DROPFILES(ctypes.Structure):
            _fields_ = [
                ("pFiles", ctypes.c_uint),
                ("x", ctypes.c_long),
                ("y", ctypes.c_long),
                ("fNC", ctypes.c_int),
                # 指示文件是否包含 ANSI 或 Unicode 字符。如果值为零,则文件包含 ANSI 字符。否则,它包含 Unicode 字符。
                ("fWide", ctypes.c_bool),
            ]

        pDropFiles = DROPFILES()
        pDropFiles.pFiles = ctypes.sizeof(DROPFILES)
        pDropFiles.fWide = True
        #a = bytes(pDropFiles)

        # 获取文件绝对路径
        e = event.widget  # 获取发出事件的控件
        selec_line_id = e.selection()  # 返回选中的行ID元组
        column = e.identify("column", event.x, event.y)  # 获取右击所在的列
        filepaths_list = []
        [filepaths_list.append("{}\\{}".format(e.item(selec_x_i, "values")[
                               0], e.item(selec_x_i, "text"))) for selec_x_i in selec_line_id]
        if len(filepaths_list) == 1 and filepaths_list[0] == '\\':
            self.__statusBar.config(text="非法路径", fg="red")
            return
        if len(filepaths_list) > 500:
            choose_ret = tkinter.messagebox.askquestion(
                title="警告", message="你想要复制数量超过500个的文件集合,确定吗?")
            if choose_ret == 'yes':
                pass
            else:
                self.__statusBar.config(text="取消复制!", fg="black")
                return
        files = ("\0".join(filepaths_list)).replace("/", "\\")
        # 结尾一定要两个\0\0字符,这是规定!就是构造NULL字符
        data = files.encode("U16")[2:] + b"\0\0"

        '''
        对于多个文本路径,我们如何将其转换为我们需要的Unicode 双字节形式呢?
        首先,我们要知道Unicode编码采用UCS-2格式直接存储,而UTF-16恰好对应于UCS-2的,即UCS-2指定的码位通过大端或小端的方式直接保存。UTF-16 有三种类型:UTF-16,UTF-16BE(大端序),UTF-16LE(小端序).UTF-16 通过以名称BOM(字节顺序标记,U + FEFF)启动文件来指示该文件仍然是小端序。
        我们只需要把python String使用UTF-16编码后,去掉前两个字节,得到相应的Unicode双字节。
        '''
        win32clipboard.OpenClipboard()  # 打开剪贴板(独占)
        try:
            # 若要将信息放在剪贴板上,首先需要使用 EmptyClipboard 函数清除任何以前的剪贴板内容
            win32clipboard.EmptyClipboard()  # 清空当前的剪贴板信息,否则不能写入

            #uDropEffect = win32clipboard.RegisterClipboardFormat("Preferred DropEffect")

            win32clipboard.SetClipboardData(
                win32clipboard.CF_HDROP, bytes(pDropFiles)+data)  # 设置当前剪贴板数据
            # win32clipboard.SetClipboardData(uDropEffect,b'\x02\x00\x00\x00')
            #print("error= ",ctypes.windll.kernel32.GetLastError())

            self.__statusBar.config(text="复制成功!", fg="green")
        except Exception as e:
            exc_type, exc_value, exc_traceback_obj = sys.exc_info()
            traceback.print_tb(exc_traceback_obj)
            self.__statusBar.config(text="复制失败!", fg="red")
        finally:
            win32clipboard.CloseClipboard()  # 无论什么情况,都关闭剪贴板

    def __cutFiles_toClip(self, event):
        '''剪切文件到剪贴板'''
        # 这其实是一个结构体,用以记录文件的各种信息。
        class DROPFILES(ctypes.Structure):
            _fields_ = [
                ("pFiles", ctypes.c_uint),
                ("x", ctypes.c_long),
                ("y", ctypes.c_long),
                ("fNC", ctypes.c_int),
                # 指示文件是否包含 ANSI 或 Unicode 字符。如果值为零,则文件包含 ANSI 字符。否则,它包含 Unicode 字符。
                ("fWide", ctypes.c_bool),
            ]

        pDropFiles = DROPFILES()
        pDropFiles.pFiles = ctypes.sizeof(DROPFILES)
        pDropFiles.fWide = True
        # 获取文件绝对路径
        e = event.widget  # 获取发出事件的控件
        selec_line_id = e.selection()  # 返回选中的行ID元组
        column = e.identify("column", event.x, event.y)  # 获取右击所在的列
        filepaths_list = []
        [filepaths_list.append("{}\\{}".format(e.item(selec_x_i, "values")[
                               0], e.item(selec_x_i, "text"))) for selec_x_i in selec_line_id]
        if len(filepaths_list) == 1 and filepaths_list[0] == '\\':
            self.__statusBar.config(text="非法路径", fg="red")
            return
        if len(filepaths_list) > 50:
            choose_ret = tkinter.messagebox.askquestion(
                title="警告", message="你想要剪切数量超过50个的文件集合,确定吗?")
            if choose_ret == 'yes':
                pass
            else:
                self.__statusBar.config(text="取消剪切!", fg="black")
                return
        files = ("\0".join(filepaths_list)).replace("/", "\\")
        # 结尾一定要两个\0\0字符,这是规定!就是构造NULL字符
        data = files.encode("U16")[2:] + b"\0\0"

        '''
        对于多个文本路径,我们如何将其转换为我们需要的Unicode 双字节形式呢?
        首先,我们要知道Unicode编码采用UCS-2格式直接存储,而UTF-16恰好对应于UCS-2的,即UCS-2指定的码位通过大端或小端的方式直接保存。UTF-16 有三种类型:UTF-16,UTF-16BE(大端序),UTF-16LE(小端序).UTF-16 通过以名称BOM(字节顺序标记,U + FEFF)启动文件来指示该文件仍然是小端序。
        我们只需要把python String使用UTF-16编码后,去掉前两个字节,得到相应的Unicode双字节。
        '''
        win32clipboard.OpenClipboard()  # 打开剪贴板(独占)
        try:
            # 若要将信息放在剪贴板上,首先需要使用 EmptyClipboard 函数清除任何以前的剪贴板内容
            win32clipboard.EmptyClipboard()  # 清空当前的剪贴板信息,否则不能写入

            uDropEffect = win32clipboard.RegisterClipboardFormat(
                "Preferred DropEffect")

            win32clipboard.SetClipboardData(
                win32clipboard.CF_HDROP, bytes(pDropFiles)+data)  # 设置当前剪贴板数据
            win32clipboard.SetClipboardData(uDropEffect, b'\x02\x00\x00\x00')
            err = ctypes.windll.kernel32.GetLastError()
            if err == 0:
                self.__statusBar.config(text="剪切成功!", fg="green")
            else:
                self.__statusBar.config(
                    text="剪切失败!errorCode={}".format(err), fg="red")
        except Exception as e:
            exc_type, exc_value, exc_traceback_obj = sys.exc_info()
            traceback.print_tb(exc_traceback_obj)
            self.__statusBar.config(text="剪切失败!", fg="red")
        finally:
            win32clipboard.CloseClipboard()  # 无论什么情况,都关闭剪贴板

    def __open_fileLocation(self, event):
        '''打开文件所在的位置'''
        # 获取文件的绝对路径
        e = event.widget  # 获取发出事件的控件
        selec_line_id = e.selection()  # 返回选中的行ID元组
        column = e.identify("column", event.x, event.y)  # 获取右击所在的列
        filepaths_list = []
        [filepaths_list.append("{}\\{}".format(e.item(selec_x_i, "values")[
                               0], e.item(selec_x_i, "text"))) for selec_x_i in selec_line_id]
        if len(filepaths_list) == 1 and filepaths_list[0] == '\\':
            self.__statusBar.config(text="非法路径", fg="red")
            return
        if len(filepaths_list) > 1:
            choose_ret = tkinter.messagebox.askquestion(
                title="警告", message="你想要打开{}个文件的所在位置,确定吗?".format(len(filepaths_list)))
            if choose_ret == 'yes':
                pass
            else:
                self.__statusBar.config(text="取消打开文件所在位置!", fg="black")
                return
        for path in filepaths_list:
            self.__open_fileLocation_run(path)

    def __close_to_missionBoard(self):
        '''关闭,到任务栏'''
        self.__root.iconify()  # 实际上是最小化了

    def Hidden_window(self, icon=r'python_everything.ico', hover_text="python everything"):  # ico文件一定要是合法的,不能使用改后缀的方式
        '''隐藏窗口至托盘区,调用SysTrayIcon的重要函数'''

        # 托盘图标右键菜单, 格式: ('name', None, callback),下面也是二级菜单的例子
        # 24行有自动添加‘退出’,不需要的可删除
        menu_options = (
            # ('显示窗口', None, lambda:s.SysTrayIcon.destroy(exit = 0)),  #我放到后面去了。
            # ('二级 菜单', None, (('更改 图标', None, s.switch_icon), )))
            #('退出', None, s.exit)
        )
        self.__root.withdraw()  # 隐藏tk窗口

        if not self.__SysTrayIcon:
            self.__SysTrayIcon = sysTrayIcon.SysTrayIcon(
                icon,  # 图标
                hover_text,  # 光标停留显示文字
                menu_options,  # 右键菜单
                on_quit=self.exit_off,  # 退出调用
                tk_window=self.__root,  # Tk窗口
            )
        self.__SysTrayIcon.activation()

    def exit_off(self):
        '''退出'''
        '''
        if self.__threading_Operating_condition.is_alive():
            #若子进程还存活,等待子进程结束
            self.__threading_Operating_condition.join()
        '''
        os.kill(self.__pid, signal.SIGBREAK)
        # self.__root.destroy()

    def __searchBar_focus(self, event):
        '''快捷键Ctrl+f,捕捉到这个事件后,使搜索框得到焦点'''
        self.__searchBar.focus_set()  # 使得搜索框获得焦点,便于快速输入搜索表达式
        self.__searchBar.selection_range(0,tkinter.END) #选择全部内容

    def __tableView_sortColumn(self, col_int):
        '''排序;col_int表示第几列(从0开始算)'''
        self.__search_result.sort(key=lambda x: int(
            x[col_int]), reverse=self.__sortFlag_col_size)
        self.__sortFlag_col_size = not self.__sortFlag_col_size

        self.__type_filter(self.__filter_condition)
        self.__filling_init(self.__show_data)

        if not self.__sortFlag_col_size:
            self.__statusBar.config(text="从大到小排序", fg="green")
        else:
            self.__statusBar.config(text="从小到大排序", fg="green")

    def __type_filter(self, type_flag):
        '''筛选指定的类型文件
            type_flag取值:
            all:所有文件
            zip:压缩文件
            audio:音频文件
            video:视频文件
            picture:图片文件
            document:文档文件
            execute:可执行文件
            folder:文件夹
            lnk:快捷方式
        '''
        
        type_dict = {'zip': ['rar', 'zip', 'cab', 'arj', 'lzh', 'ace', '7z', 'tar', 'gzip', 'uue', 'bz2', 'jar', 'iso', 'z'],
                     'audio': ['mp3', 'wma', 'wav', 'mid', 'ape', 'flac', 'weba', 'webm', 'cda', 'aif', 'aiff', 'ra', 'vqf', ],
                     'video': ['mpg', 'mpeg', 'avi', 'rmvb', 'mov', 'wmv', 'asf', 'asx', 'wvx', 'mpe', 'mpa', 'mp4', 'rm', 'navi', 'mkv', 'ogg', 'mod', ],
                     'picture': ['bmp', 'jpeg', 'png', 'tiff', 'gif', 'pcx', 'tga', 'exif', 'fpx', 'svg', 'psd', 'ico', 'jpg', 'wmf', 'tif', 'webp', ],
                     'document': ['pptx', 'pptm', 'ppt', 'pdf', 'xps', 'potx', 'potm', 'pot', 'thmx', 'ppsx', 'pps', 'ppam', 'ppa', 'xml', 'json', 'log', 'config', 'rtf', 'odp', 'wps', 'doc', 'docx', 'docm', 'dotx', 'dotm', 'dot', 'mht', 'mhtml', 'htm', 'html', 'odt', 'xlsx', 'xlsm', 'xlsb', 'xls', 'csv', 'xltx', 'xltm', 'xlt', 'prn', 'dif', 'slk', 'xlam', 'xla', 'ods', 'java', 'class', 'jav', 'bat', 'cmd', 'bib', 'c', 'cs', 'csx', 'cake', 'cpp', 'cc', 'cxx', 'c++', 'hpp', 'hh', 'h++', 'h', 'ii', 'clj', 'cljs', 'cljc', 'cljx', 'clojure', 'edn', 'cmake', 'coffee', 'cson', 'iced', 'css', 'cu', 'cuh', 'dart', 'i', 'diff', 'patch', 'rej', 'dockerfile', 'containerfile', 'fs', 'fsi', 'fsx', 'fsscript', 'go', 'groovy', 'gvy', 'gradle', 'jenkinsfile', 'nf', 'handlebars', 'hbs', 'hjs', 'hlsl', 'hlsli', 'fx', 'fxh', 'vsh', 'psh', 'cginc', 'compute', 'shtml', 'xhtml', 'xht', 'mdoc', 'jsp', 'asp', 'aspx', 'jshtm', 'js', 'gitgnore_global', 'hitigonre', 'npmignore', 'ini', 'es6', 'mjs', 'cjs', 'pac', 'jsx', 'j2', 'jinja2', 'bowerrc', 'jscsrc', 'webmainfest', 'js.map', 'css.map', 'ts.map', 'har', 'jslintrc', 'jsonld', 'jsonc', 'eslintrc', 'eslintrc.json', 'jsfmtrc', 'swcrc', 'hintrc', 'babelrc', 'code-workspace', 'language-configuration.json', 'jl', 'jmd', 'ipynb', 'tex', 'ltx', 'ctx', 'less', 'lua', 'mak', 'mk', 'md', 'mkd', 'mdwn', 'mdown', 'markdown', 'markdn', 'mdtxt', 'mdtext', 'workbook', 'm', 'mm', 'pl', 'pm', 'pod', 't', 'psgi', 'p6', 'pl6', 'pm6', 'nqp', 'php', 'php4', 'php5', 'phtml', 'ctp', 'ps1', 'psm1', 'psd1', 'pssc', 'psrc', 'properties', 'cfg', 'conf', 'directory', 'giattributes', 'gitconfig', 'gitmodules', 'editorconfig', 'npmrc', 'pug', 'jade', 'py', 'rpy', 'pyw', 'cpy', 'gyp', 'gypi', 'pyi', 'ipy', 'pyt', 'r', 'rhistory', 'rprofile', 'rt', 'cshtml', 'rb', 'rbx', 'rjs', 'gemspec', 'rake', 'ru', 'erb', 'podspec', 'rbi', 'rs', 'scss', 'code-search', 'shader', 'sh', 'bash', 'bashrc', 'bash_aliases', 'bash_profile', 'bash_login', 'ebuild', 'profile', 'bash_logout', 'xprofile', 'sql', 'dsql', 'swift', 'sty', 'cls', 'bbx', 'cbx', 'cts', 'mts', 'tsx', 'vb', 'brs', 'vbs', 'bas', 'vba', 'xsd', 'ascx', 'atom', 'axml', 'axaml', 'bpmn', 'cpt', 'csl', 'csproj', 'xsl', 'xslt', 'yml', 'eyaml', 'eyml', 'yaml', 'cff', 'txt', ],
                     'execute': ['exe', 'msi', 'bat', 'scr', 'cmd', 'bat', 'com', ],
                     'lnk': ['lnk'],
                     }
        self.__statusBar_right_TEXT = re.sub(r'(?<=显示).+?(?=;)', self.__map_dict__[type_flag], self.__statusBar_right_TEXT)
        self.__statusBar_right.config(text=self.__statusBar_right_TEXT)
        
        # 初始化显示数据
        self.__show_data = []

        if type_flag == 'all':
            self.__show_data = self.__search_result
        elif type_flag == 'folder':
            for i_list in self.__search_result:
                if int(i_list[-1]) & stat.FILE_ATTRIBUTE_DIRECTORY == stat.FILE_ATTRIBUTE_DIRECTORY:
                    self.__show_data.append(i_list)
        elif type_flag == 'zip':
            for i_list in self.__search_result:
                suffix_str = re.findall(r'(?<=\.)[^\.]+$', i_list[0])
                if len(suffix_str) > 0:
                    if suffix_str[0] in type_dict['zip']:
                        self.__show_data.append(i_list)
        elif type_flag == 'audio':
            for i_list in self.__search_result:
                suffix_str = re.findall(r'(?<=\.)[^\.]+$', i_list[0])
                if len(suffix_str) > 0:
                    if suffix_str[0] in type_dict['audio']:
                        self.__show_data.append(i_list)
        elif type_flag == 'video':
            for i_list in self.__search_result:
                suffix_str = re.findall(r'(?<=\.)[^\.]+$', i_list[0])
                if len(suffix_str) > 0:
                    if suffix_str[0] in type_dict['video']:
                        self.__show_data.append(i_list)
        elif type_flag == 'picture':
            for i_list in self.__search_result:
                suffix_str = re.findall(r'(?<=\.)[^\.]+$', i_list[0])
                if len(suffix_str) > 0:
                    if suffix_str[0] in type_dict['picture']:
                        self.__show_data.append(i_list)
        elif type_flag == 'document':
            for i_list in self.__search_result:
                suffix_str = re.findall(r'(?<=\.)[^\.]+$', i_list[0])
                if len(suffix_str) > 0:
                    if suffix_str[0] in type_dict['document']:
                        self.__show_data.append(i_list)
        elif type_flag == 'execute':
            for i_list in self.__search_result:
                suffix_str = re.findall(r'(?<=\.)[^\.]+$', i_list[0])
                if len(suffix_str) > 0:
                    if suffix_str[0] in type_dict['execute']:
                        self.__show_data.append(i_list)
        elif type_flag == 'lnk':
            for i_list in self.__search_result:
                suffix_str = re.findall(r'(?<=\.)[^\.]+$', i_list[0])
                if len(suffix_str) > 0:
                    if suffix_str[0] in type_dict['lnk']:
                        self.__show_data.append(i_list)
        else:
            tkinter.messagebox.showinfo(title="异常", message="参数有误:{}".format(type_flag)) 

    def __filter_console(self, condition, index):
        '''显示condition所指定类型的文件
            condition:字符串
            index:下标,int
        '''
        self.__stratTime = time.time()
        [self.__filterStatus_list[i].set(False) for i in range(9)]
        if index < len(self.__filterStatus_list):
            self.__filterStatus_list[index].set(True)
        else:
            tkinter.messagebox.showinfo(title="异常", message="筛选文件类型时,传入的下标发生越界。函数:__filter_console")
            return
        self.__filter_condition = condition
        self.__type_filter(self.__filter_condition)
        self.__filling_init(self.__show_data)
        num = len(self.__show_data)
        if num > 0:
            if num > self.__max_showLine:
                self.__statusBar.config(
                    text="筛选出{}个结果,此处只显示前{}个结果。所用时间:{};当前显示文件类型:{}".format(num,self.__max_showLine,time.time()-self.__stratTime,self.__map_dict__[self.__filter_condition]), fg="green")
            else:
                self.__statusBar.config(
                    text="筛选出{}个结果。所用时间:{};当前显示文件类型:{}".format(num,time.time()-self.__stratTime,self.__map_dict__[self.__filter_condition]), fg="green")
        else:
            self.__statusBar.config(text="没有此类型文件。当前显示文件类型:{}".format(self.__map_dict__[self.__filter_condition]), fg="red")
    def __pathFilter(self):
        '''开启路径筛选
        显示路径输入框,位于搜索框的右侧
        路径匹配从左到右严格匹配
        '''
        if self.__path_filter_Status_bool:
            self.__path_filter_Status.set(False)
            self.__path_filter_Status_bool = False
            self.__pathBar.forget() #隐藏控件
            self.__statusBar_right_TEXT = self.__statusBar_right_TEXT.replace("开启路径筛选","")
            self.__statusBar_right.config(text=self.__statusBar_right_TEXT)
        else:
            #显示
            self.__path_filter_Status.set(True)
            self.__path_filter_Status_bool = True
            self.__pathBar.config()
            self.__pathBar.pack(side=tkinter.RIGHT,anchor=tkinter.E)
            self.__statusBar_right_TEXT = self.__statusBar_right_TEXT + "开启路径筛选"
            self.__statusBar_right.config(text = self.__statusBar_right_TEXT)
            

    def show(self):
        """#显示界面"""
        self.__root.mainloop()

 

 

 

 

 

 

sysTrayIcon.py

import win32api
import win32con
import win32gui_struct
import win32gui
import os


class SysTrayIcon (object):
    '''SysTrayIcon类用于显示任务栏图标'''
    QUIT = 'QUIT'
    SPECIAL_ACTIONS = [QUIT]
    FIRST_ID = 5320

    def __init__(s, icon, hover_text, menu_options, on_quit, tk_window=None, default_menu_index=None, window_class_name=None):
        '''
        icon         需要显示的图标文件路径
        hover_text   鼠标停留在图标上方时显示的文字
        menu_options 右键菜单,格式: (('a', None, callback), ('b', None, (('b1', None, callback),)))
        on_quit      传递退出函数,在执行退出时一并运行
        tk_window    传递Tk窗口,s.root,用于单击图标显示窗口
        default_menu_index 不显示的右键菜单序号
        window_class_name  窗口类名
        '''
        s.icon = icon
        s.hover_text = hover_text
        s.on_quit = on_quit
        s.root = tk_window
        #菜单是由上往下显示的,比如下面这个,显示在退出的上面
        menu_options = menu_options + (("显示", None, lambda x: s.destroy(exit=0))  # 这里回调的时候,系统会传递一个参数,所以lambda表达式需要指明可以接受一个参数
                                       , ('退出', None, s.destroy))
        s._next_action_id = s.FIRST_ID
        s.menu_actions_by_id = set()
        s.menu_options = s._add_ids_to_menu_options(list(menu_options))
        s.menu_actions_by_id = dict(s.menu_actions_by_id)
        del s._next_action_id

        s.default_menu_index = (default_menu_index or 0)
        s.window_class_name = window_class_name or "SysTrayIconPy"

        message_map = {win32gui.RegisterWindowMessage("TaskbarCreated"): s.restart,
                       win32con.WM_DESTROY: s.destroy,
                       win32con.WM_COMMAND: s.command,
                       win32con.WM_USER+20: s.notify, }
        # 注册窗口类。
        wc = win32gui.WNDCLASS()
        wc.hInstance = win32gui.GetModuleHandle(None)
        wc.lpszClassName = s.window_class_name
        wc.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW
        wc.hCursor = win32gui.LoadCursor(0, win32con.IDC_ARROW)
        wc.hbrBackground = win32con.COLOR_WINDOW
        wc.lpfnWndProc = message_map  # 也可以指定wndproc.
        s.classAtom = win32gui.RegisterClass(wc)

    def activation(s):
        '''激活任务栏图标,不用每次都重新创建新的托盘图标'''
        hinst = win32gui.GetModuleHandle(None)  # 创建窗口。
        style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
        s.hwnd = win32gui.CreateWindow(s.classAtom,
                                       s.window_class_name,
                                       style,
                                       0, 0,
                                       win32con.CW_USEDEFAULT,
                                       win32con.CW_USEDEFAULT,
                                       0, 0, hinst, None)
        win32gui.UpdateWindow(s.hwnd)
        s.notify_id = None
        s.refresh(title='软件已后台!', msg='点击图标重新打开', time=500)  # 发送气泡通知

        win32gui.PumpMessages()

    def refresh(s, title='', msg='', time=500):
        '''刷新托盘图标
           title 标题
           msg   内容,为空的话就不显示提示
           time  提示显示时间'''
        hinst = win32gui.GetModuleHandle(None)
        if os.path.isfile(s.icon):
            icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
            hicon = win32gui.LoadImage(hinst, s.icon, win32con.IMAGE_ICON,
                                       0, 0, icon_flags)
        else:  # 找不到图标文件 - 使用默认值
            hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)

        if s.notify_id:
            message = win32gui.NIM_MODIFY
        else:
            message = win32gui.NIM_ADD

        s.notify_id = (s.hwnd, 0,  # 句柄、托盘图标ID
                       # 托盘图标可以使用的功能的标识
                       win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP | win32gui.NIF_INFO,
                       win32con.WM_USER + 20, hicon, s.hover_text,  # 回调消息ID、托盘图标句柄、图标字符串
                       msg, time, title,   # 提示内容、提示显示时间、提示标题
                       win32gui.NIIF_INFO  # 提示用到的图标
                       )
        win32gui.Shell_NotifyIcon(message, s.notify_id)

    def show_menu(s):
        '''显示右键菜单'''
        menu = win32gui.CreatePopupMenu()
        s.create_menu(menu, s.menu_options)

        pos = win32gui.GetCursorPos()
        win32gui.SetForegroundWindow(s.hwnd)
        win32gui.TrackPopupMenu(menu,
                                win32con.TPM_LEFTALIGN,
                                pos[0],
                                pos[1],
                                0,
                                s.hwnd,
                                None)
        win32gui.PostMessage(s.hwnd, win32con.WM_NULL, 0, 0)

    def _add_ids_to_menu_options(s, menu_options):
        result = []
        for menu_option in menu_options:
            option_text, option_icon, option_action = menu_option
            if callable(option_action) or option_action in s.SPECIAL_ACTIONS:
                s.menu_actions_by_id.add((s._next_action_id, option_action))
                result.append(menu_option + (s._next_action_id,))
            else:
                result.append((option_text,
                               option_icon,
                               s._add_ids_to_menu_options(option_action),
                               s._next_action_id))
            s._next_action_id += 1
        return result

    def restart(s, hwnd, msg, wparam, lparam):
        s.refresh()

    def destroy(s, hwnd=None, msg=None, wparam=None, lparam=None, exit=1):
        if exit and s.on_quit:
            s.on_quit()  # 需要传递自身过去时用 s.on_quit(s)
        else:
            s.root.deiconify()  # 显示tk窗口
        nid = (s.hwnd, 0)
        win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, nid)
        win32gui.PostQuitMessage(0)  # 终止应用程序。

    def notify(s, hwnd, msg, wparam, lparam):
        '''鼠标事件'''
        if lparam == win32con.WM_LBUTTONDBLCLK:  # 双击左键
            pass
        elif lparam == win32con.WM_RBUTTONUP:  # 右键弹起
            s.show_menu()
        elif lparam == win32con.WM_LBUTTONUP:  # 左键弹起
            s.destroy(exit=0)
        return True
        """
        可能的鼠标事件:
          WM_MOUSEMOVE      #光标经过图标
          WM_LBUTTONDOWN    #左键按下
          WM_LBUTTONUP      #左键弹起
          WM_LBUTTONDBLCLK  #双击左键
          WM_RBUTTONDOWN    #右键按下
          WM_RBUTTONUP      #右键弹起
          WM_RBUTTONDBLCLK  #双击右键
          WM_MBUTTONDOWN    #滚轮按下
          WM_MBUTTONUP      #滚轮弹起
          WM_MBUTTONDBLCLK  #双击滚轮
        """

    def create_menu(s, menu, menu_options):
        for option_text, option_icon, option_action, option_id in menu_options[::-1]:
            if option_icon:
                option_icon = s.prep_menu_icon(option_icon)

            if option_id in s.menu_actions_by_id:
                item, extras = win32gui_struct.PackMENUITEMINFO(text=option_text,
                                                                hbmpItem=option_icon,
                                                                wID=option_id)
                win32gui.InsertMenuItem(menu, 0, 1, item)
            else:
                submenu = win32gui.CreatePopupMenu()
                s.create_menu(submenu, option_action)
                item, extras = win32gui_struct.PackMENUITEMINFO(text=option_text,
                                                                hbmpItem=option_icon,
                                                                hSubMenu=submenu)
                win32gui.InsertMenuItem(menu, 0, 1, item)

    def prep_menu_icon(s, icon):
        # 加载图标。
        ico_x = win32api.GetSystemMetrics(win32con.SM_CXSMICON)
        ico_y = win32api.GetSystemMetrics(win32con.SM_CYSMICON)
        hicon = win32gui.LoadImage(
            0, icon, win32con.IMAGE_ICON, ico_x, ico_y, win32con.LR_LOADFROMFILE)

        hdcBitmap = win32gui.CreateCompatibleDC(0)
        hdcScreen = win32gui.GetDC(0)
        hbm = win32gui.CreateCompatibleBitmap(hdcScreen, ico_x, ico_y)
        hbmOld = win32gui.SelectObject(hdcBitmap, hbm)
        brush = win32gui.GetSysColorBrush(win32con.COLOR_MENU)
        win32gui.FillRect(hdcBitmap, (0, 0, 16, 16), brush)
        win32gui.DrawIconEx(hdcBitmap, 0, 0, hicon, ico_x,
                            ico_y, 0, 0, win32con.DI_NORMAL)
        win32gui.SelectObject(hdcBitmap, hbmOld)
        win32gui.DeleteDC(hdcBitmap)

        return hbm

    def command(s, hwnd, msg, wparam, lparam):
        id = win32gui.LOWORD(wparam)
        s.execute_menu_option(id)

    def execute_menu_option(s, id):
        menu_action = s.menu_actions_by_id[id]
        if menu_action == s.QUIT:
            win32gui.DestroyWindow(s.hwnd)
        else:
            menu_action(s)

 

python_everything.py

import os
import UI
import time
import scan_module

a=UI.UI()
a.show()

效果示例:

双击文件名称打开文件,双击路径打开所在文件夹

 

 python_everything.ico: 

 

 

 

 

 多进程多线程模式(打包的时候有严重的bug)

python_eveything.py

只是变了一点点

import UI
import scan_module

if __name__ == "__main__":
    '''
    这个版本是多进程的,总体运行流程是:ui界面起来时,会生成子线程(使用threading),然后子线程使用Process生成扫描进程,扫描进程在根据盘符数量来生成子扫描进程,子扫描进程再根据根文件夹数量来生成对应数量的子线程来扫描。最终子扫描进程汇总数据,扫描进程处理子扫描进程回传的数据,至此扫描结束
    启动时会启动扫描进程,在搜索时也会启动扫描进程,如果已存在扫描进程,就不会再启动(判断ui子进程是否存活来实现)
    多进程多线程扫描会快一些(大概一倍多),但是相应的cpu占用也高。
    '''
    scan = scan_module.scan_module()  # 扫描模块对象
    ui = UI.UI(scan)

    ui.show()

scan_module.py

变得比较多,也存在许多实际上没有使用的代码,没有删除。

'''
本类需要用到的模块信息
'''
import os
import string
import time
from copy import _reconstruct
from multiprocessing import Process, Queue,Manager
import threading
import signal
from unicodedata import name

'''
    scan_module.py
    本文件内容是扫描模块,作用是用于扫描和保存扫描结果。
    代码不再使用或大幅度减少函数式编程代码,改为使用对象编程的方法。
    本代码只适用于Windows系统
'''


class scan_module:
    '''
    扫描模块,实现扫描的核心代码和存储扫描出来的数据。
    '''
    __all_file_dict = {}    #最终的数据存放
    Process_scan = ''       #存放进程对象
    selfnum  = 0           #扫描次数
    selftime = 0           #本次扫描用时
    scan_childPro = []      #扫描子进程对象地址
    selfcompleteSign = False#扫描完成的标志,False表示未完成,True表示已完成

    def __init__(self):
        '''
        实例化对象的时候就初始化出需要扫描的根路径
        '''
        self.__paths_list = ['C:\\Windows\\']  # 存储盘符,变量类型为列表,C:\Windows目录太庞大,做特殊处理,但是这个是针对进程版本的扫描,而非普通单线程扫描。

        self.__get_DriveLetter()  # 初始化盘符列表变量。

    def __get_DriveLetter(self):
        '''
        获取盘符列表,是通过猜测验证其是否存在。
        私有函数
        '''
        self.__paths_list = ['C:\\Windows\\']   #每次都进行初始化,不然会越来越多的进程
        for i in string.ascii_uppercase:
            if os.path.exists(i + ':\\'):
                self.__paths_list.append(i + ':\\')
            # else的情况不考虑停止,实际上,用户的磁盘盘符完全可以不连续。

    def __get_fileList(self, initial_path):
        '''
        扫描,私有函数
        '''
        try:
            child_file_list = os.scandir(initial_path)
        except:
            # 遇到异常则直接终止函数
            # print(initial_path)
            return
        for file_ in child_file_list:
            file_stat = file_.stat(follow_symlinks=False)
            time_local = time.localtime(file_stat.st_ctime)
            # Ctiem,创建时间
            file_ctime = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
            # Mtime,最近的修改时间
            time_local = time.localtime(file_stat.st_mtime)
            file_mtime = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
            # atime,最近的访问时间
            time_local = time.localtime(file_stat.st_atime)
            file_atime = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
            # size,文件大小(以字节为单位),文件可以是常规文件或符号链接。符号链接的大小是它包含的路径的长度,不包括末尾的空字节。
            file_size = int(file_stat.st_size)
            isDir = 0
            if file_.is_dir(): 
                isDir = 1 
            else: 
                isDir = 0
            #isDir应该永远位于列表的最后一位
            if file_.name in self.__all_file_dict.keys():
                self.__all_file_dict[file_.name].append(
                    [initial_path, file_ctime, file_mtime, file_atime, file_size, isDir,])
            else:
                self.__all_file_dict[file_.name] = [
                    [initial_path, file_ctime, file_mtime, file_atime, file_size, isDir,], ]
            # 是否是目录
            if file_.is_dir():
                self.__get_fileList(file_.path)
    def get_fileList_thread(self,initial_path,in_data):
        '''扫描功能,但是是线程版本的'''
        if initial_path == "C:\\Windows":
            return
        try:
            child_file_list = os.scandir(initial_path)
        except:
            # 遇到异常则直接终止函数
            # print(initial_path)
            return
        for file_ in child_file_list:
            file_stat = file_.stat(follow_symlinks=False)
            time_local = time.localtime(file_stat.st_ctime)
            # Ctiem,创建时间
            file_ctime = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
            # Mtime,最近的修改时间
            time_local = time.localtime(file_stat.st_mtime)
            file_mtime = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
            # atime,最近的访问时间
            time_local = time.localtime(file_stat.st_atime)
            file_atime = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
            # size,文件大小(以字节为单位),文件可以是常规文件或符号链接。符号链接的大小是它包含的路径的长度,不包括末尾的空字节。
            file_size = int(file_stat.st_size)
            isDir = 0
            if file_.is_dir(): 
                isDir = 1 
            else: 
                isDir = 0
            #isDir应该永远位于列表的最后一位
            if file_.name in in_data.keys():
                in_data[file_.name].append(
                    [initial_path, file_ctime, file_mtime, file_atime, file_size, isDir,])
            else:
                in_data[file_.name] = [
                    [initial_path, file_ctime, file_mtime, file_atime, file_size, isDir,], ]
            # 是否是目录
            if isDir:
                self.get_fileList_thread(file_.path,in_data)
    def get_fileList_v2(self):
        '''
        听说了一个os.walk库,专用于遍历文件存在的
        比我自己写的慢一倍。
        '''
        start_time = time.time()
        for path_i in self.__paths_list:
            for dirpath, dirnames, filenames in os.walk(path_i):
                for name in dirnames:
                    absPath = os.path.join(dirpath, name)
                    if name in self.__all_file_dict.keys():
                        self.__all_file_dict[name].append([absPath, ])
                    else:
                        self.__all_file_dict[name] = [[absPath, ], ]
                for name in filenames:
                    absPath = os.path.join(dirpath, name)
                    if name in self.__all_file_dict.keys():
                        self.__all_file_dict[name].append([absPath, ])
                    else:
                        self.__all_file_dict[name] = [[absPath, ], ]
        print("V2版本的扫描时间:{}".format(time.time() - start_time))
    def get_fileList_thread_console(self,DriveLetter,info):
        '''
        DriveLetter:盘符
        本函数是扫描功能(线程)的控制函数,做第一级控制
        由于C:\windows目录实在是太庞大,所以这个目录也需要做一级处理
        '''
        try:
            child_file_list = os.scandir(DriveLetter)
        except Exception as e:
            print("函数get_fileList_thread_console,遇到异常。原因:{}".format(e))
            return
        folder_num = 0
        thread_dict = {}
        data_dict = {}
        for file_ in child_file_list:
            #最初一层级的目录和文件也要添加进去
            file_stat = file_.stat(follow_symlinks=False)
            time_local = time.localtime(file_stat.st_ctime)
            # Ctiem,创建时间
            file_ctime = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
            # Mtime,最近的修改时间
            time_local = time.localtime(file_stat.st_mtime)
            file_mtime = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
            # atime,最近的访问时间
            time_local = time.localtime(file_stat.st_atime)
            file_atime = time.strftime("%Y-%m-%d %H:%M:%S", time_local)
            # size,文件大小(以字节为单位),文件可以是常规文件或符号链接。符号链接的大小是它包含的路径的长度,不包括末尾的空字节。
            file_size = int(file_stat.st_size)
            isDir = 0
            if file_.is_dir(): 
                isDir = 1 
            else: 
                isDir = 0
            #isDir应该永远位于列表的最后一位
            if file_.name in data_dict.keys():
                data_dict[file_.name].append(
                    [DriveLetter, file_ctime, file_mtime, file_atime, file_size, isDir,])
            else:
                data_dict[file_.name] = [
                    [DriveLetter, file_ctime, file_mtime, file_atime, file_size, isDir,], ]
            if file_.is_dir():
                folder_num += 1
                data_dict[file_.name] = {}
                thread_dict[file_.name] = threading.Thread(target=self.get_fileList_thread,args=("{}{}".format(DriveLetter,file_.name),data_dict[file_.name],))
                thread_dict[file_.name].start() #运行线程
                #print(file_.name)
        Pro_data = {}
        while True:
            #检查是否扫描完毕
            c = False    #是否全部结束
            for key in thread_dict:
                if thread_dict[key].is_alive():
                    flag = False
                    time.sleep(0.5)
                    break
                else:
                    if type([]) == type(data_dict[key]):
                        if len(data_dict[key]) > 0:
                            if key in Pro_data.keys():
                                Pro_data[key] += data_dict[key]
                            else:
                                Pro_data[key] = data_dict[key]
                    elif type({}) == type(data_dict[key]):
                        for data_i_i in data_dict[key]:
                            if data_i_i in Pro_data.keys():
                                Pro_data[data_i_i] += data_dict[key][data_i_i]
                            else:
                                try:
                                    Pro_data[data_i_i] = data_dict[key][data_i_i]
                                except KeyError as e:
                                    exit()
                    data_dict[key] = []
                    flag = True
            if flag:
                break
        info.put(Pro_data)
        
    def scanProcess_console(self,info):
        '''扫描进程控制
            但是这个不能用在tkinter中,哎,可惜
            info:是向上一级传递
        '''
        start_time = time.time()
        scanProcess_Dict = {};  #扫描进程字典,以盘符为key
        manager = Manager()
        infos = manager.Queue()
        self.__get_DriveLetter()    #扫描盘符
        self.scan_childPro = []     #初始化子进程pid列表
        for DriveLetter_i in self.__paths_list:
            scanProcess_Dict[DriveLetter_i] = Process(target = self.get_fileList_thread_console,args = (DriveLetter_i,infos,),name=DriveLetter_i)
            scanProcess_Dict[DriveLetter_i].start()         #运行进程
            self.scan_childPro.append(scanProcess_Dict[DriveLetter_i].pid)   
        info.put(self.scan_childPro)    #[]
        while True:
            flag = False
            scanProcess_Dict_copy = self.__my_deepcopy(scanProcess_Dict)
            for key in scanProcess_Dict_copy:
                if scanProcess_Dict[key].is_alive():
                    time.sleep(1)
                else:
                    '''汇总数据'''
                    valueNumber = infos.qsize(); 
                    #print("返回值的数量:{}".format(valueNumber))
                    for i in range(valueNumber):
                        data_DriveLetter = infos.get(); 
                        for data_i in data_DriveLetter:
                            if data_i in self.__all_file_dict.keys():
                                self.__all_file_dict[data_i] += data_DriveLetter[data_i]
                            else:
                                self.__all_file_dict[data_i] = data_DriveLetter[data_i]
                    del scanProcess_Dict[key]
                    if len(scanProcess_Dict) == 0:
                        flag = True
            if flag:
                info.put(self.__all_file_dict)              #{}
                info.put(time.time() - start_time)          #float
                self.formatted_output()
                return time.time() - start_time
        
        
    def __check_Process_is_alive(self,scanProcess_Dict):
        '''
        检查进程是否还有存活的,是就返回true,否返回false
        '''
        for key in scanProcess_Dict:
            if scanProcess_Dict[key].is_alive():
                return True
        return False
            
        
    def selfconsole(self):
        '''
        调用扫描函数
        '''
        start_time = time.time()
        for path_i in self.__paths_list:
            self.__get_fileList(path_i)
        return time.time() - start_time
        #print("扫描花费时间:{}".format(time.time() - start_time))

    def formatted_output(self):
        '''
        将结果字典格式化输出到文件中存储。
        输出到用户家目录下存放,名称叫python_class_eveything_data.data
        '''
        out_file_path_str = os.environ['USERPROFILE'] + \
            '\\python_class_eveything_data.data'
        with open(out_file_path_str, 'w', encoding='utf-8')as fd:
            # 偶尔会发生在迭代期间字典大小发生变化的运行时错误
            # 之前在迭代期间字典大小发生变化的bug解决了,但是这里就这样吧。
            data = self.__my_deepcopy(self.__all_file_dict)
            for key, value in data.items():
                for value_i in value:
                    line_str = key
                    for value_i_i in value_i:
                        line_str += "\t{}".format(value_i_i)
                    fd.write(line_str + '\n')
            del data

    def __my_deepcopy(self, x):
        '''
        深拷贝,copy库的deepcopy性能不太行,所以在deepcopy的基础上改了一下
        这个深拷贝函数仅用于本脚本中,copy库中的deepcopy函数慢是有原因的,了解之后再使用。
        '''
        reductor = getattr(x, "__reduce_ex__", None)
        if reductor is not None:
            rv = reductor(4)
        y = _reconstruct(x, None, *rv)
        return y

    def get_data(self):
        """
        返回数据的副本,同时删除源数据
        """
        y = self.__my_deepcopy(self.__all_file_dict)
        self.__all_file_dict = {}
        return y

    def get_file_num(self):
        '''
        获取扫描的文件数量,在执行get_data之后此函数失去准确性
        '''
        num = len(self.__all_file_dict)
        print("文件数量(只统计key):{}。".format(num))
        return num
    
    def get_data_dict(self,info):
        """获取扫描数据的实际代码"""
        #time_ = self.selfconsole()
        self.selftime = self.scanProcess_console()
        self.formatted_output()
        info.put(self.__all_file_dict)  #{}
        info.put(self.selftime)         #float
        info.put(self.scan_childPro)    #[]

    def selfconsole(self):
        """控制扫描进程"""
        manager = Manager()
        infos = manager.Queue()
        if self.Process_scan == '' or self.Process_scan.is_alive() == False:
            #扫描未开始或者扫描已结束才开始再次扫描
            self.selfcompleteSign = False
            self.Process_scan = Process(target = self.scanProcess_console,args=(infos,))
            self.Process_scan.start()
        if self.Process_scan.is_alive() == True:
            #判断进程是否结束,接受回传的数据
            while True:
                time.sleep(0.5)
                if self.Process_scan.is_alive() == False:
                    ret_num  = infos.qsize()
                    for i in range(ret_num):
                        info_data = infos.get(); 
                        if type({}) == type(info_data):
                            #print("数据已传递")
                            #扫描数据
                            self.__all_file_dict = info_data
                        elif type(1.1) == type(info_data):
                            #print("时间已传递")
                            #扫描时间
                            self.selftime = info_data
                        else:
                            print("传递了{}类型的数据。".format(type(info_data)))
                    self.selfcompleteSign = True
                    self.selfnum += 1
                    break
                else:
                    ret_num  = infos.qsize()
                    if ret_num == 1:
                        #获取子进程PID
                        info_data = infos.get(); 
                        if type([]) == type(info_data):
                            #print("pid已传递:{}".format(info_data))
                            #子进程PID
                            self.scan_childPro = info_data
                        else:
                            print("传递了{}类型的数据。".format(type(info_data)))
    def kill_scan(self):
        if self.Process_scan != "":
            if self.Process_scan.is_alive():
                if len(self.scan_childPro) > 0:
                    for i in self.scan_childPro:
                        try:
                            os.kill(int(i),signal.SIGBREAK)
                        except OSError:
                            #说明不存在或者访问被拒绝,但是不进行进一步处理
                            pass
                self.Process_scan.terminate()   #结束扫描进程

if __name__ == "__main__":
    a = scan_module()
    while True:
        print("扫描花费时间:{}".format(a.scanProcess_console()))
        a.formatted_output()
        a.get_file_num()
        a.get_data()

 

sysTrayIcon.py

这个模块没有发生变化,直接使用前面的代码即可

UI.py

有变化,但是也不太多

import ctypes  # 文件操作-剪贴板操作相关
import os

import re
import sys
import threading
import time
import tkinter
import signal
import tkinter.messagebox
import tkinter.ttk
import traceback
import subprocess
import pyperclip  # 字符串-剪贴板相关
import win32clipboard  # 文件操作-剪贴板操作相关
from win32com.shell import shell, shellcon


import sysTrayIcon


class UI:
    '''
    界面
    '''

    def __init__(self,scan_):
        self.__root = tkinter.Tk()
        self.__root.title("python_everything")
        self.__root.iconbitmap(
            r"C:\Program Files\Internet Explorer\images\bing.ico")
        self.__root.geometry("1200x500")
        self.__SysTrayIcon = None  # 值在Hidden_window函数中初始化
        # 根窗口大小改变的事件
        self.__root.bind("<Configure>", func=self.__table_cloumnSize)
        self.__root.protocol("WM_DELETE_WINDOW", self.Hidden_window)  # 重定义关闭事件
        # 绑定快捷键Ctrl+f,捕捉到这个事件后,使搜索框得到焦点
        self.__root.bind("<Control-f>", func=self.__searchBar_focus)
        # 数据
        self.__data = self.__get_data_fromFile()

        # 初始化一些变量
        self.__is_caseSensitive = False  # 是否区分大小写,FALSE表示不区分
        self.__max_showLine = 1000  #最大显示的行数
        self.__sortFlag_col_size = False #列“大小”如何排序(False表示从大到小,True表示从小到大)
        self.__search_result = self.__data   #搜索的结果
        self.__show_data = []   #最终显示的结果
        self.__filter_condition = "all" #筛选条件,在显示数据前的最后一层条件
        self.__threading_Operating_condition = ''
        self.__scan = scan_  # 扫描模块对象
        self.__scan_num = 0  # 扫描次数
        self.__pid = os.getpid()    #用来结束自己的
        self.__scan_console()

        self.__menu()
        self.__search_bar()
        self.__status_bar()
        self.__content()
    def __scan_console(self):
        """扫描以及数据获取"""
        def get_data_dict():
            """获取扫描数据的实际代码"""
            self.__scan.selfconsole()
            self.__data = self.__scan.get_data()
            self.__scan_num = self.__scan.selfnum
            # 提示扫描完毕
            self.__statusBar.config(fg="black", text="扫描完毕,用时:{}秒。".format(self.__scan.selftime))
        if self.__threading_Operating_condition == '' or self.__threading_Operating_condition.is_alive() == False:
            self.__threading_Operating_condition = threading.Thread(target=get_data_dict)
            self.__threading_Operating_condition.start()

    def __get_data_fromFile(self):
        """#从文件中获取数据"""
        in_file_path_str = os.environ['USERPROFILE'] + \
            '\\python_class_eveything_data.data'
        if(os.path.exists(in_file_path_str)):
            with open(in_file_path_str, "r", encoding='utf-8-sig') as in_fd:
                lines_list = in_fd.read().splitlines()
                lines_list_ = [i.split('\t') for i in lines_list]
                return lines_list_
        else:
            tkinter.messagebox.showinfo(
                title="提示", message="数据文件不存在,请等待扫描结果出来之后再进行搜索。")
            return []

    def __menu(self):
        """#建立菜单"""
        # 顶层菜单
        self.__ui_menu_top = tkinter.Menu(master=self.__root)
        # 文件菜单,且取消虚线和分离功能
        self.__ui_menu_file = tkinter.Menu(
            master=self.__ui_menu_top, tearoff=False)
        # 搜索菜单
        self.__ui_menu_search = tkinter.Menu(
            master=self.__ui_menu_top, tearoff=False)
        # 关联顶层菜单和文件菜单
        self.__ui_menu_top.add_cascade(label="文件", menu=self.__ui_menu_file)
        self.__ui_menu_top.add_cascade(label="搜索", menu=self.__ui_menu_search)
        # 文件菜单的菜单列表
        # self.ui_menu_file.add_command()
        #self.__ui_menu_file.add_command(label="新建窗口", command=lambda: print("ok"))
        self.__ui_menu_file.add_command(
            label="退出", command=self.exit_off)
        # 创建具有复选框的子项
        self.__demoStatus = tkinter.BooleanVar()
        self.__demoStatus.set(False)  # 初始状态为不区分大小写,__demoStatus是一个bool值记录器。
        self.__ui_menu_search.add_checkbutton(
            label="区分大小写", command=self.__case_sensitive, variable=self.__demoStatus)
        self.__ui_menu_search.add_separator()  #添加分隔线
        
        self.__filterStatus_list =[]
        [self.__filterStatus_list.append(tkinter.BooleanVar()) for i in range(9)]
        [self.__filterStatus_list[i].set(False) for i in range(9)]
        self.__filterStatus_list[0].set(True)   #所有文件这个选择时默认勾选上的
        
        self.__ui_menu_search.add_checkbutton(
            label="所有文件", command= lambda cond="all",index=0 : self.__filter_console(cond,index), variable=self.__filterStatus_list[0])
        self.__ui_menu_search.add_checkbutton(
            label="压缩文件", command=lambda cond="zip",index=1 : self.__filter_console(cond,index), variable=self.__filterStatus_list[1])
        self.__ui_menu_search.add_checkbutton(
            label="音频文件", command=lambda cond="audio",index=2 : self.__filter_console(cond,index), variable=self.__filterStatus_list[2])
        self.__ui_menu_search.add_checkbutton(
            label="视频文件", command=lambda cond="video",index=3 : self.__filter_console(cond,index), variable=self.__filterStatus_list[3])
        self.__ui_menu_search.add_checkbutton(
            label="图片文件", command=lambda cond="picture",index=4 : self.__filter_console(cond,index), variable=self.__filterStatus_list[4])
        self.__ui_menu_search.add_checkbutton(
            label="文档文件", command=lambda cond="document",index=5 : self.__filter_console(cond,index), variable=self.__filterStatus_list[5])
        self.__ui_menu_search.add_checkbutton(
            label="可执行文件", command=lambda cond="execute",index=6 : self.__filter_console(cond,index), variable=self.__filterStatus_list[6])
        self.__ui_menu_search.add_checkbutton(
            label="快捷方式", command=lambda cond="lnk",index=7 : self.__filter_console(cond,index), variable=self.__filterStatus_list[7])
        self.__ui_menu_search.add_checkbutton(
            label="文件夹", command=lambda cond="folder",index=8 : self.__filter_console(cond,index), variable=self.__filterStatus_list[8])
        # 显示菜单
        self.__root.config(menu=self.__ui_menu_top)

    def __case_sensitive(self):
        '''使得搜索区分大小写'''
        if self.__is_caseSensitive:
            # 已经勾选了要区分大小写,再次点击就是不区分大小写了
            self.__demoStatus.set(False)
            self.__is_caseSensitive = False
        else:
            self.__demoStatus.set(True)
            self.__is_caseSensitive = True

    def __status_bar(self):
        """状态栏"""
        self.__statusBar = tkinter.Label(master=self.__root, text="{}个对象".format(
            len(self.__data)), anchor=tkinter.W, relief=tkinter.RIDGE)
        self.__statusBar.pack(side=tkinter.BOTTOM, fill=tkinter.X)

    def __search_bar(self):
        """#搜索框"""
        # exportselection=0:禁止自动复制选取的字符串,当任何击键更改小组件的内容时进行回调函数
        self.__searchBar = tkinter.Entry(master=self.__root, exportselection=0)
        self.__searchBar.bind("<Return>", self.__search_console)
        self.__searchBar.pack(fill=tkinter.X)

        # 事件
        self.__searchBar.bind("<Button-3>", self.__searchBar_rightClik)

    def __searchBar_rightClik(self, event):
        '''搜索框右键,目前支持,复制,粘贴,剪切'''
        e = event.widget    # 获取发出事件的控件
        # 获取搜索框中的字符串
        searcStr = e.get()
        # 是否有选中字符
        is_checked = e.select_present()
        checked_str = ""
        start_index = 0
        end_index = 0
        if is_checked:
            # checked_str = e.select_get()    #获取选中的文本
            start_index = e.index(tkinter.ANCHOR)  # 开始选择时的index
            end_index = e.index(tkinter.INSERT)  # 结束选择时的index
            if start_index < end_index:
                # 从左向右选择
                checked_str = searcStr[start_index: end_index]
            elif start_index > end_index:
                # 从右向左选择
                checked_str = searcStr[end_index: start_index]
            else:
                checked_str = ""

        def copy_Str(Str):
            pyperclip.copy(Str)  # 将字符串发送到剪贴板上

        def cut_selectStr(Str, e):
            pyperclip.copy(Str)  # 将字符串发送到剪贴板上
            # e.select_clear()        #清除所选字符串,妈的,竟然不管用
            new_str = ""
            if start_index < end_index:
                new_str = searcStr[0:start_index] + searcStr[end_index:]
                e.delete(0, tkinter.END)
                e.insert(0, new_str)
                e.icursor(end_index - len(checked_str))  # 设置光标位置
            elif start_index > end_index:
                # 从右向左选择
                new_str = searcStr[0:end_index] + searcStr[start_index:]
                e.delete(0, tkinter.END)
                e.insert(0, new_str)
                e.icursor(start_index - len(checked_str))  # 设置光标位置
            else:
                new_str = ""

        def cut_allStr(Str, e):
            pyperclip.copy(Str)  # 将字符串发送到剪贴板上
            e.delete(0, tkinter.END)  # 清空整个字符串

        def paste(e):
            str_ = pyperclip.paste()  # 获取剪贴板上的文字内容
            index_ = e.index(tkinter.INSERT)  # 获取光标当前的位置
            e.insert(index_, str_)

        def select_all(e):
            e.select_range(0, tkinter.END)

        def del_allStr(e):
            e.delete(0, tkinter.END)  # 清空整个字符串

        def del_selectStr(e):
            e.select_clear()  # 清除所选字符串
        # 建立弹出式菜单

        popupMenu = tkinter.Menu(master=self.__root, tearoff=False)  # 隐藏虚线分割线
        if is_checked:
            popupMenu.add_command(
                label="复制所选文字", command=lambda: copy_Str(checked_str))
        popupMenu.add_command(
            label="复制全部文字", command=lambda: copy_Str(searcStr))
        if is_checked:
            popupMenu.add_command(
                label="剪切所选文字", command=lambda: cut_selectStr(checked_str, e))
        popupMenu.add_command(
            label="剪切全部文字", command=lambda: cut_allStr(searcStr, e))
        popupMenu.add_command(label="粘贴", command=lambda: paste(e))
        popupMenu.add_command(label="全选", command=lambda: select_all(e))
        if is_checked:
            popupMenu.add_command(
                label="删除所选文字", command=lambda: del_selectStr(e))
        popupMenu.add_command(label="删除全部文字", command=lambda: del_allStr(e))

        popupMenu.post(event.x_root, event.y_root)  # 显示菜单;

    def __search_console(self, event):
        """
        搜索行为控制,是从文件读取的内容中进行搜索还是从字典中进行搜索;
        每次搜索,都将调用扫描函数进行扫描获取数据
        """
        self.__scan_console()    #尝试启动扫描

        result_list = ''
        if self.__scan_num > 0:
            # 此时self.__data是字典类型
            result_list = self.__find_match_dict()
        else:
            # 此时self.__data是列表类型
            result_list = self.__find_match()
        self.__search_result = result_list
        self.__type_filter(self.__filter_condition)
        self.__filling_init(self.__show_data)

    def __find_match(self):
        '''
        查询函数,从列表中读取,没有进行性能优化,即没有聚合成大字符串,再一次性匹配出所有符合条件的值得处理。
        '''
        #print("regex_str = {}".format(regex_str))
        regex_str = self.__searchBar.get()
        if regex_str == "":
            self.__statusBar.config(text="搜索框中没有字符哦。", foreground="red")
            return [self.__data[i] for i in range(self.__max_showLine)]
        data = self.__data

        start_time = time.time()
        result_list = []
        for i in data:
            if self.__is_caseSensitive:
                result_regex = re.findall(regex_str, i[0])
            else:
                result_regex = re.findall(regex_str, i[0], re.I)
            if len(result_regex) != 0:
                result_list.append(i)
        self.__statusBar.config(text="匹配出{}个结果;查询花费:{}秒。".format(
            len(result_list), time.time() - start_time), fg="black")
        return result_list

    def __regex_translate(self, regex_str):
        """表达式翻译处理"""
        if regex_str == '':
            return ""
        else:
            # 需要判断输入的正则表达式是否合法,不合法的话,就需要提示用户重新输入,之所以不纠正是因为不要猜测用户的意图。
            try:
                re.compile(regex_str)
            except:
                self.__statusBar.config(text="输入的正则表达式有误!", fg='red')
                return ''
            if regex_str[0] == '^':
                regex_str = regex_str[1:]
                replace_result_str = r"(?<=\t)"
            elif regex_str[0] == '.':  # 提升数倍的性能(6倍)
                replace_result_str = r"(?<=\t)"
            else:
                replace_result_str = r'(?<=\t)[^\t]*?'
            for index in range(len(regex_str)):
                if regex_str[index] == '.':
                    if index != 0 and regex_str[index - 1] == '\\':
                        replace_result_str += regex_str[index]
                    else:
                        replace_result_str += r'[^\t]'
                else:
                    replace_result_str += regex_str[index]
            if replace_result_str[-1] == "$":
                replace_result_str = replace_result_str[0:-1]
                replace_result_str += r'(?=\t)'
            else:
                replace_result_str += r'[^\t]*?(?=\t)'
            # print(replace_result_str)
            return replace_result_str

    def __find_match_dict(self):
        '''
        查询函数,从字典中读取,并生成结果列表类型变量,先将可以聚合成大字符串,在一次性匹配出所有符合条件的值。
        '''
        data = self.__data
        regex_str = self.__searchBar.get()
        regex_str = self.__regex_translate(regex_str)
        if regex_str == "":
            self.__statusBar.config(text="搜索框中没有字符哦。", foreground="red")
            return []
        start_time = time.time()
        result_list = []
        if type({}) == type(data):
            keys = data.keys()
            keys_str = "\t"
            keys_str += '\t'.join(keys)
            keys_str += '\t'
            if self.__is_caseSensitive:
                result_key = re.findall(regex_str, keys_str)
            else:
                result_key = re.findall(regex_str, keys_str, re.I)
            # print("匹配出{}个结果。".format(len(result_key)))   #这里输出的是不重复的文件数量
            #print("result_key = {}".format(result_key))
            for key in result_key:
                temp_ = {}
                temp_[key] = data[key]
                result_list += self.__dict_Conversion_list(temp_)
        else:
            # print("未传入字典类型变量!")
            self.__statusBar.config(text="未传入字典类型变量!", fg='red')
            return []
        #print('查询花费:{}秒。'.format(time.time() - start_time))
        #print("result_list = {}".format(result_list))
        self.__statusBar.config(text="匹配出{}个结果;查询花费:{}秒。".format(
            len(result_list), time.time() - start_time), fg="black")
        return result_list

    def __dict_Conversion_list(self, dict_data):
        '''
        将字典键和值转变为列表
        '''
        if type({}) != type(dict_data):
            return []
        return_result = []
        for key, value in dict_data.items():
            for value_i in value:
                temp_list = []
                temp_list.append(key)
                for value_i_i in value_i:
                    temp_list.append(value_i_i)
                return_result.append(temp_list)
        return return_result

    def __content(self):
        """数据表格"""
        self.__tableView = tkinter.ttk.Treeview(master=self.__root, columns=(
            "路径", "创建时间", "修改时间", "访问时间", "大小"), selectmode=tkinter.EXTENDED)
        # 建立栏标题
        self.__tableView.heading("#0", text="名称")
        self.__tableView.heading("#1", text="路径")
        self.__tableView.heading("#2", text="创建时间")
        self.__tableView.heading("#3", text="修改时间")
        self.__tableView.heading("#4", text="访问时间")
        self.__tableView.heading("#5", text="大小",command=lambda c=5:self.__tableView_sortColumn(c))
        # 固定列宽
        self.__table_cloumnSize(None)

        # 右对齐
        self.__tableView.column("大小", anchor=tkinter.E)

        # 滚动条
        self.__scroll_bar()  # 实例化滚动条对象
        self.__tableView.config(yscrollcommand=self.__scrollBar_y.set,
                                xscrollcommand=self.__scrollBar_x.set)  # 数据表格与滚动条做联动

        # 填充内容
        self.__type_filter(self.__filter_condition)
        self.__filling_init(self.__show_data)
        #self.__filling_init(self.__search_result)

        # 绑定函数
        self.__tableView.bind(
            "<Double-1>", self.__tableView_doubleClike)  # 双击事件
        self.__tableView.bind(
            "<Return>", self.__tableView_enterEvent)  # 回车键盘事件
        self.__tableView.bind(
            "<Delete>", self.__tableView_deleteEvent)  # 回车键盘事件
        self.__tableView.bind(
            "<Button-3>", self.__tableView_rightClick)  # 右键事件
        self.__tableView.bind("<Button-1>", self.__tableView_Click)  # 单击事件
        self.__tableView.bind("<Control-c>",self.__copyFiles_toClip)    #复制选中的文件到剪贴板
        self.__tableView.bind("<Control-x>",self.__cutFiles_toClip)    #剪切选中的文件到剪贴板

        # 显示
        self.__tableView.pack(fill=tkinter.BOTH, expand=1)

    def __content_copy(self, event):
        '''复制选中行和鼠标所在列交集处的内容'''
        event_widget = event.widget                 # 获取发出事件的控件
        selec_lins_ids = event_widget.selection()   # 返回选中的行ID元组
        column = event_widget.identify("column", event.x, event.y)  # 获取右击所在的列
        content_str = ""
        index = int(column[1:]) - 1
        if index < 0:
            for i in selec_lins_ids:
                content_str += "{}\n".format(event_widget.item(i, "text"))
        else:
            for i in selec_lins_ids:
                content_str += "{}\n".format(event_widget.item(i,
                                             "values")[index])
        content_str = content_str.strip()
        pyperclip.copy(content_str)  # 将字符串发送到剪贴板上

    def __tableView_Click(self, event):
        '''鼠标单击事件,主要是在状态栏上显示点击的文件信息'''
        e = event.widget  # 获取发出事件的控件
        column = e.identify("row", event.x, event.y)  # 返回单击所在的行ID
        filePath = e.item(column, "values")
        if len(filePath) > 0:
            filePath = filePath[0]
        else:
            return
        fileName = e.item(column, "text")
        fileABSpath = "{}\\{}".format(filePath, fileName)
        if fileABSpath == "\\":
            self.__statusBar.config(text="", fg="green")
            return
        file_info = "文件 {}".format(fileName)
        if os.path.exists(fileABSpath):
            file_info += "存在;"
            if os.path.isdir(fileABSpath):
                file_info += "是文件夹;"
                self.__statusBar.config(text=file_info, fg="yellow")
                return
            elif os.path.islink(fileABSpath):
                # elif fileABSpath[-3:] == "lnk": #妈的,python不支持Windows的快捷方式
                file_info += "是快捷方式;"
                pointTO_path = os.readlink(fileABSpath)
                file_info += "指向{}".format(pointTO_path)
                self.__statusBar.config(text=file_info, fg="purple")
            else:
                file_info += "是普通文件;"
                self.__statusBar.config(text=file_info, fg="green")
                return
        else:
            file_info += "不存在;"
        self.__statusBar.config(text=file_info, fg="red")

    def __tableView_rightClick(self, event):
        '''右键事件'''
        e = event.widget  # 获取发出事件的控件
        if len(e.get_children()) == 0:
            # 若是没有子项就不做处理
            return
        line_x = column = e.identify("row", event.x, event.y)  # 返回右键所在的行ID
        selec_x = e.selection()  # 返回选中的行ID元组
        if len(selec_x) == 0 or line_x not in selec_x:
            # 刷新
            def refresh(event):
                ''' 显示弹出式菜单:刷新
                    刷新作用有两个,一个是根据搜索框内容重新显示结果,二是触发扫描
                '''
                self.__search_console(event)
            refresh_popupMenu = tkinter.Menu(
                master=self.__root, tearoff=False)  # 隐藏虚线分割线
            refresh_popupMenu.add_command(
                label="刷新", command=lambda: refresh(event))
            refresh_popupMenu.post(event.x_root, event.y_root)  # 显示菜单;
            return
        column = e.identify("column", event.x, event.y)  # 获取右击所在的列
        filepaths_list = []
        [filepaths_list.append("{}\\{}".format(e.item(selec_x_i, "values")[
                               0], e.item(selec_x_i, "text"))) for selec_x_i in selec_x]
        if len(filepaths_list) == 1 and filepaths_list[0] == '\\':
            self.__statusBar.config(text="非法路径", fg="red")
            return
        if(column == "#0"):
            # 建立弹出式菜单
            # 打开,复制,剪切,重命名,复制名称
            popupMenu = tkinter.Menu(
                master=self.__root, tearoff=False)  # 隐藏虚线分割线

            popupMenu.add_command(
                label="打开文件", command=lambda: self.__open_files(event))
            popupMenu.add_command(
                label="复制文件", command=lambda: self.__copyFiles_toClip(event))
            popupMenu.add_command(
                label="剪切文件", command=lambda: self.__cutFiles_toClip(event))
            popupMenu.add_command(
                label="复制名称", command=lambda: self.__content_copy(event))
            popupMenu.add_command(
                label="打开文件所在位置", command=lambda: self.__open_fileLocation(event))

            popupMenu.post(event.x_root, event.y_root)  # 显示菜单;
        # elif (column == "#1"):
        else:
            # 建立弹出式菜单
            # 打开,复制,剪切,重命名,复制名称
            popupMenu = tkinter.Menu(
                master=self.__root, tearoff=False)  # 隐藏虚线分割线

            popupMenu.add_command(
                label="打开文件", command=lambda: self.__open_files(event))
            popupMenu.add_command(
                label="复制文件", command=lambda: self.__copyFiles_toClip(event))
            popupMenu.add_command(
                label="剪切文件", command=lambda: self.__cutFiles_toClip(event))
            popupMenu.add_command(
                label="复制名称", command=lambda: self.__content_copy(event))
            popupMenu.add_command(
                label="打开文件所在位置", command=lambda: self.__open_fileLocation(event))

            popupMenu.post(event.x_root, event.y_root)  # 显示菜单;

    def __tableView_deleteEvent(self, event):
        """delete事件,删除选择的项目"""
        event_widget = event.widget
        ids = event_widget.selection()
        for id in ids:
            path = event_widget.item(id, "values")[0]
            name = event_widget.item(id, "text")
            file_absPath = "{}\\{}".format(path, name)
            if file_absPath == '\\':
                self.__statusBar.config(text="非法路径", fg="red")
                continue
            if self.__del_file(file_absPath):
                event_widget.delete(id)
                self.__statusBar.config(
                    text="删除{}成功!".format(name), fg="green")
                # 源数据也需要删除的
                self.__sourceData_del(name, path)
            else:
                self.__statusBar.config(text="删除{}失败!".format(name), fg="red")

    def __tableView_enterEvent(self, event):
        '''数据区域内的回车事件,回车事件的动作为打开选中的文件'''
        event_widget = event.widget
        ids = event_widget.selection()
        if len(ids) > 5:
            choose_ret = tkinter.messagebox.askquestion(
                title="警告", message="你想要打开数量超过5个的文件集合,确定吗?")
            if choose_ret == 'yes':
                pass
            else:
                self.__statusBar.config(text="取消打开!", fg="black")
                return
        for id in ids:
            file_absPath = "{}\\{}".format(event_widget.item(id, "values")[
                                           0], event_widget.item(id, "text"))
            if file_absPath == '\\':
                self.__statusBar.config(text="非法路径", fg="red")
                continue
            ret = self.__open_file(file_absPath)
            label_str = self.__statusBar.cget(key="text")  # 获取text的值
            if ret:
                self.__statusBar.config(
                    text="{};打开成功!".format(label_str), fg="green")
            else:
                self.__statusBar.config(
                    text="{};打开失败!".format(label_str), fg="red")

    def __table_cloumnSize(self, event):
        '''调整__tableView的列宽'''
        # 固定列宽
        event_widget = self.__tableView        
        event_widget.column("#0", width=350)
        event_widget.column("#1", width=350)
        event_widget.column("#2", width=130)
        event_widget.column("#3", width=130)
        event_widget.column("#4", width=130)

    def __sourceData_del(self, key, value):
        '''删除__data变量里面的相关数据'''
        if type({"a": 1}) == type(self.__data):
            for data_i in self.__data[key]:
                index = -1  # 存储下标
                is_flag = False  # 标志是否找到
                for list_i in data_i:
                    index += 1
                    if list_i[0] == value:
                        is_flag = True
                        break
                if is_flag:
                    if len(self.__data[key]) == 1:
                        del self.__data[key]
                    else:
                        del self.__data[key][index]
                else:
                    tkinter.messagebox.showinfo(
                        title="异常", message="没有在源数据中找到对应的需要删除的数据:{}\\{}".format(value, key))

    def __del_file(self, path_str):
        '''删除文件(夹),这里会做确认操作;但是若只是删除一个项目还好说,若是删除文件夹,连带着会删除很多项目,这样去显示的时候就比较麻烦
            考虑了一下,若是文件夹下面是空的,那么可以删除,若是文件夹下面有东西,那么不允许删除。
        '''
        if(os.path.exists(path_str)):
            if(os.path.isdir(path_str)):
                # 是文件夹
                try:
                    # os.rmdir(path_str)
                    shell.SHFileOperation((0, shellcon.FO_DELETE, path_str, None, shellcon.FOF_SILENT |
                                          shellcon.FOF_ALLOWUNDO | shellcon.FOF_NOCONFIRMATION, None, None))  # 删除文件到回收站
                except OSError:
                    tkinter.messagebox.showinfo(
                        title="失败", message="文件夹:{}  删除失败,原因是:\n目标文件夹非空".format(path_str))
                    return False
                return True
            else:
                try:
                    # os.remove(path_str)
                    shell.SHFileOperation((0, shellcon.FO_DELETE, path_str, None, shellcon.FOF_SILENT |
                                          shellcon.FOF_ALLOWUNDO | shellcon.FOF_NOCONFIRMATION, None, None))  # 删除文件到回收站
                except Exception as e:
                    tkinter.messagebox.showinfo(
                        title="失败", message="文件:{}  删除失败,原因是:\n{}".format(path_str, str(e)))
                    return False
                return True
        else:
            # 文件不存在了,这时候需要更新一下显示,所以仍然返回True。同时也做一个提示
            tkinter.messagebox.showinfo(
                title="提示", message="文件(夹):{}  不存在".format(path_str))
            return True

    def __scroll_bar(self):
        """滚动条"""
        # Y轴
        self.__scrollBar_y = tkinter.Scrollbar(
            master=self.__tableView, command=self.__tableView.yview)
        self.__scrollBar_y.pack(side=tkinter.RIGHT, fill=tkinter.Y)
        # X轴
        self.__scrollBar_x = tkinter.Scrollbar(
            orient=tkinter.HORIZONTAL, master=self.__tableView, command=self.__tableView.xview)
        self.__scrollBar_x.pack(side=tkinter.BOTTOM, fill=tkinter.X)

    def __filling_init(self, data):
        """
        将扫描的数据填充到控件中,仅填充前self.__max_showLine行
        """
        def size_show(size):
            '''友好化文件大小字符串'''
            unit_dict = {0: "B", 1: "KB", 2: "MB", 3: "GB", 4: "TB", 5: "PB"}
            size = int(size)
            size_ = size
            unit_flag_int = 0
            while size_ > 1024:
                size_ = size_ / 1024
                unit_flag_int += 1
            ret_size = "{}{}".format(round(size_, 2), unit_dict[unit_flag_int])
            return ret_size
        if data != None and len(data) != 0:
            num = 0
            # 清空
            [self.__tableView.delete(id)
             for id in self.__tableView.get_children()]
            for line in data:
                num += 1
                self.__tableView.insert("", index=tkinter.END, text=line[0], value=(
                    line[1], line[2], line[3], line[4], size_show(line[5])))
                if num > self.__max_showLine:
                    break
        else:
            # 清空
            [self.__tableView.delete(id)
             for id in self.__tableView.get_children()]
            #self.__statusBar.config(text="查询结果为空;type(self.__data)={};len(self.__data)={}".format(
            #    type(self.__data), len(self.__data)), fg="red")
            self.__statusBar.config(text="查询结果为空", fg="red")
        # 填充一行空行,使得原本不能被显示完全的最后一行显示完全。同是避免搜索结果为空时,不可刷新
        self.__tableView.insert("", index=tkinter.END,
                                text="", value=("", "", "", "", ""))

    def __tableView_doubleClike(self, event):
        """数据区域的双击事件,根据双击的位置做出不同的响应"""
        e = event.widget  # 获取发出事件的控件
        if len(e.get_children()) == 0:
            # 若是没有子项就不做处理
            return
        column = e.identify("column", event.x, event.y)  # 获取双击所在的列
        id_ = e.identify("item", event.x, event.y)

        ret = True  # 是否打开成功
        if(column == "#1"):
            # 双击的路径,打开文件资源管理器,定位到这个路径下
            path_str = e.item(id_, "values")[0]
            ret = self.__open_fileLocation_run(path_str + "\\")
        else:
            # 其他地方
            path_str = "{}\\{}".format(e.item(id_, "values")[
                                       0], e.item(id_, "text"))
            if path_str == '\\':
                self.__statusBar.config(text="非法路径", fg="red")
                return
            ret = self.__open_file(path_str)
        label_str = self.__statusBar.cget(key="text")  # 获取text的值
        if ret:
            self.__statusBar.config(
                text="{};打开成功!".format(label_str), fg="green")
        else:
            self.__statusBar.config(
                text="{};打开失败!".format(label_str), fg="red")

    def __open_file(self, filepath):
        '''
        打开指定的文件,使用系统默认方式;
        打开之前会检查文件/目录是否存在。
        成功返回True,不成功返回False
        '''
        if os.path.exists(filepath):
            try:
                os.startfile(filepath)
            except Exception as e:
                errInfo = str(e)
                self.__statusBar.config(text=errInfo)
                return False
            return True
        else:
            self.__statusBar.config(text="该文件已经不存在了。", fg="red")
            return False

    def __open_fileLocation_run(self, filepath):
        '''打开文件所在的位置,执行代码,主要是使得目标文件在资源管理器中被选定。'''
        if os.path.exists(filepath):
            try:
                # os.startfile(filepath)
                subprocess.run('Explorer.exe /select,"{}"'.format(filepath))    #这里的命令,在Windows下,使用cmd来运行更符合系统模式,不要使用PowerShell命令行。cmd里面,空格要使用双引号来引起来,而非单引号。
            except OSError as msg:
                traceback.print_exc()
                return False
            return True
        else:
            self.__statusBar.config(text="该文件已经不存在了。", fg="red")
            return False

    def __open_files(self, event):
        '''多文件打开'''
        e = event.widget  # 获取发出事件的控件
        selec_line_id = e.selection()  # 返回选中的行ID元组
        column = e.identify("column", event.x, event.y)  # 获取右击所在的列
        filepaths_list = []
        [filepaths_list.append("{}\\{}".format(e.item(selec_x_i, "values")[
                               0], e.item(selec_x_i, "text"))) for selec_x_i in selec_line_id]
        if len(filepaths_list) == 1 and filepaths_list[0] == '\\':
            self.__statusBar.config(text="非法路径", fg="red")
            return
        if len(filepaths_list) > 5:
            choose_ret = tkinter.messagebox.askquestion(
                title="警告", message="你想要打开数量超过5个的文件集合,确定吗?")
            if choose_ret == 'yes':
                pass
            else:
                self.__statusBar.config(text="取消打开!", fg="black")
                return
        for filepath in filepaths_list:
            ret = self.__open_file(filepath)
            label_str = self.__statusBar.cget(key="text")  # 获取text的值
            if ret:
                self.__statusBar.config(
                    text="{};打开成功!".format(label_str), fg="green")
            else:
                self.__statusBar.config(
                    text="{};打开失败!".format(label_str), fg="red")

    def __copyFiles_toClip(self, event):
        '''复制文件到Windows剪贴板'''
        # 参考:https://chowdera.com/2021/10/20211031055535475l.html
        # 这其实是一个结构体,用以记录文件的各种信息。
        class DROPFILES(ctypes.Structure):
            _fields_ = [
                ("pFiles", ctypes.c_uint),
                ("x", ctypes.c_long),
                ("y", ctypes.c_long),
                ("fNC", ctypes.c_int),
                # 指示文件是否包含 ANSI 或 Unicode 字符。如果值为零,则文件包含 ANSI 字符。否则,它包含 Unicode 字符。
                ("fWide", ctypes.c_bool),
            ]

        pDropFiles = DROPFILES()
        pDropFiles.pFiles = ctypes.sizeof(DROPFILES)
        pDropFiles.fWide = True
        #a = bytes(pDropFiles)

        # 获取文件绝对路径
        e = event.widget  # 获取发出事件的控件
        selec_line_id = e.selection()  # 返回选中的行ID元组
        column = e.identify("column", event.x, event.y)  # 获取右击所在的列
        filepaths_list = []
        [filepaths_list.append("{}\\{}".format(e.item(selec_x_i, "values")[
                               0], e.item(selec_x_i, "text"))) for selec_x_i in selec_line_id]
        if len(filepaths_list) == 1 and filepaths_list[0] == '\\':
            self.__statusBar.config(text="非法路径", fg="red")
            return
        if len(filepaths_list) > 500:
            choose_ret = tkinter.messagebox.askquestion(
                title="警告", message="你想要复制数量超过500个的文件集合,确定吗?")
            if choose_ret == 'yes':
                pass
            else:
                self.__statusBar.config(text="取消复制!", fg="black")
                return
        files = ("\0".join(filepaths_list)).replace("/", "\\")
        # 结尾一定要两个\0\0字符,这是规定!就是构造NULL字符
        data = files.encode("U16")[2:] + b"\0\0"

        '''
        对于多个文本路径,我们如何将其转换为我们需要的Unicode 双字节形式呢?
        首先,我们要知道Unicode编码采用UCS-2格式直接存储,而UTF-16恰好对应于UCS-2的,即UCS-2指定的码位通过大端或小端的方式直接保存。UTF-16 有三种类型:UTF-16,UTF-16BE(大端序),UTF-16LE(小端序).UTF-16 通过以名称BOM(字节顺序标记,U + FEFF)启动文件来指示该文件仍然是小端序。
        我们只需要把python String使用UTF-16编码后,去掉前两个字节,得到相应的Unicode双字节。
        '''
        win32clipboard.OpenClipboard()  # 打开剪贴板(独占)
        try:
            # 若要将信息放在剪贴板上,首先需要使用 EmptyClipboard 函数清除任何以前的剪贴板内容
            win32clipboard.EmptyClipboard()  # 清空当前的剪贴板信息,否则不能写入

            #uDropEffect = win32clipboard.RegisterClipboardFormat("Preferred DropEffect")

            win32clipboard.SetClipboardData(
                win32clipboard.CF_HDROP, bytes(pDropFiles)+data)  # 设置当前剪贴板数据
            # win32clipboard.SetClipboardData(uDropEffect,b'\x02\x00\x00\x00')
            #print("error= ",ctypes.windll.kernel32.GetLastError())

            self.__statusBar.config(text="复制成功!", fg="green")
        except Exception as e:
            exc_type, exc_value, exc_traceback_obj = sys.exc_info()
            traceback.print_tb(exc_traceback_obj)
            self.__statusBar.config(text="复制失败!", fg="red")
        finally:
            win32clipboard.CloseClipboard()  # 无论什么情况,都关闭剪贴板

    def __cutFiles_toClip(self, event):
        '''剪切文件到剪贴板'''
        # 这其实是一个结构体,用以记录文件的各种信息。
        class DROPFILES(ctypes.Structure):
            _fields_ = [
                ("pFiles", ctypes.c_uint),
                ("x", ctypes.c_long),
                ("y", ctypes.c_long),
                ("fNC", ctypes.c_int),
                # 指示文件是否包含 ANSI 或 Unicode 字符。如果值为零,则文件包含 ANSI 字符。否则,它包含 Unicode 字符。
                ("fWide", ctypes.c_bool),
            ]

        pDropFiles = DROPFILES()
        pDropFiles.pFiles = ctypes.sizeof(DROPFILES)
        pDropFiles.fWide = True
        # 获取文件绝对路径
        e = event.widget  # 获取发出事件的控件
        selec_line_id = e.selection()  # 返回选中的行ID元组
        column = e.identify("column", event.x, event.y)  # 获取右击所在的列
        filepaths_list = []
        [filepaths_list.append("{}\\{}".format(e.item(selec_x_i, "values")[
                               0], e.item(selec_x_i, "text"))) for selec_x_i in selec_line_id]
        if len(filepaths_list) == 1 and filepaths_list[0] == '\\':
            self.__statusBar.config(text="非法路径", fg="red")
            return
        if len(filepaths_list) > 50:
            choose_ret = tkinter.messagebox.askquestion(
                title="警告", message="你想要剪切数量超过50个的文件集合,确定吗?")
            if choose_ret == 'yes':
                pass
            else:
                self.__statusBar.config(text="取消剪切!", fg="black")
                return
        files = ("\0".join(filepaths_list)).replace("/", "\\")
        # 结尾一定要两个\0\0字符,这是规定!就是构造NULL字符
        data = files.encode("U16")[2:] + b"\0\0"

        '''
        对于多个文本路径,我们如何将其转换为我们需要的Unicode 双字节形式呢?
        首先,我们要知道Unicode编码采用UCS-2格式直接存储,而UTF-16恰好对应于UCS-2的,即UCS-2指定的码位通过大端或小端的方式直接保存。UTF-16 有三种类型:UTF-16,UTF-16BE(大端序),UTF-16LE(小端序).UTF-16 通过以名称BOM(字节顺序标记,U + FEFF)启动文件来指示该文件仍然是小端序。
        我们只需要把python String使用UTF-16编码后,去掉前两个字节,得到相应的Unicode双字节。
        '''
        win32clipboard.OpenClipboard()  # 打开剪贴板(独占)
        try:
            # 若要将信息放在剪贴板上,首先需要使用 EmptyClipboard 函数清除任何以前的剪贴板内容
            win32clipboard.EmptyClipboard()  # 清空当前的剪贴板信息,否则不能写入

            uDropEffect = win32clipboard.RegisterClipboardFormat(
                "Preferred DropEffect")

            win32clipboard.SetClipboardData(
                win32clipboard.CF_HDROP, bytes(pDropFiles)+data)  # 设置当前剪贴板数据
            win32clipboard.SetClipboardData(uDropEffect, b'\x02\x00\x00\x00')
            err = ctypes.windll.kernel32.GetLastError()
            if err == 0:
                self.__statusBar.config(text="剪切成功!", fg="green")
            else:
                self.__statusBar.config(
                    text="剪切失败!errorCode={}".format(err), fg="red")
        except Exception as e:
            exc_type, exc_value, exc_traceback_obj = sys.exc_info()
            traceback.print_tb(exc_traceback_obj)
            self.__statusBar.config(text="剪切失败!", fg="red")
        finally:
            win32clipboard.CloseClipboard()  # 无论什么情况,都关闭剪贴板

    def __open_fileLocation(self, event):
        '''打开文件所在的位置'''
        # 获取文件的绝对路径
        e = event.widget  # 获取发出事件的控件
        selec_line_id = e.selection()  # 返回选中的行ID元组
        column = e.identify("column", event.x, event.y)  # 获取右击所在的列
        filepaths_list = []
        [filepaths_list.append("{}\\{}".format(e.item(selec_x_i, "values")[
                               0], e.item(selec_x_i, "text"))) for selec_x_i in selec_line_id]
        if len(filepaths_list) == 1 and filepaths_list[0] == '\\':
            self.__statusBar.config(text="非法路径", fg="red")
            return
        if len(filepaths_list) > 1:
            choose_ret = tkinter.messagebox.askquestion(
                title="警告", message="你想要打开{}个文件的所在位置,确定吗?".format(len(filepaths_list)))
            if choose_ret == 'yes':
                pass
            else:
                self.__statusBar.config(text="取消打开文件所在位置!", fg="black")
                return
        for path in filepaths_list:
            self.__open_fileLocation_run(path)


    def Hidden_window(self, icon=r'D:\python\python_class_everything\python_everything.ico', hover_text="python everything"):  # ico文件一定要是合法的,不能使用改后缀的方式
        '''隐藏窗口至托盘区,调用SysTrayIcon的重要函数'''

        # 托盘图标右键菜单, 格式: ('name', None, callback),下面也是二级菜单的例子
        # 24行有自动添加‘退出’,不需要的可删除
        menu_options = (
            # ('显示窗口', None, lambda:s.SysTrayIcon.destroy(exit = 0)),  #我放到后面去了。
            # ('二级 菜单', None, (('更改 图标', None, s.switch_icon), )))
            #('退出', None, s.exit)
        )
        self.__root.withdraw()  # 隐藏tk窗口

        if not self.__SysTrayIcon:
            self.__SysTrayIcon = sysTrayIcon.SysTrayIcon(
                icon,  # 图标
                hover_text,  # 光标停留显示文字
                menu_options,  # 右键菜单
                on_quit=self.exit_off,  # 退出调用
                tk_window=self.__root,  # Tk窗口
            )
        self.__SysTrayIcon.activation()

    def exit_off(self):
        '''退出'''
        '''
        if self.__threading_Operating_condition.is_alive():
            #若子进程还存活,等待子进程结束
            self.__threading_Operating_condition.join()
        '''
        self.__scan.kill_scan()    #结束扫描进程,如果存在
        os.kill(self.__pid, signal.SIGBREAK)
        # self.__root.destroy()

    def __searchBar_focus(self, event):
        '''快捷键Ctrl+f,捕捉到这个事件后,使搜索框得到焦点'''
        self.__searchBar.focus_set()  # 使得搜索框获得焦点,便于快速输入搜索表达式
    def __tableView_sortColumn(self,col_int):
        '''排序;col_int表示第几列(从0开始算)'''
        self.__search_result.sort(key=lambda x:int(x[col_int]),reverse=self.__sortFlag_col_size)
        self.__sortFlag_col_size = not self.__sortFlag_col_size

        self.__type_filter(self.__filter_condition)
        self.__filling_init(self.__show_data)

        if not self.__sortFlag_col_size:
            self.__statusBar.config(text="从大到小排序",fg="green")
        else:
            self.__statusBar.config(text="从小到大排序",fg="green")
    def __type_filter(self,type_flag):
        '''筛选指定的类型文件
            type_flag取值:
            all:所有类型
            zip:压缩文件
            audio:音频文件
            video:视频文件
            picture:图片文件
            document:文档文件
            execute:可执行文件
            folder:文件夹
            lnk:快捷方式
        '''
        type_dict={'zip':['rar','zip','cab','arj','lzh','ace','7z','tar','gzip','uue','bz2','jar','iso','z'],
                    'audio':['mp3','wma','wav','mid','ape','flac','weba','webm','cda','aif','aiff','ra','vqf',],
                    'video':['mpg','mpeg','avi','rmvb','mov','wmv','asf','asx','wvx','mpe','mpa','mp4','rm','navi','mkv','ogg','mod',],
                    'picture':['bmp','jpeg','png','tiff','gif','pcx','tga','exif','fpx','svg','psd','ico','jpg','wmf','tif','webp',],
                    'document':['pptx','pptm','ppt','pdf','xps','potx','potm','pot','thmx','ppsx','pps','ppam','ppa','xml','json','log','config','rtf','odp','wps','doc','docx','docm','dotx','dotm','dot','mht','mhtml','htm','html','odt','xlsx','xlsm','xlsb','xls','csv','xltx','xltm','xlt','prn','dif','slk','xlam','xla','ods','java','class','jav','bat','cmd','bib','c','cs','csx','cake','cpp','cc','cxx','c++','hpp','hh','h++','h','ii','clj','cljs','cljc','cljx','clojure','edn','cmake','coffee','cson','iced','css','cu','cuh','dart','i','diff','patch','rej','dockerfile','containerfile','fs','fsi','fsx','fsscript','go','groovy','gvy','gradle','jenkinsfile','nf','handlebars','hbs','hjs','hlsl','hlsli','fx','fxh','vsh','psh','cginc','compute','shtml','xhtml','xht','mdoc','jsp','asp','aspx','jshtm','js','gitgnore_global','hitigonre','npmignore','ini','es6','mjs','cjs','pac','jsx','j2','jinja2','bowerrc','jscsrc','webmainfest','js.map','css.map','ts.map','har','jslintrc','jsonld','jsonc','eslintrc','eslintrc.json','jsfmtrc','swcrc','hintrc','babelrc','code-workspace','language-configuration.json','jl','jmd','ipynb','tex','ltx','ctx','less','lua','mak','mk','md','mkd','mdwn','mdown','markdown','markdn','mdtxt','mdtext','workbook','m','mm','pl','pm','pod','t','psgi','p6','pl6','pm6','nqp','php','php4','php5','phtml','ctp','ps1','psm1','psd1','pssc','psrc','properties','cfg','conf','directory','giattributes','gitconfig','gitmodules','editorconfig','npmrc','pug','jade','py','rpy','pyw','cpy','gyp','gypi','pyi','ipy','pyt','r','rhistory','rprofile','rt','cshtml','rb','rbx','rjs','gemspec','rake','ru','erb','podspec','rbi','rs','scss','code-search','shader','sh','bash','bashrc','bash_aliases','bash_profile','bash_login','ebuild','profile','bash_logout','xprofile','sql','dsql','swift','sty','cls','bbx','cbx','cts','mts','tsx','vb','brs','vbs','bas','vba','xsd','ascx','atom','axml','axaml','bpmn','cpt','csl','csproj','xsl','xslt','yml','eyaml','eyml','yaml','cff','txt',],
                    'execute':['exe','msi','bat','scr','cmd','bat','com',],
                    'lnk':['lnk'],
        }
        #初始化显示数据
        self.__show_data = []

        if type_flag == 'all':
            self.__show_data = self.__search_result
        elif type_flag == 'folder':
            for i_list in self.__search_result:
                if int(i_list[-1]) == 1:
                    self.__show_data.append(i_list)
        elif type_flag == 'zip':
            for i_list in self.__search_result:
                suffix_str = re.findall(r'(?<=\.)[^\.]+$', i_list[0])
                if len(suffix_str) > 0:
                    if suffix_str[0] in type_dict['zip']:
                        self.__show_data.append(i_list)
        elif type_flag == 'audio':
            for i_list in self.__search_result:
                suffix_str = re.findall(r'(?<=\.)[^\.]+$', i_list[0])
                if len(suffix_str) > 0:
                    if suffix_str[0] in type_dict['audio']:
                        self.__show_data.append(i_list)
        elif type_flag == 'video':
            for i_list in self.__search_result:
                suffix_str = re.findall(r'(?<=\.)[^\.]+$', i_list[0])
                if len(suffix_str) > 0:
                    if suffix_str[0] in type_dict['video']:
                        self.__show_data.append(i_list)
        elif type_flag == 'picture':
            for i_list in self.__search_result:
                suffix_str = re.findall(r'(?<=\.)[^\.]+$', i_list[0])
                if len(suffix_str) > 0:
                    if suffix_str[0] in type_dict['picture']:
                        self.__show_data.append(i_list)
        elif type_flag == 'document':
            for i_list in self.__search_result:
                suffix_str = re.findall(r'(?<=\.)[^\.]+$', i_list[0])
                if len(suffix_str) > 0:
                    if suffix_str[0] in type_dict['document']:
                        self.__show_data.append(i_list)
        elif type_flag == 'execute':
            for i_list in self.__search_result:
                suffix_str = re.findall(r'(?<=\.)[^\.]+$', i_list[0])
                if len(suffix_str) > 0:
                    if suffix_str[0] in type_dict['execute']:
                        self.__show_data.append(i_list)
        elif type_flag == 'lnk':
            for i_list in self.__search_result:
                suffix_str = re.findall(r'(?<=\.)[^\.]+$', i_list[0])
                if len(suffix_str) > 0:
                    if suffix_str[0] in type_dict['lnk']:
                        self.__show_data.append(i_list)
        else:
            self.__statusBar.config(text="参数有误{}".format(type_flag),fg='red')
    def __filter_console(self,condition,index):
        '''显示所有文件
            condition:字符串
            index:下标,int
        '''
        #print("{}".format(condition))
        [self.__filterStatus_list[i].set(False) for i in range(9)]
        if index < len(self.__filterStatus_list):
            self.__filterStatus_list[index].set(True)
        else:
            self.__statusBar.config(text="筛选文件类型时,传入的下标发生越界。",fg="red")
            return
        self.__filter_condition = condition
        self.__type_filter(self.__filter_condition)
        self.__filling_init(self.__show_data)
        num = len(self.__show_data)
        if num > 0:
            if num > self.__max_showLine:
                self.__statusBar.config(text="筛选出{}个结果,此处只显示前self.__max_showLine个结果。".format(num),fg="green")
            else:
                self.__statusBar.config(text="筛选出{}个结果。".format(num),fg="green")
        else:
            self.__statusBar.config(text="没有此类型文件。",fg="red")
    def show(self):
        """#显示界面"""
        self.__root.mainloop()

 

posted on 2022-02-21 15:55  狂自私  阅读(267)  评论(0编辑  收藏  举报