图像分家GUI

# --*--coding:utf-8--*--
# updatedtime:20220507
import inspect
import ctypes
import re
import shutil
import threading
import os
import configparser
import webbrowser

import PySimpleGUI as sg

list_ths = []  # 多线程列表
list_path = []  # 存放选过的路径
list_newpath = []  # 存放选过的新路径

class utils(object):

    def __init__(self):
        self.result_display = None

    # 处理文件
    def search_path_method(self, search_path, ignore_folder: list, filter_suffix, newPath_this="", reString=""):
        """
        根据search_path路径下的的所有后缀名为filter_suffix的文件,输出时筛选掉ignore_path目录下的文件
        :param search_path: 待搜索的路径。字符串
        :param newPath_this: 放置文件的路径。字符串
        :param ignore_folder:忽略的文件夹的绝对路径。列表
        :param filter_suffix:需要查出来的后缀名。元组
        :return:路径结果
        """
        """
        def print_result(future):
            global jump_num_ths, jump_success_ths
            jump_num_ths += 1
            if future.result():
                jump_success_ths += 1
        # ThreadPoolExecutor适用于处理I/O 密集型任务。对于CPU密集型任务还不如单线程快。
        with ThreadPoolExecutor(max_workers=MaxWorkers) as pool:
            input_flag = False  # 真完结标志
            while True:  # 提单成功列表尽数跳转
                try:
                    dataDict = q.get(timeout=120)
                    while dataDict.get("flag"):
                        if jump_num_ths == submit_num_ths:
                            if destination == destination_list[2]:  # 下一步执行,才塞结束标记,不执行,就等执行那次再塞
                                boe_jump_ths_queue.put(True)
                            input_flag = True
                            break
                        time.sleep(2)
                    if input_flag:
                        break
                    boeNo = dataDict.get("boeNo")
                    taskId = dataDict.get("taskId")
                    future1 = pool.submit(jump, cookie, taskId, boeNo)
                    future1.add_done_callback(print_result)
                except BaseException:
                    print("jump_ths方法出现一次error\n", end='')
                    # raise error
        """
        failnum, successnum = 0, 0
        for o, value in enumerate(ignore_folder):  # 进不去了
            ignore_folder[o] = os.path.normcase(value.replace('\\\\', '\\'))
        list_file_path = ""  # 文件路径
        if not newPath_this:
            newPath_this = search_path
        if search_path:
            for root, dirs, files in os.walk(search_path):
                if os.path.normcase(root).replace('\\\\', '\\') in ignore_folder:  # 被忽略的文件夹路径  # 进不去了
                    for i, j, k in os.walk(root):
                        pathsadads = os.path.normcase(str(i).replace('\\\\', '\\'))
                        ignore_folder.append(pathsadads)
                    continue
                list_all = ['', '.', ".*"]
                result = list(set(list_all).intersection(set(list(filter_suffix))))
                if not len(result):
                    files = [f for f in files if f.endswith(filter_suffix)]  # 选择后缀名  # filter_suffix后缀
                for name in files:
                    list_file_path = os.path.join(root, name).replace('\\', '/').strip('\n').strip('\t').strip('\r')  # 路径和文件名连接构成完整路径
                    try:
                        newPathName = self.ElementFilter(name, reString, '1')
                        if not newPathName:
                            # reString = '.*?(?=\\.[jpg|jpeg|gif|bmp|png])'
                            print(f"图片  {list_file_path}  处理失败:正则过滤目录名失败")
                            failnum += 1
                            continue
                    except:
                        sg.popup_quick_message("正则有误")
                        return

                    newPath_True = os.path.join(newPath_this, newPathName)
                    if not os.path.exists(newPath_True):
                        os.makedirs(newPath_True)
                    try:
                        shutil.move(list_file_path, os.path.join(newPath_True, name))
                    except shutil.Error as error:
                        try:
                            os.remove('F:\PythonProject\CHN_TGtools\\newPath212.jpg')
                        except:
                            pass
                    finally:
                        successnum += 1
            # 保存历史路径
            # self.save_his_path(search_path)
            print(f"处理完成!\n共{successnum + failnum}次处理。成功{successnum},失败{failnum}")
            sg.popup_quick_message("处理完成!", background_color='green', auto_close_duration=7)
        return list_file_path

    # 正则匹配
    def ElementFilter(self, string, re_string, return_num="1"):
        """
        正则匹配搜索string,以列表形式返回某个能匹配的子串,默认取列表第一个,
        :param string: 总的字符串,string
        :param re_string: 目标字符串的匹配规则,string。
               如想提取首尾字符串中间的最短内容,ride填:(?<=首字符串).*?(?=尾字符串)
               如想提取首尾字符串中间的最长内容,ride填:(?<=首字符串).*(?=尾字符串)
               如想提取首尾字符串中间的数字内容,ride填:(?<=首字符串)\d+(?=尾字符串)
               如想提取首尾字符串中间的字符串内容,ride填:(?<=首字符串).*(?=尾字符串)
        :param return_num: 返回匹配的第几个值,默认1返回第一个,int。传空返回全部匹配结果。
        :return: result_list 匹配结果以列表形式返回全部能匹配的子串,默认返回全部匹配结果,string。
        使用参考:https://www.runoob.com/regexp/regexp-tutorial.html
        找出连续的数字并且最后一个数字跟着至少一个a-z里面的字符序列: r"\d+(?=[a-z]+)"。
        找出连续的数字,并且最后一个数字后面不能跟任何一个a-z里面的字符序列: r"\d+(?![a-z]+)"。
        找出连续的数字,并且第一个数字的前面要是a-z中的一个字符:r"(?<=[a-z])\d+"。
        找出连续的数字,并且第一个数字的前面不能是a-z中的一个字符:r"(?![a-z])\d+"。
        """
        return_num = eval(return_num)
        if isinstance(return_num, int) is False:
            print(">>>return_num只支持传入数字,ride填写时不带引号。", "\n>>>type(return_num):", type(return_num))
        # 正则过滤出目标规则字符串,存储成元组列表
        reObj = re.compile(re_string)
        result_list = reObj.findall(string)
        # print(">>>匹配结果列表:", result_list)
        return_num_new = return_num - 1
        if len(result_list) == 0:
            # print(">>>未匹配到内容,请检查正则表达式书写格式")
            return ""
        if len(result_list) > return_num_new and return_num > 0:
            # print(">>>返回第%s个结果:" % (return_num), result_list[return_num_new])
            return result_list[return_num_new]
        else:
            # print(">>>参数return_num大小超出了匹配出来的结果数量,返回全部匹配结果:,", result_list)
            return result_list

    # 中止执行
    def suspend_btn_onclick(self):
        """中止线程"""
        global list_ths
        def stop_thread(thread):
            def _async_raise(tid, exctype):
                """线程停止"""
                if not inspect.isclass(exctype):
                    exctype = type(exctype)
                res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
                if res == 0:
                    raise ValueError("invalid thread id")
                elif res != 1:
                    ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
                    raise SystemError("PyThreadState_SetAsyncExc failed")
            _async_raise(thread.ident, SystemExit)

        if not len(list_ths):
            return True
        for key, value in enumerate(list_ths):
            print(key, value)
            try:
                stop_thread(value)
            except:
                pass
        list_ths.clear()
        return True

    # 执行按钮
    def loading_btn_onclick(self, value, windows, text_choice=None):
        """执行按钮函数
        value: 界面值
        window: 操作窗口
        text_choice:忽略列表
        """
        global list_ths
        search_path = value.get('basePath')  # 取出目标路径输入框的值
        newPath_done = value.get('newPath')  # 取出移动至输入框的值
        fileEndname = value.get('fileEndname')  # 取出文件后缀输入框的值
        reString = value.get('reString')  # 取出文件后缀输入框的值

        ignore_folder = []  # 取出忽略文件夹输入框的值,暂时不做此功能
        filter_suffix = tuple(fileEndname.replace(',', ',').replace(' ', '').split(","))  # 取出筛选后缀输入框的值写入元组
        if not newPath_done:
            newPath_done = search_path
        if not os.path.isdir(search_path) or not os.path.isdir(newPath_done):
            sg.popup_auto_close("目标路径 或 移动至的路径 不存在,请检查", title="提示")
            return
        if search_path not in list_path:
            list_path.append(search_path)
        else:
            pass
        if newPath_done not in list_newpath:
            list_newpath.append(newPath_done)
        else:
            pass
        windows['basePath'].update(value=search_path, values=list_path)
        windows['newPath'].update(value=newPath_done, values=list_newpath)
        windows['debug_result'].Update(value='')
        # 输出加载结果
        #     def search_path_method(self, search_path, ignore_folder: list, filter_suffix, newPath_this="", reString=""):
        t2 = threading.Thread(
            target=self.search_path_method, args=(search_path, ignore_folder, filter_suffix, newPath_done, reString))
        t2.daemon = True
        t2.start()
        list_ths.append(t2)

class WindowsTool(utils):
    def __init__(self):
        super().__init__()

        self.right_button_menu = ['正直诚信', '专业敬业', '开放合作', '持续成长']

    # 数据工厂UI
    def tool_data_ui(self, **kwargs):
        global list_path
        if isinstance(kwargs.get('list_hisPath'), list):
            list_path.extend(kwargs.get('list_hisPath'))
        layout_title = [
            [
                sg.Text('* 目标路径:', size=10, justification='right'),
                sg.InputCombo(key='basePath', size=40, default_value=os.path.join(os.path.expanduser('~'),"Desktop"), values=list_path,
                              enable_events=True, tooltip="存放路径"),
                sg.FolderBrowse(button_text='选择', auto_size_button=True, tooltip="选择待处理照片存放路径"),
                sg.Text('移动至:', size=10, justification='right'),
                sg.InputCombo(key='newPath', size=40, default_value='', values=list_newpath, enable_events=True, tooltip="移放路径"),
                sg.FolderBrowse(button_text='选择', auto_size_button=True, tooltip="选择移放路径,不填默认同目录"),
            ],
            [
                sg.Text('* 文件后缀:', size=10, justification='right'),
                sg.Input(key='fileEndname', size=20, default_text=".jpg", tooltip="需要处理的文件后缀,多个用逗号隔开\n如.jpg,.png"),
                sg.Text('* 目录正则:', size=10, justification='right'),  # .*?(?=\.[jpg|jpeg|gif|bmp|png])
                sg.Input(key='reString', size=35, default_text="(?<=_)[a-zA-Z0-9]+?_[a-zA-Z0-9]+?(?=_)", tooltip="从文件绝对路径中匹配目录名的正则表达式\n请经过测试后再填写使用"),
                sg.Text('  ', size=1, justification='left', right_click_menu=['&Right', self.right_button_menu]),  # 空白区域
                sg.Button(
                    "执行",
                    bind_return_key=True,
                    size=10,
                    mouseover_colors='#1d953f',
                    use_ttk_buttons=True,
                    right_click_menu=['&Right', ['源码']]),
                sg.Button(
                    "中止",
                    bind_return_key=True,
                    size=10,
                    mouseover_colors='#1d953f',
                    use_ttk_buttons=True,
                    right_click_menu=['&Right', self.right_button_menu]),
                sg.Button(
                    "重置",
                    bind_return_key=True,
                    size=10,
                    mouseover_colors='#1d953f',
                    use_ttk_buttons=True,
                    right_click_menu=['&Right', self.right_button_menu]),
            ],
        ]
        layout_result = [
            [
                sg.Output(key='debug_result', size=(120, 20), right_click_menu=['&Right', self.right_button_menu])
            ]
        ]
        layout = [
            [layout_title],
            [sg.Frame('', layout_result, border_width=1, right_click_menu=['&Right', self.right_button_menu])],
        ]
        return layout

    """数据工厂"""
    def windowDataTools(self):
        """数据工厂"""
        layout = self.tool_data_ui()

        windows = sg.Window("图像分家", layout, finalize=True, enable_close_attempted_event=True)  # 加载布局生成窗口
        while True:
            event, value = windows.read(300)
            if event == "执行":
                self.loading_btn_onclick(value, windows)

            elif event == "中止":
                self.suspend_btn_onclick()

            elif event == "重置":
                windows['debug_result'].Update(value='')
                windows['basePath'].Update(value='')
                windows['newPath'].Update(value='')
                sg.popup_quick_message("已重置", auto_close_duration=1)

            elif event == "源码":
                if sg.popup_yes_no("是否打开存放源码的博客?", title='确认') == 'Yes':
                    url = 'http://t.csdn.cn/g1Ajz'
                    webbrowser.open(url)

            elif event in [sg.WINDOW_CLOSE_ATTEMPTED_EVENT, sg.WIN_X_EVENT] and sg.popup_yes_no('退出程序?',
                                                                                                title='确认') == 'Yes':
                windows.close()

            elif event in [sg.WINDOW_CLOSED, sg.WIN_CLOSED]:
                try:
                    windows.close()
                    break
                except:
                    return


if __name__ == '__main__':
    WindowsTool = WindowsTool()
    WindowsTool.windowDataTools()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值