├── .gitignore ├── LICENSE ├── app ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-313.pyc │ └── config.cpython-313.pyc ├── config.py ├── log │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-313.pyc │ │ ├── exception_handling.cpython-313.pyc │ │ └── logger.cpython-313.pyc │ ├── exception_handling.py │ └── logger.py ├── model │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-313.pyc │ │ ├── doc_cov_model.cpython-313.pyc │ │ └── file_model.cpython-313.pyc │ ├── doc_cov_model.py │ └── file_model.py ├── ui │ ├── Icon.py │ ├── __init__.py │ ├── __pycache__ │ │ ├── Icon.cpython-313.pyc │ │ ├── __init__.cpython-313.pyc │ │ ├── global_signal.cpython-313.pyc │ │ ├── mainview.cpython-313.pyc │ │ └── mainwindow.cpython-313.pyc │ ├── components │ │ ├── CAvatar.py │ │ ├── QCursorGif.py │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── QCursorGif.cpython-313.pyc │ │ │ ├── __init__.cpython-313.pyc │ │ │ ├── flowlayout.cpython-313.pyc │ │ │ ├── router.cpython-313.pyc │ │ │ └── scroll_bar.cpython-313.pyc │ │ ├── calendar_dialog.py │ │ ├── file_list │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ │ ├── __init__.cpython-313.pyc │ │ │ │ └── file_item_ui.cpython-313.pyc │ │ │ ├── file_item_ui.py │ │ │ ├── file_item_ui.ui │ │ │ └── file_list_widget.py │ │ ├── flowlayout.py │ │ ├── form.py │ │ ├── form.ui │ │ ├── prompt_bar.py │ │ ├── router.py │ │ ├── scroll_bar.py │ │ └── sidebar │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-313.pyc │ │ │ ├── sidebar.cpython-313.pyc │ │ │ └── sidebar_ui.cpython-313.pyc │ │ │ ├── sidebar.py │ │ │ ├── sidebar_ui.py │ │ │ └── sidebar_ui.ui │ ├── doc_convert │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-313.pyc │ │ │ ├── doc_convert.cpython-313.pyc │ │ │ └── doc_convert_ui.cpython-313.pyc │ │ ├── doc_convert.py │ │ ├── doc_convert_ui.py │ │ ├── doc_convert_ui.ui │ │ ├── pdf2image │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ │ ├── __init__.cpython-313.pyc │ │ │ │ ├── pdf2image.cpython-313.pyc │ │ │ │ └── pdf2image_ui.cpython-313.pyc │ │ │ ├── pdf2image.py │ │ │ ├── pdf2image_ui.py │ │ │ └── pdf2image_ui.ui │ │ ├── pdf2wordui │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ │ ├── __init__.cpython-313.pyc │ │ │ │ ├── pdf2word.cpython-313.pyc │ │ │ │ └── pdf2word_ui.cpython-313.pyc │ │ │ ├── pdf2word.py │ │ │ ├── pdf2word_ui.py │ │ │ └── pdf2word_ui.ui │ │ └── web2pdf │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-313.pyc │ │ │ ├── web2pdf.cpython-313.pyc │ │ │ └── web2pdf_ui.cpython-313.pyc │ │ │ ├── web2pdf.py │ │ │ ├── web2pdf_ui.py │ │ │ └── web2pdf_ui.ui │ ├── global_signal.py │ ├── image_tools │ │ ├── __pycache__ │ │ │ ├── image_tool.cpython-313.pyc │ │ │ └── image_tool_ui.cpython-313.pyc │ │ ├── image_tool.py │ │ ├── image_tool_ui.py │ │ ├── image_tool_ui.ui │ │ └── modify_date │ │ │ ├── __pycache__ │ │ │ ├── modify_date.cpython-313.pyc │ │ │ └── modify_date_ui.cpython-313.pyc │ │ │ ├── modify_date.py │ │ │ ├── modify_date_ui.py │ │ │ └── modify_date_ui.ui │ ├── mainview.py │ ├── mainwindow.py │ ├── mainwindow.ui │ ├── memotrace_enhance │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-313.pyc │ │ │ ├── enhance.cpython-313.pyc │ │ │ └── enhance_ui.cpython-313.pyc │ │ ├── enhance.py │ │ ├── enhance_ui.py │ │ ├── enhance_ui.ui │ │ └── toc │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-313.pyc │ │ │ ├── toc.cpython-313.pyc │ │ │ └── toc_ui.cpython-313.pyc │ │ │ ├── toc.py │ │ │ ├── toc_ui.py │ │ │ └── toc_ui.ui │ ├── pdf_tools │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-313.pyc │ │ │ ├── pdf_tool.cpython-313.pyc │ │ │ └── pdf_tool_ui.cpython-313.pyc │ │ ├── blank_pages │ │ │ ├── blank_pages.py │ │ │ └── blank_pages_ui.py │ │ ├── conversion │ │ │ └── __init__.py │ │ ├── merge │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ │ ├── __init__.cpython-313.pyc │ │ │ │ ├── encrypt_dialog.cpython-313.pyc │ │ │ │ ├── encrypt_dialog_ui.cpython-313.pyc │ │ │ │ ├── merge.cpython-313.pyc │ │ │ │ └── merge_ui.cpython-313.pyc │ │ │ ├── encrypt_dialog.py │ │ │ ├── encrypt_dialog_ui.py │ │ │ ├── encrypt_dialog_ui.ui │ │ │ ├── merge.py │ │ │ ├── merge_ui.py │ │ │ └── merge_ui.ui │ │ ├── pdf_tool.py │ │ ├── pdf_tool_ui.py │ │ ├── pdf_tool_ui.ui │ │ ├── security │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ │ ├── __init__.cpython-313.pyc │ │ │ │ ├── decrypt.cpython-313.pyc │ │ │ │ ├── decrypt_ui.cpython-313.pyc │ │ │ │ ├── encrypt.cpython-313.pyc │ │ │ │ └── encrypt_ui.cpython-313.pyc │ │ │ ├── decrypt.py │ │ │ ├── decrypt_ui.py │ │ │ ├── encrypt.py │ │ │ └── encrypt_ui.py │ │ ├── split │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ │ ├── __init__.cpython-313.pyc │ │ │ │ ├── split.cpython-313.pyc │ │ │ │ └── split_ui.cpython-313.pyc │ │ │ ├── split.py │ │ │ └── split_ui.py │ │ └── watermark │ │ │ ├── watermark.py │ │ │ └── watermark_ui.py │ ├── screen_record │ │ ├── __init__.py │ │ ├── screen_record.py │ │ ├── screen_record_ui.py │ │ └── screen_record_ui.ui │ ├── setting │ │ ├── __pycache__ │ │ │ ├── about_dialog.cpython-313.pyc │ │ │ ├── seetingUi.cpython-313.pyc │ │ │ └── setting.cpython-313.pyc │ │ ├── about_dialog.py │ │ ├── seetingUi.py │ │ ├── seetingUi.ui │ │ └── setting.py │ └── video_tools │ │ ├── __pycache__ │ │ ├── video_tool.cpython-313.pyc │ │ └── video_tool_ui.cpython-313.pyc │ │ ├── video_tool.py │ │ ├── video_tool_ui.py │ │ └── video_tool_ui.ui └── util │ ├── __init__.py │ ├── __pycache__ │ ├── __init__.cpython-313.pyc │ └── common.cpython-313.pyc │ └── common.py ├── doc └── images │ ├── img.png │ ├── img_1.png │ ├── img_2.png │ └── img_3.png ├── main.py ├── main1.spec ├── main2.spec ├── pdf2docx ├── AFFERO GPL ├── LICENSE ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-313.pyc │ └── converter.cpython-313.pyc ├── common │ ├── Block.py │ ├── Collection.py │ ├── Element.py │ ├── __init__.py │ ├── __pycache__ │ │ ├── Block.cpython-313.pyc │ │ ├── Collection.cpython-313.pyc │ │ ├── Element.cpython-313.pyc │ │ ├── __init__.cpython-313.pyc │ │ ├── algorithm.cpython-313.pyc │ │ ├── constants.cpython-313.pyc │ │ ├── docx.cpython-313.pyc │ │ └── share.cpython-313.pyc │ ├── algorithm.py │ ├── constants.py │ ├── docx.py │ └── share.py ├── converter.py ├── font │ ├── Fonts.py │ ├── __init__.py │ └── __pycache__ │ │ ├── Fonts.cpython-313.pyc │ │ └── __init__.cpython-313.pyc ├── image │ ├── Image.py │ ├── ImageBlock.py │ ├── ImageSpan.py │ ├── ImagesExtractor.py │ ├── __init__.py │ └── __pycache__ │ │ ├── Image.cpython-313.pyc │ │ ├── ImageBlock.cpython-313.pyc │ │ ├── ImageSpan.cpython-313.pyc │ │ ├── ImagesExtractor.cpython-313.pyc │ │ └── __init__.cpython-313.pyc ├── layout │ ├── Blocks.py │ ├── Column.py │ ├── Layout.py │ ├── Section.py │ ├── Sections.py │ ├── __init__.py │ └── __pycache__ │ │ ├── Blocks.cpython-313.pyc │ │ ├── Column.cpython-313.pyc │ │ ├── Layout.cpython-313.pyc │ │ ├── Section.cpython-313.pyc │ │ ├── Sections.cpython-313.pyc │ │ └── __init__.cpython-313.pyc ├── page │ ├── BasePage.py │ ├── Page.py │ ├── Pages.py │ ├── RawPage.py │ ├── RawPageFactory.py │ ├── RawPageFitz.py │ ├── __init__.py │ └── __pycache__ │ │ ├── BasePage.cpython-313.pyc │ │ ├── Page.cpython-313.pyc │ │ ├── Pages.cpython-313.pyc │ │ ├── RawPage.cpython-313.pyc │ │ ├── RawPageFactory.cpython-313.pyc │ │ ├── RawPageFitz.cpython-313.pyc │ │ └── __init__.cpython-313.pyc ├── shape │ ├── Path.py │ ├── Paths.py │ ├── Shape.py │ ├── Shapes.py │ ├── __init__.py │ └── __pycache__ │ │ ├── Path.cpython-313.pyc │ │ ├── Paths.cpython-313.pyc │ │ ├── Shape.cpython-313.pyc │ │ ├── Shapes.cpython-313.pyc │ │ └── __init__.cpython-313.pyc ├── table │ ├── Border.py │ ├── Cell.py │ ├── Cells.py │ ├── Row.py │ ├── Rows.py │ ├── TableBlock.py │ ├── TableStructure.py │ ├── TablesConstructor.py │ ├── __init__.py │ └── __pycache__ │ │ ├── Cell.cpython-313.pyc │ │ ├── Cells.cpython-313.pyc │ │ ├── Row.cpython-313.pyc │ │ ├── Rows.cpython-313.pyc │ │ ├── TableBlock.cpython-313.pyc │ │ └── __init__.cpython-313.pyc └── text │ ├── Char.py │ ├── Line.py │ ├── Lines.py │ ├── Spans.py │ ├── TextBlock.py │ ├── TextSpan.py │ ├── __init__.py │ └── __pycache__ │ ├── Char.cpython-313.pyc │ ├── Line.cpython-313.pyc │ ├── Lines.cpython-313.pyc │ ├── Spans.cpython-313.pyc │ ├── TextBlock.cpython-313.pyc │ ├── TextSpan.cpython-313.pyc │ └── __init__.cpython-313.pyc ├── readme.md ├── requirements.txt ├── resource.qrc ├── resource_rc.py ├── resources ├── QSS │ └── style.qss ├── __init__.py ├── icons │ ├── Cursors │ │ ├── 0.png │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ ├── 6.png │ │ └── 7.png │ ├── Excel.svg │ ├── __init__.py │ ├── arrow-left.svg │ ├── arrow-right.svg │ ├── back.svg │ ├── down.svg │ ├── help.svg │ ├── html.svg │ ├── logo.ico │ ├── logo.png │ ├── markdown.svg │ ├── pdf.svg │ ├── select.svg │ ├── setting.svg │ ├── txt.svg │ ├── unselect.svg │ ├── up.svg │ ├── weixin.png │ ├── word.svg │ ├── 减.svg │ ├── 删除.svg │ ├── 加.svg │ ├── 右展开.svg │ ├── 合并.svg │ ├── 图片.svg │ ├── 增加.svg │ ├── 工具箱.svg │ ├── 左展开.svg │ ├── 录屏.svg │ ├── 批量操作.svg │ ├── 批量添加.svg │ ├── 拆分.svg │ ├── 文档转换.svg │ ├── 水印.svg │ ├── 视频.svg │ ├── 钥匙.svg │ └── 锁.svg └── images │ └── logo.png ├── screenshot └── screenrecord.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | ### Qt template 2 | # C++ objects and libs 3 | *.slo 4 | *.lo 5 | *.o 6 | *.a 7 | *.la 8 | *.lai 9 | *.so 10 | *.so.* 11 | *.dll 12 | *.dylib 13 | 14 | # Qt-es 15 | object_script.*.Release 16 | object_script.*.Debug 17 | *_plugin_import.cpp 18 | /.qmake.cache 19 | /.qmake.stash 20 | *.pro.user 21 | *.pro.user.* 22 | *.qbs.user 23 | *.qbs.user.* 24 | *.moc 25 | moc_*.cpp 26 | moc_*.h 27 | qrc_*.cpp 28 | ui_*.h 29 | *.qmlc 30 | *.jsc 31 | Makefile* 32 | *build-* 33 | *.qm 34 | *.prl 35 | 36 | # Qt unit tests 37 | target_wrapper.* 38 | 39 | # QtCreator 40 | *.autosave 41 | 42 | # QtCreator Qml 43 | *.qmlproject.user 44 | *.qmlproject.user.* 45 | 46 | # QtCreator CMake 47 | CMakeLists.txt.user* 48 | 49 | # QtCreator 4.8< compilation database 50 | compile_commands.json 51 | 52 | # QtCreator local machine specific files for imported projects 53 | *creator.user* 54 | 55 | *_qmlcache.qrc 56 | 57 | ### PyCharm template 58 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 59 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 60 | 61 | # User-specific stuff 62 | .idea/**/workspace.xml 63 | .idea/**/tasks.xml 64 | .idea/**/usage.statistics.xml 65 | .idea/**/dictionaries 66 | .idea/**/shelf 67 | 68 | # AWS User-specific 69 | .idea/**/aws.xml 70 | 71 | # Generated files 72 | .idea/**/contentModel.xml 73 | 74 | # Sensitive or high-churn files 75 | .idea/**/dataSources/ 76 | .idea/**/dataSources.ids 77 | .idea/**/dataSources.local.xml 78 | .idea/**/sqlDataSources.xml 79 | .idea/**/dynamic.xml 80 | .idea/**/uiDesigner.xml 81 | .idea/**/dbnavigator.xml 82 | 83 | # Gradle 84 | .idea/**/gradle.xml 85 | .idea/**/libraries 86 | 87 | # Gradle and Maven with auto-import 88 | # When using Gradle or Maven with auto-import, you should exclude module files, 89 | # since they will be recreated, and may cause churn. Uncomment if using 90 | # auto-import. 91 | # .idea/artifacts 92 | # .idea/compiler.xml 93 | # .idea/jarRepositories.xml 94 | # .idea/modules.xml 95 | # .idea/*.iml 96 | # .idea/modules 97 | # *.iml 98 | # *.ipr 99 | 100 | # CMake 101 | cmake-build-*/ 102 | 103 | # Mongo Explorer plugin 104 | .idea/**/mongoSettings.xml 105 | 106 | # File-based project format 107 | *.iws 108 | 109 | # IntelliJ 110 | out/ 111 | 112 | # mpeltonen/sbt-idea plugin 113 | .idea_modules/ 114 | 115 | # JIRA plugin 116 | atlassian-ide-plugin.xml 117 | 118 | # Cursive Clojure plugin 119 | .idea/replstate.xml 120 | 121 | # SonarLint plugin 122 | .idea/sonarlint/ 123 | 124 | # Crashlytics plugin (for Android Studio and IntelliJ) 125 | com_crashlytics_export_strings.xml 126 | crashlytics.properties 127 | crashlytics-build.properties 128 | fabric.properties 129 | 130 | # Editor-based Rest Client 131 | .idea/httpRequests 132 | 133 | # Android studio 3.1+ serialized cache file 134 | .idea/caches/build_file_checksums.ser 135 | 136 | ### Example user template template 137 | ### Example user template 138 | 139 | # IntelliJ project files 140 | .idea 141 | *.iml 142 | out 143 | gen 144 | dist 145 | build 146 | *test* 147 | *.log 148 | *.c 149 | *.pyd 150 | __pycache__/ 151 | *.pyc -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @Time : 2024/11/7 10:51 6 | @Author : SiYuan 7 | @Email : siyuan044@qq.com 8 | @File : EasyBox-__init__.py.py 9 | @Description : 10 | """ 11 | 12 | if __name__ == '__main__': 13 | pass 14 | -------------------------------------------------------------------------------- /app/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /app/__pycache__/config.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/__pycache__/config.cpython-313.pyc -------------------------------------------------------------------------------- /app/config.py: -------------------------------------------------------------------------------- 1 | version = '0.1.4' 2 | contact = '' 3 | github = '' 4 | website = 'https://memotrace.cn/tools/' 5 | copyright = '© 2022-2024 SiYuan' 6 | license = '' 7 | description = [ 8 | 9 | ] 10 | about = f''' 11 | 版本:{version}
12 | QQ交流群:请关注微信公众号回复:联系方式
13 | 地址:{github}
14 | 官网:{website}
15 | 新特性:
{''.join(['' + i for i in description])}
16 | Copyright {copyright} 17 | ''' -------------------------------------------------------------------------------- /app/log/__init__.py: -------------------------------------------------------------------------------- 1 | from .logger import log, logger 2 | 3 | __all__ = ["logger", "log"] 4 | -------------------------------------------------------------------------------- /app/log/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/log/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /app/log/__pycache__/exception_handling.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/log/__pycache__/exception_handling.cpython-313.pyc -------------------------------------------------------------------------------- /app/log/__pycache__/logger.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/log/__pycache__/logger.cpython-313.pyc -------------------------------------------------------------------------------- /app/log/exception_handling.py: -------------------------------------------------------------------------------- 1 | import sys,os 2 | import traceback 3 | from app.config import version 4 | 5 | 6 | class ExceptionHanding: 7 | def __init__(self, exc_type, exc_value, traceback_): 8 | self.exc_type = exc_type 9 | self.exc_value = exc_value 10 | self.traceback = traceback_ 11 | self.error_message = ''.join(traceback.format_exception(exc_type, exc_value, traceback_)) 12 | 13 | def parser_exc(self): 14 | return (f'{self.error_message}\n未知错误类型,可参考 https://memotrace.cn/doc/posts/error/faq.html ' 15 | f'解决该问题\n温馨提示:重启电脑可解决80%的问题') 16 | 17 | def __str__(self): 18 | errmsg = f'version:{version}\n{self.parser_exc()}' 19 | return errmsg 20 | 21 | 22 | def excepthook(exc_type, exc_value, traceback_): 23 | # 将异常信息转为字符串 24 | 25 | # 在这里处理全局异常 26 | 27 | error_message = ExceptionHanding(exc_type, exc_value, traceback_) 28 | txt = '您可添加QQ群发送log文件以便解决该问题' 29 | msg = f"Exception Type: {exc_type.__name__}\nException Value: {exc_value}\ndetails: {error_message}\n\n{txt}" 30 | print(msg) 31 | 32 | # 调用原始的 excepthook,以便程序正常退出 33 | sys.__excepthook__(exc_type, exc_value, traceback_) 34 | -------------------------------------------------------------------------------- /app/log/logger.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import time 4 | import traceback 5 | from functools import wraps 6 | filename = time.strftime("%Y-%m-%d", time.localtime(time.time())) 7 | logger = logging.getLogger('test') 8 | logger.setLevel(level=logging.DEBUG) 9 | formatter = logging.Formatter('%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s') 10 | try: 11 | if not os.path.exists('./app/log/logs'): 12 | os.mkdir('./app/log/logs') 13 | file_handler = logging.FileHandler(f'./app/log/logs/{filename}-log.log') 14 | except: 15 | file_handler = logging.FileHandler(f'日志文件-{filename}-log.log') 16 | 17 | file_handler.setLevel(level=logging.INFO) 18 | file_handler.setFormatter(formatter) 19 | stream_handler = logging.StreamHandler() 20 | stream_handler.setLevel(logging.DEBUG) 21 | stream_handler.setFormatter(formatter) 22 | logger.addHandler(file_handler) 23 | logger.addHandler(stream_handler) 24 | 25 | 26 | def log(func): 27 | @wraps(func) 28 | def log_(*args, **kwargs): 29 | try: 30 | return func(*args, **kwargs) 31 | except Exception as e: 32 | logger.error( 33 | f"\n{func.__qualname__} is error,params:{(args, kwargs)},here are details:\n{traceback.format_exc()}") 34 | return log_ 35 | -------------------------------------------------------------------------------- /app/model/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @Time : 2024/11/20 14:30 6 | @Author : SiYuan 7 | @Email : siyuan044@qq.com 8 | @File : EasyBox-__init__.py.py 9 | @Description : 10 | """ 11 | from .file_model import FileInfo, PdfFile, FileType, ImageFile 12 | from .doc_cov_model import Pdf2ImageOpt, Pdf2DocxOpt 13 | 14 | if __name__ == '__main__': 15 | pass 16 | -------------------------------------------------------------------------------- /app/model/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/model/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /app/model/__pycache__/doc_cov_model.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/model/__pycache__/doc_cov_model.cpython-313.pyc -------------------------------------------------------------------------------- /app/model/__pycache__/file_model.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/model/__pycache__/file_model.cpython-313.pyc -------------------------------------------------------------------------------- /app/model/doc_cov_model.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @Time : 2024/11/26 0:56 6 | @Author : SiYuan 7 | @Email : siyuan044@qq.com 8 | @File : EasyBox-doc_cov_model.py 9 | @Description : 描述文档转换的配置选项 10 | """ 11 | import re 12 | 13 | 14 | def format_file_name(filename): 15 | return re.sub(r'[\\/:*?"<>|\s\.]', '_', filename) 16 | 17 | 18 | class Pdf2DocxOpt: 19 | def __init__(self, output_path): 20 | self.o_dir = output_path 21 | 22 | 23 | class Pdf2ImageOpt: 24 | def __init__(self, output_dir, format_='png', dpi=200): 25 | self.o_dir = output_dir 26 | self.o_fmt = format_ 27 | self.o_name = '(转图片)' 28 | self.o_quality = 100 29 | self.o_dpi = dpi 30 | 31 | 32 | class Web2PdfOpt: 33 | def __init__(self, url: str, output_dir='.'): 34 | self.url = url 35 | self.is_url = url.startswith('http') 36 | self.o_dir = output_dir 37 | self.o_name = '' 38 | self.set_name(url) 39 | 40 | def set_name(self, name: str): 41 | name = format_file_name(name) 42 | if name.endswith('.pdf'): 43 | self.o_name = name 44 | else: 45 | self.o_name = name + '.pdf' 46 | 47 | return self.o_name 48 | 49 | 50 | if __name__ == '__main__': 51 | pass 52 | -------------------------------------------------------------------------------- /app/ui/Icon.py: -------------------------------------------------------------------------------- 1 | from PySide6.QtGui import QIcon 2 | 3 | import resource_rc 4 | 5 | var = resource_rc.qt_resource_name 6 | 7 | 8 | class Icon: 9 | logo_path = ':/icons/resources/icons/logo.png' 10 | logo_ico_path = ':/icons/resources/icons/logo.ico' 11 | # MainWindow_Icon = QIcon(':/icons/icons/logo.svg') 12 | Back = QIcon(':/icons/resources/icons/back.svg') 13 | Tool_Icon = QIcon(':/icons/resources/icons/工具箱.svg') 14 | Arrow_left_Icon = QIcon(':/icons/resources/icons/arrow-left.svg') 15 | Arrow_right_Icon = QIcon(':/icons/resources/icons/arrow-right.svg') 16 | Setting_Icon = QIcon(':/icons/resources/icons/setting.svg') 17 | Add_Icon = QIcon(':/icons/icons/resources/批量添加.svg') 18 | Exp_left_Icon = QIcon(':/icons/resources/icons/左展开.svg') 19 | Exp_right_Icon = QIcon(':/icons/resources/icons/右展开.svg') 20 | Img_Icon = QIcon(':/icons/resources/icons/图片.svg') 21 | PDF_Icon = QIcon(':/icons/resources/icons/pdf.svg') 22 | Doc_Transfer_Icon = QIcon(':/icons/resources/icons/文档转换.svg') 23 | Batch_Icon = QIcon(':/icons/resources/icons/批量操作.svg') 24 | Video_Icon = QIcon(':/icons/resources/icons/视频.svg') 25 | -------------------------------------------------------------------------------- /app/ui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/__init__.py -------------------------------------------------------------------------------- /app/ui/__pycache__/Icon.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/__pycache__/Icon.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/__pycache__/global_signal.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/__pycache__/global_signal.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/__pycache__/mainview.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/__pycache__/mainview.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/__pycache__/mainwindow.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/__pycache__/mainwindow.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/components/QCursorGif.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Created on 2020年3月13日 6 | @author: Irony 7 | @site: https://pyqt.site , https://github.com/PySide6 8 | @email: 892768447@qq.com 9 | @file: Demo.Lib.QCursorGif 10 | @description: 11 | """ 12 | 13 | # try: 14 | # from PySide6.QtCore import QTimer, Qt 15 | # from PySide6.QtGui import QCursor, QPixmap 16 | # from PySide6.QtWidgets import QApplication 17 | # except ImportError: 18 | # from PySide6.QtCore import QTimer, Qt 19 | # from PySide6.QtGui import QCursor, QPixmap 20 | # from PySide6.QtWidgets import QApplication 21 | 22 | from PySide6.QtCore import QTimer, Qt 23 | from PySide6.QtGui import QCursor, QPixmap 24 | from PySide6.QtWidgets import QApplication 25 | import resource_rc 26 | 27 | var = resource_rc.qt_resource_name 28 | 29 | 30 | class QCursorGif: 31 | 32 | def initCursor(self, cursors, parent=None): 33 | # 记录默认的光标 34 | self._oldCursor = Qt.ArrowCursor 35 | self.setOldCursor(parent) 36 | # 加载光标图片 37 | self._cursorImages = [ 38 | QCursor(QPixmap(cursor)) for cursor in cursors] 39 | self._cursorIndex = 0 40 | self._cursorCount = len(self._cursorImages) - 1 41 | # 创建刷新定时器 42 | self._cursorTimeout = 200 43 | self._cursorTimer = QTimer(parent) 44 | self._cursorTimer.timeout.connect(self._doBusy) 45 | self.num = 0 46 | 47 | def _doBusy(self): 48 | if self._cursorIndex > self._cursorCount: 49 | self._cursorIndex = 0 50 | QApplication.setOverrideCursor( 51 | self._cursorImages[self._cursorIndex]) 52 | self._cursorIndex += 1 53 | self.num += 1 54 | 55 | def startBusy(self): 56 | # QApplication.setOverrideCursor(Qt.WaitCursor) 57 | if not self._cursorTimer.isActive(): 58 | self._cursorTimer.start(self._cursorTimeout) 59 | 60 | def stopBusy(self): 61 | self._cursorTimer.stop() 62 | QApplication.restoreOverrideCursor() 63 | # 将光标出栈,恢复至原始状态 64 | for i in range(self.num): 65 | QApplication.restoreOverrideCursor() 66 | self.num = 0 67 | 68 | 69 | def setCursorTimeout(self, timeout): 70 | self._cursorTimeout = timeout 71 | 72 | def setOldCursor(self, parent=None): 73 | QApplication.overrideCursor() 74 | self._oldCursor = (QApplication.overrideCursor() or parent.cursor() or Qt.ArrowCursor or Qt.IBeamCursor) if parent else ( 75 | QApplication.overrideCursor() or Qt.ArrowCursor) 76 | -------------------------------------------------------------------------------- /app/ui/components/__init__.py: -------------------------------------------------------------------------------- 1 | from .scroll_bar import ScrollBar 2 | from .QCursorGif import QCursorGif 3 | from app.ui.components.sidebar.sidebar import Sidebar, SidebarButton 4 | from .flowlayout import FlowLayout 5 | -------------------------------------------------------------------------------- /app/ui/components/__pycache__/QCursorGif.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/components/__pycache__/QCursorGif.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/components/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/components/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/components/__pycache__/flowlayout.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/components/__pycache__/flowlayout.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/components/__pycache__/router.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/components/__pycache__/router.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/components/__pycache__/scroll_bar.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/components/__pycache__/scroll_bar.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/components/calendar_dialog.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from datetime import datetime, timedelta 4 | from PySide6.QtCore import Signal, Qt, QLocale 5 | from PySide6.QtWidgets import QApplication, QDialog, QCalendarWidget, QVBoxLayout, \ 6 | QToolButton 7 | 8 | from app.ui.Icon import Icon 9 | 10 | 11 | class CalendarDialog(QDialog): 12 | selected_date_signal = Signal(int) 13 | 14 | def __init__(self, date_range=None, parent=None): 15 | """ 16 | 17 | @param date_range: tuple[Union[QDate, datetime.date],Union[QDate, datetime.date]] #限定的可选择范围 18 | @param parent: 19 | """ 20 | super().__init__(parent) 21 | self.setWindowTitle('选择日期') 22 | self.calendar = QCalendarWidget(self) 23 | self.calendar.setLocale(QLocale(QLocale.Chinese, QLocale.China)) 24 | self.calendar.clicked.connect(self.onDateChanged) 25 | prev_btn = self.calendar.findChild(QToolButton, "qt_calendar_prevmonth") 26 | prev_btn.setIcon(Icon.Arrow_left_Icon) 27 | next_btn = self.calendar.findChild(QToolButton, "qt_calendar_nextmonth") 28 | next_btn.setIcon(Icon.Arrow_right_Icon) 29 | self.date_range = date_range 30 | if date_range: 31 | self.calendar.setDateRange(*date_range) 32 | # 从第一天开始,依次添加日期到列表,直到该月的最后一天 33 | current_date = date_range[1] 34 | while (current_date + timedelta(days=1)).month == date_range[1].month: 35 | current_date += timedelta(days=1) 36 | range_format = self.calendar.dateTextFormat(current_date) 37 | range_format.setForeground(Qt.gray) 38 | self.calendar.setDateTextFormat(current_date, range_format) 39 | # 从第一天开始,依次添加日期到列表,直到该月的最后一天 40 | current_date = date_range[0] 41 | while (current_date - timedelta(days=1)).month == date_range[0].month: 42 | current_date -= timedelta(days=1) 43 | range_format = self.calendar.dateTextFormat(current_date) 44 | range_format.setForeground(Qt.gray) 45 | self.calendar.setDateTextFormat(current_date, range_format) 46 | layout = QVBoxLayout(self) 47 | layout.addWidget(self.calendar) 48 | self.setLayout(layout) 49 | 50 | def set_start_date(self): 51 | if self.date_range: 52 | self.calendar.setCurrentPage(self.date_range[0].year, self.date_range[0].month) 53 | 54 | def set_end_date(self): 55 | if self.date_range: 56 | self.calendar.setCurrentPage(self.date_range[1].year, self.date_range[1].month) 57 | 58 | def onDateChanged(self): 59 | # 获取选择的日期 60 | selected_date = self.calendar.selectedDate() 61 | selected_date_str = selected_date.toString(Qt.ISODate) # 使用 ISO 标准格式 62 | s_t = time.strptime(selected_date_str, "%Y-%m-%d") # 返回元祖 63 | mkt = int(time.mktime(s_t)) 64 | timestamp = mkt 65 | self.selected_date_signal.emit(timestamp) 66 | print("Selected Date:", selected_date_str, timestamp) 67 | self.close() 68 | 69 | 70 | if __name__ == '__main__': 71 | import sys 72 | from datetime import datetime, timedelta 73 | 74 | app = QApplication(sys.argv) 75 | # 设置日期范围 76 | start_date = datetime(2024, 1, 5) 77 | end_date = datetime(2024, 1, 9) 78 | date_range = (start_date.date(), end_date.date()) 79 | ex = CalendarDialog(date_range=date_range) 80 | ex.show() 81 | sys.exit(app.exec_()) 82 | -------------------------------------------------------------------------------- /app/ui/components/file_list/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/components/file_list/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/components/file_list/__pycache__/file_item_ui.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/components/file_list/__pycache__/file_item_ui.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/components/file_list/file_list_widget.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @Time : 2024/11/8 22:38 6 | @Author : SiYuan 7 | @Email : siyuan044@qq.com 8 | @File : EasyBox-file_list_widget.py 9 | @Description : 10 | """ 11 | 12 | if __name__ == '__main__': 13 | pass 14 | -------------------------------------------------------------------------------- /app/ui/components/flowlayout.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @Time : 2024/11/30 19:26 6 | @Author : SiYuan 7 | @Email : siyuan044@qq.com 8 | @File : EasyBox-flowlayout.py 9 | @Description : 10 | """ 11 | try: 12 | from PySide6.QtCore import QPoint, QRect, QSize, Qt 13 | from PySide6.QtWidgets import (QApplication, QLayout, QPushButton, 14 | QSizePolicy, QWidget) 15 | except ImportError: 16 | from PySide2.QtCore import QPoint, QRect, QSize, Qt 17 | from PySide2.QtWidgets import (QApplication, QLayout, QPushButton, 18 | QSizePolicy, QWidget) 19 | 20 | 21 | class FlowLayout(QLayout): 22 | def __init__(self, parent=None, margin=0, spacing=-1): 23 | super(FlowLayout, self).__init__(parent) 24 | 25 | if parent is not None: 26 | self.setContentsMargins(margin, margin, margin, margin) 27 | 28 | self.setSpacing(spacing) 29 | 30 | self.itemList = [] 31 | 32 | def __del__(self): 33 | item = self.takeAt(0) 34 | while item: 35 | item = self.takeAt(0) 36 | 37 | def addItem(self, item): 38 | self.itemList.append(item) 39 | 40 | def clear(self): 41 | item = self.takeAt(0) 42 | while item: 43 | item = self.takeAt(0) 44 | 45 | def count(self): 46 | return len(self.itemList) 47 | 48 | def itemAt(self, index): 49 | if index >= 0 and index < len(self.itemList): 50 | return self.itemList[index] 51 | 52 | return None 53 | 54 | def takeAt(self, index): 55 | if index >= 0 and index < len(self.itemList): 56 | return self.itemList.pop(index) 57 | 58 | return None 59 | 60 | def expandingDirections(self): 61 | return Qt.Orientations(Qt.Orientation(0)) 62 | 63 | def hasHeightForWidth(self): 64 | return True 65 | 66 | def heightForWidth(self, width): 67 | height = self.doLayout(QRect(0, 0, width, 0), True) 68 | return height 69 | 70 | def setGeometry(self, rect): 71 | super(FlowLayout, self).setGeometry(rect) 72 | self.doLayout(rect, False) 73 | 74 | def sizeHint(self): 75 | return self.minimumSize() 76 | 77 | def minimumSize(self): 78 | size = QSize() 79 | 80 | for item in self.itemList: 81 | size = size.expandedTo(item.minimumSize()) 82 | 83 | margin, _, _, _ = self.getContentsMargins() 84 | 85 | size += QSize(2 * margin, 2 * margin) 86 | return size 87 | 88 | def doLayout(self, rect, testOnly): 89 | x = rect.x() 90 | y = rect.y() 91 | lineHeight = 0 92 | 93 | for item in self.itemList: 94 | wid = item.widget() 95 | spaceX = self.spacing() + wid.style().layoutSpacing(QSizePolicy.PushButton, 96 | QSizePolicy.PushButton, Qt.Horizontal) 97 | spaceY = self.spacing() + wid.style().layoutSpacing(QSizePolicy.PushButton, 98 | QSizePolicy.PushButton, Qt.Vertical) 99 | nextX = x + item.sizeHint().width() + spaceX 100 | if nextX - spaceX > rect.right() and lineHeight > 0: 101 | x = rect.x() 102 | y = y + lineHeight + spaceY 103 | nextX = x + item.sizeHint().width() + spaceX 104 | lineHeight = 0 105 | 106 | if not testOnly: 107 | item.setGeometry(QRect(QPoint(x, y), item.sizeHint())) 108 | 109 | x = nextX 110 | lineHeight = max(lineHeight, item.sizeHint().height()) 111 | 112 | return y + lineHeight - rect.y() 113 | 114 | 115 | class Window(QWidget): 116 | def __init__(self): 117 | super(Window, self).__init__() 118 | 119 | flowLayout = FlowLayout() 120 | flowLayout.addWidget(QPushButton("Short")) 121 | flowLayout.addWidget(QPushButton("Longer")) 122 | flowLayout.addWidget(QPushButton("Different text")) 123 | flowLayout.addWidget(QPushButton("More text")) 124 | flowLayout.addWidget(QPushButton("Even longer button text")) 125 | self.setLayout(flowLayout) 126 | 127 | self.setWindowTitle("Flow Layout") 128 | 129 | 130 | if __name__ == '__main__': 131 | import sys 132 | 133 | app = QApplication(sys.argv) 134 | mainWin = Window() 135 | mainWin.show() 136 | sys.exit(app.exec_()) 137 | -------------------------------------------------------------------------------- /app/ui/components/form.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'form.ui' 4 | # 5 | # Created by: PySide6 UI code generator 5.15.10 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PySide6 import QtCore, QtGui, QtWidgets 12 | 13 | 14 | class Ui_Form(object): 15 | def setupUi(self, Form): 16 | Form.setObjectName("Form") 17 | Form.resize(250, 92) 18 | Form.setMaximumSize(QtCore.QSize(250, 16777215)) 19 | self.gridLayout = QtWidgets.QGridLayout(Form) 20 | self.gridLayout.setObjectName("gridLayout") 21 | self.label_avatar = QtWidgets.QLabel(Form) 22 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) 23 | sizePolicy.setHorizontalStretch(0) 24 | sizePolicy.setVerticalStretch(0) 25 | sizePolicy.setHeightForWidth(self.label_avatar.sizePolicy().hasHeightForWidth()) 26 | self.label_avatar.setSizePolicy(sizePolicy) 27 | self.label_avatar.setMinimumSize(QtCore.QSize(45, 45)) 28 | self.label_avatar.setMaximumSize(QtCore.QSize(45, 45)) 29 | self.label_avatar.setObjectName("label_avatar") 30 | self.gridLayout.addWidget(self.label_avatar, 0, 0, 2, 1) 31 | self.label_nickname = QtWidgets.QLabel(Form) 32 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) 33 | sizePolicy.setHorizontalStretch(0) 34 | sizePolicy.setVerticalStretch(0) 35 | sizePolicy.setHeightForWidth(self.label_nickname.sizePolicy().hasHeightForWidth()) 36 | self.label_nickname.setSizePolicy(sizePolicy) 37 | self.label_nickname.setObjectName("label_nickname") 38 | self.gridLayout.addWidget(self.label_nickname, 0, 1, 1, 1) 39 | self.label_time = QtWidgets.QLabel(Form) 40 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) 41 | sizePolicy.setHorizontalStretch(0) 42 | sizePolicy.setVerticalStretch(0) 43 | sizePolicy.setHeightForWidth(self.label_time.sizePolicy().hasHeightForWidth()) 44 | self.label_time.setSizePolicy(sizePolicy) 45 | font = QtGui.QFont() 46 | font.setPointSize(8) 47 | self.label_time.setFont(font) 48 | self.label_time.setObjectName("label_time") 49 | self.gridLayout.addWidget(self.label_time, 0, 2, 1, 1) 50 | self.label_content = QtWidgets.QLabel(Form) 51 | font = QtGui.QFont() 52 | font.setPointSize(8) 53 | self.label_content.setFont(font) 54 | self.label_content.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse) 55 | self.label_content.setObjectName("label_content") 56 | self.gridLayout.addWidget(self.label_content, 1, 1, 1, 2) 57 | 58 | self.retranslateUi(Form) 59 | QtCore.QMetaObject.connectSlotsByName(Form) 60 | 61 | def retranslateUi(self, Form): 62 | _translate = QtCore.QCoreApplication.translate 63 | Form.setWindowTitle(_translate("Form", "Form")) 64 | self.label_avatar.setText(_translate("Form", "TextLabel")) 65 | self.label_nickname.setText(_translate("Form", "TextLabel")) 66 | self.label_time.setText(_translate("Form", "24/3/17")) 67 | self.label_content.setText(_translate("Form", "TextLabel")) 68 | -------------------------------------------------------------------------------- /app/ui/components/form.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 250 10 | 92 11 | 12 | 13 | 14 | 15 | 250 16 | 16777215 17 | 18 | 19 | 20 | Form 21 | 22 | 23 | 24 | 25 | 26 | 27 | 0 28 | 0 29 | 30 | 31 | 32 | 33 | 45 34 | 45 35 | 36 | 37 | 38 | 39 | 45 40 | 45 41 | 42 | 43 | 44 | TextLabel 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 0 53 | 0 54 | 55 | 56 | 57 | TextLabel 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 0 66 | 0 67 | 68 | 69 | 70 | 71 | 8 72 | 73 | 74 | 75 | 24/3/17 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 8 84 | 85 | 86 | 87 | TextLabel 88 | 89 | 90 | Qt::LinksAccessibleByMouse 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /app/ui/components/prompt_bar.py: -------------------------------------------------------------------------------- 1 | from PySide6 import QtGui 2 | from PySide6.QtCore import * 3 | from PySide6.QtGui import * 4 | from PySide6.QtWidgets import * 5 | 6 | 7 | class PromptBar(QLabel): 8 | def __init__(self, parent=None): 9 | super().__init__(parent) 10 | 11 | def paintEvent(self, e): # 绘图事件 12 | qp = QPainter() 13 | qp.begin(self) 14 | self.drawRectangles1(qp) # 绘制线条矩形 15 | self.drawRectangles2(qp) # 绘制填充矩形 16 | self.drawRectangles3(qp) # 绘制线条+填充矩形 17 | self.drawRectangles4(qp) # 绘制线条矩形2 18 | qp.end() 19 | 20 | def drawRectangles1(self, qp): # 绘制填充矩形 21 | qp.setPen(QPen(Qt.black, 2, Qt.SolidLine)) # 颜色、线宽、线性 22 | qp.drawRect(*self.data) 23 | 24 | def drawRectangles2(self, qp): # 绘制填充矩形 25 | qp.setPen(QPen(Qt.black, 2, Qt.NoPen)) 26 | qp.setBrush(QColor(200, 0, 0)) 27 | qp.drawRect(220, 15, 200, 100) 28 | 29 | def drawRectangles3(self, qp): # 绘制线条+填充矩形 30 | qp.setPen(QPen(Qt.black, 2, Qt.SolidLine)) 31 | qp.setBrush(QColor(200, 0, 0)) 32 | qp.drawRect(430, 15, 200, 100) 33 | 34 | def drawRectangles4(self, qp): # 绘制线条矩形2 35 | path = QtGui.QPainterPath() 36 | qp.setPen(QPen(Qt.blue, 2, Qt.SolidLine)) 37 | qp.setBrush(QColor(0, 0, 0, 0)) # 设置画刷颜色透明 38 | path.addRect(100, 200, 200, 100) 39 | qp.drawPath(path) 40 | -------------------------------------------------------------------------------- /app/ui/components/router.py: -------------------------------------------------------------------------------- 1 | from PySide6.QtCore import QObject, Signal 2 | 3 | 4 | class Router(QObject): 5 | """ 路由管理器,用于管理页面和路由路径 """ 6 | route_changed = Signal(str) 7 | history_changed = Signal(int) 8 | 9 | def __init__(self, stack): 10 | """ 11 | 12 | :param stack: 存储页面的stackWidget组件,可通过索引直接显示某个页面,如果为None则直接调用页面的show方法显示到独立窗口上 13 | """ 14 | super(Router, self).__init__() 15 | self.stack = stack 16 | self.routes = {} 17 | self.history = [] 18 | self.now_router_path = '' 19 | 20 | def add_route(self, path, widget): 21 | """添加路径和页面的映射""" 22 | if self.stack is not None: 23 | index = self.stack.addWidget(widget) 24 | self.routes[path] = index 25 | else: 26 | self.routes[path] = widget 27 | index = len(self.routes) 28 | return index 29 | 30 | def navigate(self, path, turn_back=False): 31 | """ 32 | 根据路径切换到对应的页面 33 | :param path: 34 | :param turn_back: 是否是返回调用的页面切换 35 | :return: 36 | """ 37 | if self.now_router_path and not turn_back: 38 | self.history.append(self.now_router_path) 39 | self.history_changed.emit(len(self.history)) 40 | if path in self.routes: 41 | if self.stack is not None: 42 | self.stack.setCurrentIndex(self.routes[path]) 43 | else: 44 | self.routes[path].show() 45 | self.route_changed.emit(path) 46 | self.now_router_path = path 47 | print('页面切换:',self.now_router_path) 48 | else: 49 | print(f"Route '{path}' not found") 50 | 51 | def turn_back(self): 52 | if len(self.history) > 0: 53 | path = self.history.pop() 54 | self.navigate(path, True) 55 | print(f'返回:{path}') 56 | self.history_changed.emit(len(self.history)) 57 | return True 58 | else: 59 | print(f'无法返回') 60 | return False 61 | -------------------------------------------------------------------------------- /app/ui/components/scroll_bar.py: -------------------------------------------------------------------------------- 1 | from PySide6.QtWidgets import QScrollBar 2 | 3 | 4 | class ScrollBar(QScrollBar): 5 | def __init__(self): 6 | super().__init__() 7 | self.setStyleSheet( 8 | ''' 9 | QScrollBar:vertical { 10 | border-width: 0px; 11 | border: none; 12 | background:rgba(133, 135, 138, 0); 13 | width:4px; 14 | margin: 0px 0px 0px 0px; 15 | } 16 | QScrollBar::handle:vertical { 17 | background: qlineargradient(x1:0, y1:0, x2:1, y2:0, 18 | stop: 0 rgb(133, 135, 138), stop: 0.5 rgb(133, 135, 138), stop:1 rgb(133, 135, 138)); 19 | min-height: 20px; 20 | max-height: 20px; 21 | margin: 0 0px 0 0px; 22 | border-radius: 2px; 23 | } 24 | QScrollBar::add-line:vertical { 25 | background: qlineargradient(x1:0, y1:0, x2:1, y2:0, 26 | stop: 0 rgba(133, 135, 138, 0), stop: 0.5 rgba(133, 135, 138, 0), stop:1 rgba(133, 135, 138, 0)); 27 | height: 0px; 28 | border: none; 29 | subcontrol-position: bottom; 30 | subcontrol-origin: margin; 31 | } 32 | QScrollBar::sub-line:vertical { 33 | background: qlineargradient(x1:0, y1:0, x2:1, y2:0, 34 | stop: 0 rgba(133, 135, 138, 0), stop: 0.5 rgba(133, 135, 138, 0), stop:1 rgba(133, 135, 138, 0)); 35 | height: 0 px; 36 | border: none; 37 | subcontrol-position: top; 38 | subcontrol-origin: margin; 39 | } 40 | QScrollBar::sub-page:vertical { 41 | background: rgba(133, 135, 138, 0); 42 | } 43 | 44 | QScrollBar::add-page:vertical { 45 | background: rgba(133, 135, 138, 0); 46 | } 47 | ''' 48 | ) -------------------------------------------------------------------------------- /app/ui/components/sidebar/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @Time : 2024/11/7 10:51 6 | @Author : SiYuan 7 | @Email : siyuan044@qq.com 8 | @File : EasyBox-__init__.py.py 9 | @Description : 10 | """ 11 | 12 | if __name__ == '__main__': 13 | pass 14 | -------------------------------------------------------------------------------- /app/ui/components/sidebar/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/components/sidebar/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/components/sidebar/__pycache__/sidebar.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/components/sidebar/__pycache__/sidebar.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/components/sidebar/__pycache__/sidebar_ui.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/components/sidebar/__pycache__/sidebar_ui.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/doc_convert/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @Time : 2024/11/19 22:41 6 | @Author : SiYuan 7 | @Email : siyuan044@qq.com 8 | @File : EasyBox-__init__.py.py 9 | @Description : 10 | """ 11 | 12 | if __name__ == '__main__': 13 | pass 14 | -------------------------------------------------------------------------------- /app/ui/doc_convert/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/doc_convert/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/doc_convert/__pycache__/doc_convert.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/doc_convert/__pycache__/doc_convert.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/doc_convert/__pycache__/doc_convert_ui.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/doc_convert/__pycache__/doc_convert_ui.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/doc_convert/pdf2image/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @Time : 2024/11/26 0:43 6 | @Author : SiYuan 7 | @Email : siyuan044@qq.com 8 | @File : EasyBox-__init__.py.py 9 | @Description : 10 | """ 11 | 12 | if __name__ == '__main__': 13 | pass 14 | -------------------------------------------------------------------------------- /app/ui/doc_convert/pdf2image/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/doc_convert/pdf2image/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/doc_convert/pdf2image/__pycache__/pdf2image.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/doc_convert/pdf2image/__pycache__/pdf2image.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/doc_convert/pdf2image/__pycache__/pdf2image_ui.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/doc_convert/pdf2image/__pycache__/pdf2image_ui.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/doc_convert/pdf2wordui/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @Time : 2024/11/19 22:54 6 | @Author : SiYuan 7 | @Email : siyuan044@qq.com 8 | @File : EasyBox-__init__.py.py 9 | @Description : 10 | """ 11 | 12 | if __name__ == '__main__': 13 | pass 14 | -------------------------------------------------------------------------------- /app/ui/doc_convert/pdf2wordui/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/doc_convert/pdf2wordui/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/doc_convert/pdf2wordui/__pycache__/pdf2word.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/doc_convert/pdf2wordui/__pycache__/pdf2word.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/doc_convert/pdf2wordui/__pycache__/pdf2word_ui.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/doc_convert/pdf2wordui/__pycache__/pdf2word_ui.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/doc_convert/web2pdf/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @Time : 2024/11/30 16:45 6 | @Author : SiYuan 7 | @Email : siyuan044@qq.com 8 | @File : EasyBox-__init__.py.py 9 | @Description : 10 | """ 11 | 12 | if __name__ == '__main__': 13 | pass 14 | -------------------------------------------------------------------------------- /app/ui/doc_convert/web2pdf/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/doc_convert/web2pdf/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/doc_convert/web2pdf/__pycache__/web2pdf.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/doc_convert/web2pdf/__pycache__/web2pdf.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/doc_convert/web2pdf/__pycache__/web2pdf_ui.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/doc_convert/web2pdf/__pycache__/web2pdf_ui.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/global_signal.py: -------------------------------------------------------------------------------- 1 | from PySide6.QtCore import Signal, QThread, QObject 2 | 3 | 4 | class GlobalSignals(QObject): 5 | not_support = Signal(bool) 6 | information = Signal(str) # 警告弹窗信号 7 | 8 | 9 | globalSignals = GlobalSignals() 10 | -------------------------------------------------------------------------------- /app/ui/image_tools/__pycache__/image_tool.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/image_tools/__pycache__/image_tool.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/image_tools/__pycache__/image_tool_ui.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/image_tools/__pycache__/image_tool_ui.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/image_tools/image_tool.py: -------------------------------------------------------------------------------- 1 | from PySide6.QtCore import Signal, QThread, QSize, QFile, QIODevice, QTextStream 2 | from PySide6.QtWidgets import QWidget 3 | 4 | from app.ui.Icon import Icon 5 | from app.ui.components.QCursorGif import QCursorGif 6 | from app.ui.global_signal import globalSignals 7 | from app.ui.image_tools.image_tool_ui import Ui_Form 8 | from app.ui.image_tools.modify_date.modify_date import ModifyDateControl 9 | from app.ui.pdf_tools.merge import MergeControl 10 | 11 | from app.ui.components.router import Router 12 | 13 | 14 | class ImageToolControl(QWidget, Ui_Form, QCursorGif): 15 | DecryptSignal = Signal(str) 16 | get_wxidSignal = Signal(str) 17 | versionErrorSignal = Signal(str) 18 | childRouterSignal = Signal(str) 19 | 20 | def __init__(self, router: Router, parent=None): 21 | super().__init__(parent) 22 | self.router = router 23 | self.router_path = (self.parent().router_path if self.parent() else '') + '/图片工具箱' 24 | self.child_routes = {} 25 | self.merge_view = None 26 | self.running_flag = False 27 | self.setupUi(self) 28 | # 设置忙碌光标图片数组 29 | self.initCursor([':/icons/icons/Cursors/%d.png' % 30 | i for i in range(8)], self) 31 | self.setCursorTimeout(100) 32 | self.init_ui() 33 | 34 | def init_ui(self): 35 | if not self.parent(): 36 | pixmap = QPixmap(Icon.logo_ico_path) 37 | icon = QIcon(pixmap) 38 | self.setWindowIcon(icon) 39 | self.setWindowTitle('PDF工具箱') 40 | style_qss_file = QFile(":/data/resources/QSS/style.qss") 41 | if style_qss_file.open(QIODevice.ReadOnly | QIODevice.Text): 42 | stream = QTextStream(style_qss_file) 43 | style_content = stream.readAll() 44 | self.setStyleSheet(style_content) 45 | style_qss_file.close() 46 | self.commandLinkButton_modify_date.clicked.connect(self.modify_date) 47 | 48 | self.commandLinkButton_modify_name_by_time.clicked.connect(globalSignals.not_support) 49 | 50 | 51 | self.resize(QSize(640, 480)) 52 | 53 | def modify_date(self): 54 | if not self.merge_view: 55 | self.merge_view = ModifyDateControl(router=self.router, parent=self if self.parent() else None) 56 | self.merge_view.okSignal.connect(self.merge_finish) 57 | self.router.add_route(self.merge_view.router_path, self.merge_view) 58 | self.child_routes[self.merge_view.router_path] = 0 59 | self.childRouterSignal.emit(self.merge_view.router_path) 60 | self.router.navigate(self.merge_view.router_path) 61 | else: 62 | self.router.navigate(self.merge_view.router_path) 63 | 64 | def merge_finish(self): 65 | self.merge_view = None 66 | 67 | def __del__(self): 68 | self.merge_view = None 69 | 70 | 71 | if __name__ == '__main__': 72 | from PySide6.QtWidgets import QWidget, QApplication 73 | import sys 74 | from PySide6.QtGui import QFont, QPixmap, QIcon 75 | from PySide6.QtCore import Qt 76 | 77 | QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) 78 | QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) 79 | app = QApplication(sys.argv) 80 | font = QFont('微软雅黑', 10) # 使用 Times New Roman 字体,字体大小为 14 81 | app.setFont(font) 82 | router = Router(None) 83 | view = ImageToolControl(router) 84 | view.show() 85 | sys.exit(app.exec_()) 86 | -------------------------------------------------------------------------------- /app/ui/image_tools/modify_date/__pycache__/modify_date.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/image_tools/modify_date/__pycache__/modify_date.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/image_tools/modify_date/__pycache__/modify_date_ui.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/image_tools/modify_date/__pycache__/modify_date_ui.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/mainview.py: -------------------------------------------------------------------------------- 1 | from PySide6 import QtWidgets, QtGui 2 | 3 | from app.ui import mainwindow 4 | from PySide6.QtCore import Signal, QFile, QIODevice, QTextStream, QSize, Qt 5 | from PySide6.QtGui import QPixmap, QIcon 6 | from PySide6.QtWidgets import QMainWindow, QListWidgetItem, QLabel, QPushButton, QSizePolicy, QSplitter, QMessageBox 7 | 8 | from app.ui.Icon import Icon 9 | from app.ui.components.QCursorGif import QCursorGif 10 | from app.ui.components.router import Router 11 | from app.ui.components import Sidebar, SidebarButton 12 | from app.ui.doc_convert.doc_convert import DocConvertControl 13 | from app.ui.global_signal import globalSignals 14 | from app.ui.image_tools.image_tool import ImageToolControl 15 | from app.ui.memotrace_enhance.enhance import EnhanceControl 16 | from app.ui.pdf_tools.pdf_tool import PDFToolControl 17 | from app.ui.setting.setting import SettingWindow 18 | from app.ui.video_tools.video_tool import VideoToolControl 19 | 20 | 21 | class MainWinController(QMainWindow, mainwindow.Ui_MainWindow, QCursorGif): 22 | exitSignal = Signal(bool) 23 | okSignal = Signal(bool) 24 | childRouterSignal = Signal(str) 25 | 26 | # username = '' 27 | def __init__(self, parent=None): 28 | super(MainWinController, self).__init__(parent) 29 | self.setupUi(self) 30 | self.router_path = '' 31 | self.init_ui() 32 | # 设置无边框 33 | # self.setWindowFlag(Qt.FramelessWindowHint) 34 | globalSignals.not_support.connect(self.show_not_support) 35 | globalSignals.information.connect(self.show_information) 36 | 37 | def init_ui(self): 38 | self.initCursor([':/icons/icons/Cursors/%d.png' % 39 | i for i in range(8)], self) 40 | self.setCursorTimeout(100) 41 | pixmap = QPixmap(Icon.logo_ico_path) 42 | icon = QIcon(pixmap) 43 | self.setWindowIcon(icon) 44 | 45 | style_qss_file = QFile(":/data/resources/QSS/style.qss") 46 | if style_qss_file.open(QIODevice.ReadOnly | QIODevice.Text): 47 | stream = QTextStream(style_qss_file) 48 | style_content = stream.readAll() 49 | self.setStyleSheet(style_content) 50 | style_qss_file.close() 51 | 52 | self.stackedWidget = QtWidgets.QStackedWidget(self) 53 | self.stackedWidget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) # C尽可能挤压B 54 | 55 | self.router = Router(self.stackedWidget) 56 | self.sidebar = Sidebar(self.stackedWidget, parent=None) 57 | self.sidebar.btn_setting.clicked.connect(self.show_setting) 58 | self.sidebar.btn_back.setText('') 59 | self.sidebar.btn_setting.setIcon(Icon.Setting_Icon) 60 | self.sidebar.btn_back.setIcon(Icon.Back) 61 | self.sidebar.btn_back.clicked.connect(self.router.turn_back) 62 | self.router.history_changed.connect(self.sidebar.set_turn_back_enable) 63 | self.horizontalLayout.addWidget(self.sidebar) 64 | self.horizontalLayout.addWidget(self.stackedWidget) 65 | self.horizontalLayout.setContentsMargins(0,0,0,0) 66 | self.setting_view = SettingWindow() 67 | 68 | pdf_view = PDFToolControl(self.router, parent=self) 69 | self.add_widget(Icon.PDF_Icon, 'PDF工具', pdf_view.router_path, pdf_view) 70 | 71 | # l1 = QLabel('文档转换', self) 72 | doc_view = DocConvertControl(self.router, parent=self) 73 | self.add_widget(Icon.Doc_Transfer_Icon, '文档转换', '/文档转换', doc_view) 74 | 75 | image_view = ImageToolControl(self.router, parent=self) 76 | self.add_widget(Icon.Img_Icon, '图片工具', '/图片工具', image_view) 77 | 78 | # Screen_record_view = ScreenRecordControl(self.router, parent=self) 79 | Video_view = VideoToolControl(self.router, parent=self) 80 | self.add_widget(Icon.Video_Icon, '视频工具', '/视频工具', Video_view) 81 | 82 | l4 = QLabel('批量操作', self) 83 | self.add_widget(Icon.Batch_Icon, '批量操作', '/批量操作', l4) 84 | 85 | enhance_view = EnhanceControl(self.router, parent=self) 86 | self.add_widget(Icon.Tool_Icon, '留痕增强', '/留痕增强', enhance_view) 87 | 88 | # 连接信号槽:切换选中按钮样式 89 | self.router.route_changed.connect(self.sidebar.update_sidebar_selection) 90 | self.router.navigate(pdf_view.router_path) # 初始页面 91 | 92 | def add_widget(self, icon, text, router_path, widget): 93 | # """ 创建侧边栏按钮并连接路径导航 """ 94 | 95 | self.sidebar.add_nav_button(icon, text, router_path, action=lambda: self.router.navigate(router_path)) 96 | index = self.router.add_route(router_path, widget) 97 | 98 | def show_setting(self): 99 | self.setting_view.show() 100 | 101 | def show_not_support(self, a): 102 | self.show_information('暂不支持该功能!') 103 | 104 | def show_information(self, msg): 105 | QMessageBox.information(self, '温馨提示', msg) 106 | -------------------------------------------------------------------------------- /app/ui/mainwindow.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ################################################################################ 4 | ## Form generated from reading UI file 'mainwindow.ui' 5 | ## 6 | ## Created by: Qt User Interface Compiler version 6.8.0 7 | ## 8 | ## WARNING! All changes made in this file will be lost when recompiling UI file! 9 | ################################################################################ 10 | 11 | from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, 12 | QMetaObject, QObject, QPoint, QRect, 13 | QSize, QTime, QUrl, Qt) 14 | from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, 15 | QFont, QFontDatabase, QGradient, QIcon, 16 | QImage, QKeySequence, QLinearGradient, QPainter, 17 | QPalette, QPixmap, QRadialGradient, QTransform) 18 | from PySide6.QtWidgets import (QApplication, QHBoxLayout, QMainWindow, QSizePolicy, 19 | QWidget) 20 | import resource_rc 21 | 22 | class Ui_MainWindow(object): 23 | def setupUi(self, MainWindow): 24 | if not MainWindow.objectName(): 25 | MainWindow.setObjectName(u"MainWindow") 26 | MainWindow.resize(717, 565) 27 | font = QFont() 28 | font.setFamilies([u"\u5fae\u8f6f\u96c5\u9ed1"]) 29 | font.setPointSize(12) 30 | font.setBold(False) 31 | MainWindow.setFont(font) 32 | MainWindow.setMouseTracking(True) 33 | icon = QIcon() 34 | icon.addFile(u":/icons/resources/icons/logo.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off) 35 | MainWindow.setWindowIcon(icon) 36 | MainWindow.setAutoFillBackground(True) 37 | MainWindow.setStyleSheet(u"") 38 | MainWindow.setIconSize(QSize(50, 24)) 39 | MainWindow.setDockOptions(QMainWindow.DockOption.AllowTabbedDocks|QMainWindow.DockOption.AnimatedDocks) 40 | self.centralwidget = QWidget(MainWindow) 41 | self.centralwidget.setObjectName(u"centralwidget") 42 | self.centralwidget.setMouseTracking(True) 43 | self.horizontalLayout = QHBoxLayout(self.centralwidget) 44 | self.horizontalLayout.setSpacing(0) 45 | self.horizontalLayout.setObjectName(u"horizontalLayout") 46 | self.horizontalLayout.setContentsMargins(9, 0, 9, 0) 47 | MainWindow.setCentralWidget(self.centralwidget) 48 | 49 | self.retranslateUi(MainWindow) 50 | 51 | QMetaObject.connectSlotsByName(MainWindow) 52 | # setupUi 53 | 54 | def retranslateUi(self, MainWindow): 55 | MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"EasyBox", None)) 56 | #if QT_CONFIG(accessibility) 57 | MainWindow.setAccessibleName(QCoreApplication.translate("MainWindow", u"1423534", None)) 58 | #endif // QT_CONFIG(accessibility) 59 | #if QT_CONFIG(accessibility) 60 | MainWindow.setAccessibleDescription(QCoreApplication.translate("MainWindow", u"142345", None)) 61 | #endif // QT_CONFIG(accessibility) 62 | # retranslateUi 63 | 64 | -------------------------------------------------------------------------------- /app/ui/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 717 10 | 565 11 | 12 | 13 | 14 | 15 | 微软雅黑 16 | 12 17 | false 18 | 19 | 20 | 21 | true 22 | 23 | 24 | EasyBox 25 | 26 | 27 | 28 | :/icons/resources/icons/logo.png:/icons/resources/icons/logo.png 29 | 30 | 31 | 1423534 32 | 33 | 34 | 142345 35 | 36 | 37 | true 38 | 39 | 40 | 41 | 42 | 43 | 44 | 50 45 | 24 46 | 47 | 48 | 49 | QMainWindow::DockOption::AllowTabbedDocks|QMainWindow::DockOption::AnimatedDocks 50 | 51 | 52 | 53 | true 54 | 55 | 56 | 57 | 0 58 | 59 | 60 | 9 61 | 62 | 63 | 0 64 | 65 | 66 | 9 67 | 68 | 69 | 0 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /app/ui/memotrace_enhance/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @Time : 2024/11/20 21:01 6 | @Author : SiYuan 7 | @Email : siyuan044@qq.com 8 | @File : EasyBox-__init__.py.py 9 | @Description : 10 | """ 11 | 12 | if __name__ == '__main__': 13 | pass 14 | -------------------------------------------------------------------------------- /app/ui/memotrace_enhance/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/memotrace_enhance/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/memotrace_enhance/__pycache__/enhance.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/memotrace_enhance/__pycache__/enhance.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/memotrace_enhance/__pycache__/enhance_ui.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/memotrace_enhance/__pycache__/enhance_ui.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/memotrace_enhance/enhance.py: -------------------------------------------------------------------------------- 1 | from PySide6.QtCore import Signal, QThread, QSize, QFile, QIODevice, QTextStream 2 | from PySide6.QtWidgets import QWidget 3 | 4 | from app.ui.Icon import Icon 5 | from app.ui.components.QCursorGif import QCursorGif 6 | from app.ui.doc_convert.pdf2wordui.pdf2word import Pdf2WordControl 7 | from app.ui.memotrace_enhance.enhance_ui import Ui_Form 8 | from app.ui.components.router import Router 9 | from app.ui.memotrace_enhance.toc.toc import TocControl 10 | 11 | 12 | class EnhanceControl(QWidget, Ui_Form, QCursorGif): 13 | DecryptSignal = Signal(str) 14 | get_wxidSignal = Signal(str) 15 | versionErrorSignal = Signal(str) 16 | childRouterSignal = Signal(str) 17 | 18 | def __init__(self, router: Router, parent=None): 19 | super().__init__(parent) 20 | self.router = router 21 | self.router_path = (self.parent().router_path if self.parent() else '') + '/留痕增强' 22 | self.child_routes = {} 23 | self.merge_view = None 24 | self.make_toc_view = None 25 | self.running_flag = False 26 | self.setupUi(self) 27 | # 设置忙碌光标图片数组 28 | self.initCursor([':/icons/icons/Cursors/%d.png' % 29 | i for i in range(8)], self) 30 | self.setCursorTimeout(100) 31 | self.init_ui() 32 | 33 | def init_ui(self): 34 | if not self.parent(): 35 | pixmap = QPixmap(Icon.logo_ico_path) 36 | icon = QIcon(pixmap) 37 | self.setWindowIcon(icon) 38 | self.setWindowTitle('留痕增强') 39 | style_qss_file = QFile(":/data/resources/QSS/style.qss") 40 | if style_qss_file.open(QIODevice.ReadOnly | QIODevice.Text): 41 | stream = QTextStream(style_qss_file) 42 | style_content = stream.readAll() 43 | self.setStyleSheet(style_content) 44 | style_qss_file.close() 45 | self.commandLinkButton_pdf2word.clicked.connect(self.pdf2word) 46 | self.commandLinkButton_toc.clicked.connect(self.make_toc) 47 | self.resize(QSize(640, 480)) 48 | 49 | def pdf2word(self): 50 | if not self.merge_view: 51 | self.merge_view = Pdf2WordControl(router=self.router, parent=self if self.parent() else None) 52 | self.merge_view.okSignal.connect(self.merge_finish) 53 | self.router.add_route(self.merge_view.router_path, self.merge_view) 54 | self.child_routes[self.merge_view.router_path] = 0 55 | self.childRouterSignal.emit(self.merge_view.router_path) 56 | self.router.navigate(self.merge_view.router_path) 57 | else: 58 | self.router.navigate(self.merge_view.router_path) 59 | 60 | def make_toc(self): 61 | if not self.make_toc_view: 62 | self.make_toc_view = TocControl(router=self.router, parent=self if self.parent() else None) 63 | self.make_toc_view.okSignal.connect(self.make_toc_finish) 64 | self.router.add_route(self.make_toc_view.router_path, self.make_toc_view) 65 | self.child_routes[self.make_toc_view.router_path] = 0 66 | self.childRouterSignal.emit(self.make_toc_view.router_path) 67 | self.router.navigate(self.make_toc_view.router_path) 68 | else: 69 | self.router.navigate(self.make_toc_view.router_path) 70 | 71 | def make_toc_finish(self): 72 | self.make_toc_view = None 73 | 74 | def merge_finish(self): 75 | self.merge_view = None 76 | 77 | def __del__(self): 78 | self.merge_view = None 79 | 80 | 81 | if __name__ == '__main__': 82 | from PySide6.QtWidgets import QWidget, QApplication 83 | import sys 84 | from PySide6.QtGui import QFont, QPixmap, QIcon 85 | from PySide6.QtCore import Qt 86 | 87 | QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) 88 | QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) 89 | app = QApplication(sys.argv) 90 | font = QFont('微软雅黑', 10) # 使用 Times New Roman 字体,字体大小为 14 91 | app.setFont(font) 92 | router = Router(None) 93 | view = EnhanceControl(router) 94 | view.show() 95 | sys.exit(app.exec_()) 96 | -------------------------------------------------------------------------------- /app/ui/memotrace_enhance/toc/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @Time : 2024/11/19 22:54 6 | @Author : SiYuan 7 | @Email : siyuan044@qq.com 8 | @File : EasyBox-__init__.py.py 9 | @Description : 10 | """ 11 | 12 | if __name__ == '__main__': 13 | pass 14 | -------------------------------------------------------------------------------- /app/ui/memotrace_enhance/toc/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/memotrace_enhance/toc/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/memotrace_enhance/toc/__pycache__/toc.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/memotrace_enhance/toc/__pycache__/toc.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/memotrace_enhance/toc/__pycache__/toc_ui.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/memotrace_enhance/toc/__pycache__/toc_ui.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/pdf_tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/pdf_tools/__init__.py -------------------------------------------------------------------------------- /app/ui/pdf_tools/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/pdf_tools/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/pdf_tools/__pycache__/pdf_tool.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/pdf_tools/__pycache__/pdf_tool.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/pdf_tools/__pycache__/pdf_tool_ui.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/pdf_tools/__pycache__/pdf_tool_ui.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/pdf_tools/conversion/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @Time : 2024/11/10 15:39 6 | @Author : SiYuan 7 | @Email : siyuan044@qq.com 8 | @File : EasyBox-__init__.py.py 9 | @Description : 10 | """ 11 | 12 | if __name__ == '__main__': 13 | pass 14 | -------------------------------------------------------------------------------- /app/ui/pdf_tools/merge/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @Time : 2024/11/10 15:39 6 | @Author : SiYuan 7 | @Email : siyuan044@qq.com 8 | @File : EasyBox-__init__.py.py 9 | @Description : 10 | """ 11 | from .merge import MergeControl 12 | 13 | 14 | if __name__ == '__main__': 15 | pass 16 | -------------------------------------------------------------------------------- /app/ui/pdf_tools/merge/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/pdf_tools/merge/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/pdf_tools/merge/__pycache__/encrypt_dialog.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/pdf_tools/merge/__pycache__/encrypt_dialog.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/pdf_tools/merge/__pycache__/encrypt_dialog_ui.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/pdf_tools/merge/__pycache__/encrypt_dialog_ui.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/pdf_tools/merge/__pycache__/merge.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/pdf_tools/merge/__pycache__/merge.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/pdf_tools/merge/__pycache__/merge_ui.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/pdf_tools/merge/__pycache__/merge_ui.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/pdf_tools/merge/encrypt_dialog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @Time : 2024/11/10 21:13 6 | @Author : SiYuan 7 | @Email : siyuan044@qq.com 8 | @File : EasyBox-encrypt_dialog.py 9 | @Description : 10 | """ 11 | from PySide6.QtCore import Signal 12 | from PySide6.QtWidgets import QWidget, QDialog, QMessageBox 13 | from pymupdf import mupdf 14 | 15 | from app.ui.pdf_tools.merge.encrypt_dialog_ui import Ui_Dialog 16 | 17 | 18 | class EncryptControl(QDialog, Ui_Dialog): 19 | okSignal = Signal(bool) 20 | childRouterSignal = Signal(str) 21 | 22 | def __init__(self, parent=None): 23 | super().__init__(parent) 24 | self.setupUi(self) 25 | self.setWindowTitle('填写加密选项') 26 | self.buttonBox.setObjectName('border') 27 | 28 | def accept(self): 29 | owner_pw1 = self.lineEdit_owner_pw1.text() 30 | owner_pw2 = self.lineEdit_owner_pw2.text() 31 | if not owner_pw1 or not owner_pw2: 32 | QMessageBox.warning(self, '警告', '请输入编辑密码') 33 | return 34 | if owner_pw1 != owner_pw2: 35 | QMessageBox.warning(self, '警告', '两次管理员密码输入不一致') 36 | return 37 | user_pw1 = self.lineEdit_user_pw1.text() 38 | user_pw2 = self.lineEdit_user_pw2.text() 39 | if user_pw1 != user_pw2: 40 | QMessageBox.warning(self, '警告', '两次用户密码输入不一致') 41 | return 42 | super().accept() 43 | 44 | def get_data(self): 45 | print('123') 46 | owner_pw1 = self.lineEdit_owner_pw1.text() 47 | owner_pw2 = self.lineEdit_owner_pw2.text() 48 | user_pw1 = self.lineEdit_user_pw1.text() 49 | user_pw2 = self.lineEdit_user_pw2.text() 50 | permissions = 0 51 | if self.checkBox_edit.isChecked(): 52 | permissions |= mupdf.PDF_PERM_MODIFY 53 | if self.checkBox_copy.isChecked(): 54 | permissions |= mupdf.PDF_PERM_COPY 55 | if self.checkBox_annotate.isChecked(): 56 | permissions |= mupdf.PDF_PERM_ANNOTATE 57 | if self.radioButton_print.isChecked(): 58 | permissions |= mupdf.PDF_PERM_PRINT | mupdf.PDF_PERM_PRINT_HQ 59 | elif self.radioButton_print_low.isChecked(): 60 | permissions |= mupdf.PDF_PERM_PRINT 61 | else: 62 | if permissions == 0: 63 | permissions = 0 64 | return { 65 | "encryption": mupdf.PDF_ENCRYPT_AES_256, 66 | 'owner_pw': owner_pw1, 67 | 'user_pw': user_pw1, 68 | "permissions": permissions 69 | } 70 | 71 | 72 | if __name__ == '__main__': 73 | pass 74 | -------------------------------------------------------------------------------- /app/ui/pdf_tools/security/__init__.py: -------------------------------------------------------------------------------- 1 | # PDF加密解密功能模块 2 | """ 3 | 包含PDF加密和解密相关功能 4 | """ -------------------------------------------------------------------------------- /app/ui/pdf_tools/security/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/pdf_tools/security/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/pdf_tools/security/__pycache__/decrypt.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/pdf_tools/security/__pycache__/decrypt.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/pdf_tools/security/__pycache__/decrypt_ui.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/pdf_tools/security/__pycache__/decrypt_ui.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/pdf_tools/security/__pycache__/encrypt.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/pdf_tools/security/__pycache__/encrypt.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/pdf_tools/security/__pycache__/encrypt_ui.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/pdf_tools/security/__pycache__/encrypt_ui.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/pdf_tools/split/__init__.py: -------------------------------------------------------------------------------- 1 | # PDF拆分功能模块 2 | """ 3 | PDF拆分相关功能 4 | """ -------------------------------------------------------------------------------- /app/ui/pdf_tools/split/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/pdf_tools/split/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/pdf_tools/split/__pycache__/split.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/pdf_tools/split/__pycache__/split.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/pdf_tools/split/__pycache__/split_ui.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/pdf_tools/split/__pycache__/split_ui.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/screen_record/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/ui/screen_record/screen_record_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ################################################################################ 4 | ## Form generated from reading UI file 'screen_record_ui.ui' 5 | ## 6 | ## Created by: Qt User Interface Compiler version 6.8.0 7 | ## 8 | ## WARNING! All changes made in this file will be lost when recompiling UI file! 9 | ################################################################################ 10 | 11 | from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, 12 | QMetaObject, QObject, QPoint, QRect, 13 | QSize, QTime, QUrl, Qt) 14 | from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, 15 | QFont, QFontDatabase, QGradient, QIcon, 16 | QImage, QKeySequence, QLinearGradient, QPainter, 17 | QPalette, QPixmap, QRadialGradient, QTransform) 18 | from PySide6.QtWidgets import (QApplication, QComboBox, QGridLayout, QLabel, 19 | QLineEdit, QPushButton, QSizePolicy, QSpinBox, 20 | QWidget) 21 | 22 | class Ui_Form(object): 23 | def setupUi(self, Form): 24 | if not Form.objectName(): 25 | Form.setObjectName(u"Form") 26 | Form.resize(448, 300) 27 | self.widget = QWidget(Form) 28 | self.widget.setObjectName(u"widget") 29 | self.widget.setGeometry(QRect(30, 40, 355, 124)) 30 | self.gridLayout = QGridLayout(self.widget) 31 | self.gridLayout.setObjectName(u"gridLayout") 32 | self.gridLayout.setContentsMargins(0, 0, 0, 0) 33 | self.format_label = QLabel(self.widget) 34 | self.format_label.setObjectName(u"format_label") 35 | 36 | self.gridLayout.addWidget(self.format_label, 0, 0, 1, 1) 37 | 38 | self.format_combo = QComboBox(self.widget) 39 | self.format_combo.addItem("") 40 | self.format_combo.addItem("") 41 | self.format_combo.setObjectName(u"format_combo") 42 | 43 | self.gridLayout.addWidget(self.format_combo, 0, 1, 1, 3) 44 | 45 | self.fps_label = QLabel(self.widget) 46 | self.fps_label.setObjectName(u"fps_label") 47 | 48 | self.gridLayout.addWidget(self.fps_label, 1, 0, 1, 1) 49 | 50 | self.fps_spin = QSpinBox(self.widget) 51 | self.fps_spin.setObjectName(u"fps_spin") 52 | 53 | self.gridLayout.addWidget(self.fps_spin, 1, 1, 1, 3) 54 | 55 | self.path_label = QLabel(self.widget) 56 | self.path_label.setObjectName(u"path_label") 57 | 58 | self.gridLayout.addWidget(self.path_label, 2, 0, 1, 1) 59 | 60 | self.path_edit = QLineEdit(self.widget) 61 | self.path_edit.setObjectName(u"path_edit") 62 | 63 | self.gridLayout.addWidget(self.path_edit, 2, 1, 1, 2) 64 | 65 | self.browse_button = QPushButton(self.widget) 66 | self.browse_button.setObjectName(u"browse_button") 67 | 68 | self.gridLayout.addWidget(self.browse_button, 2, 3, 1, 1) 69 | 70 | self.start_button = QPushButton(self.widget) 71 | self.start_button.setObjectName(u"start_button") 72 | 73 | self.gridLayout.addWidget(self.start_button, 3, 0, 1, 2) 74 | 75 | self.stop_button = QPushButton(self.widget) 76 | self.stop_button.setObjectName(u"stop_button") 77 | 78 | self.gridLayout.addWidget(self.stop_button, 3, 2, 1, 2) 79 | 80 | 81 | self.retranslateUi(Form) 82 | 83 | QMetaObject.connectSlotsByName(Form) 84 | # setupUi 85 | 86 | def retranslateUi(self, Form): 87 | Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None)) 88 | self.format_label.setText(QCoreApplication.translate("Form", u"\u5f55\u5236\u683c\u5f0f\uff1a", None)) 89 | self.format_combo.setItemText(0, QCoreApplication.translate("Form", u"mp4", None)) 90 | self.format_combo.setItemText(1, QCoreApplication.translate("Form", u"avi", None)) 91 | 92 | self.fps_label.setText(QCoreApplication.translate("Form", u"\u5e27\u7387\uff1a", None)) 93 | self.path_label.setText(QCoreApplication.translate("Form", u"\u4fdd\u5b58\u8def\u5f84\uff1a", None)) 94 | self.browse_button.setText(QCoreApplication.translate("Form", u"\u9009\u62e9\u8def\u5f84", None)) 95 | self.start_button.setText(QCoreApplication.translate("Form", u"\u5f00\u59cb\u5f55\u5236", None)) 96 | self.stop_button.setText(QCoreApplication.translate("Form", u"\u7ed3\u675f\u5f55\u5236", None)) 97 | # retranslateUi 98 | 99 | -------------------------------------------------------------------------------- /app/ui/screen_record/screen_record_ui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 448 10 | 300 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 30 20 | 40 21 | 355 22 | 124 23 | 24 | 25 | 26 | 27 | 28 | 29 | 录制格式: 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | mp4 38 | 39 | 40 | 41 | 42 | avi 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 帧率: 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 保存路径: 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 选择路径 71 | 72 | 73 | 74 | 75 | 76 | 77 | 开始录制 78 | 79 | 80 | 81 | 82 | 83 | 84 | 结束录制 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /app/ui/setting/__pycache__/about_dialog.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/setting/__pycache__/about_dialog.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/setting/__pycache__/seetingUi.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/setting/__pycache__/seetingUi.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/setting/__pycache__/setting.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/setting/__pycache__/setting.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/setting/seetingUi.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ################################################################################ 4 | ## Form generated from reading UI file 'seetingUi.ui' 5 | ## 6 | ## Created by: Qt User Interface Compiler version 6.8.0 7 | ## 8 | ## WARNING! All changes made in this file will be lost when recompiling UI file! 9 | ################################################################################ 10 | 11 | from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, 12 | QMetaObject, QObject, QPoint, QRect, 13 | QSize, QTime, QUrl, Qt) 14 | from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, 15 | QFont, QFontDatabase, QGradient, QIcon, 16 | QImage, QKeySequence, QLinearGradient, QPainter, 17 | QPalette, QPixmap, QRadialGradient, QTransform) 18 | from PySide6.QtWidgets import (QApplication, QFrame, QHBoxLayout, QListWidget, 19 | QListWidgetItem, QSizePolicy, QStackedWidget, QWidget) 20 | 21 | class Ui_Form(object): 22 | def setupUi(self, Form): 23 | if not Form.objectName(): 24 | Form.setObjectName(u"Form") 25 | Form.resize(640, 480) 26 | self.horizontalLayout = QHBoxLayout(Form) 27 | self.horizontalLayout.setObjectName(u"horizontalLayout") 28 | self.listWidget = QListWidget(Form) 29 | self.listWidget.setObjectName(u"listWidget") 30 | self.listWidget.setMinimumSize(QSize(120, 0)) 31 | self.listWidget.setMaximumSize(QSize(120, 16777215)) 32 | self.listWidget.setFrameShape(QFrame.NoFrame) 33 | 34 | self.horizontalLayout.addWidget(self.listWidget) 35 | 36 | self.line = QFrame(Form) 37 | self.line.setObjectName(u"line") 38 | self.line.setStyleSheet(u"") 39 | self.line.setFrameShadow(QFrame.Raised) 40 | self.line.setLineWidth(5) 41 | self.line.setFrameShape(QFrame.Shape.VLine) 42 | 43 | self.horizontalLayout.addWidget(self.line) 44 | 45 | self.stackedWidget = QStackedWidget(Form) 46 | self.stackedWidget.setObjectName(u"stackedWidget") 47 | 48 | self.horizontalLayout.addWidget(self.stackedWidget) 49 | 50 | 51 | self.retranslateUi(Form) 52 | 53 | QMetaObject.connectSlotsByName(Form) 54 | # setupUi 55 | 56 | def retranslateUi(self, Form): 57 | Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None)) 58 | # retranslateUi 59 | 60 | -------------------------------------------------------------------------------- /app/ui/setting/seetingUi.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 640 10 | 480 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | 21 | 120 22 | 0 23 | 24 | 25 | 26 | 27 | 120 28 | 16777215 29 | 30 | 31 | 32 | QFrame::NoFrame 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | QFrame::Raised 43 | 44 | 45 | 5 46 | 47 | 48 | Qt::Vertical 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /app/ui/setting/setting.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import sys 3 | 4 | from PySide6.QtCore import Qt, Signal, QThread, QUrl, QFile, QIODevice, QTextStream, QStringConverter 5 | from PySide6.QtGui import QFont, QDesktopServices, QPixmap, QIcon 6 | from PySide6.QtWidgets import QWidget, QMessageBox, QApplication, QListWidgetItem, QLabel 7 | 8 | from app.ui.Icon import Icon 9 | from app.ui.global_signal import globalSignals 10 | from app.ui.setting.about_dialog import AboutDialog 11 | from app.ui.setting.seetingUi import Ui_Form 12 | 13 | Stylesheet = """ 14 | """ 15 | 16 | 17 | class SettingWindow(QWidget, Ui_Form): 18 | load_finish_signal = Signal(bool) 19 | 20 | def __init__(self, parent=None): 21 | super().__init__(parent) 22 | self.setupUi(self) 23 | self.init_ui() 24 | # self.setStyleSheet(Stylesheet) 25 | 26 | def init_ui(self): 27 | pixmap = QPixmap(Icon.logo_ico_path) 28 | icon = QIcon(pixmap) 29 | self.setWindowIcon(icon) 30 | self.setWindowTitle('设置') 31 | self.listWidget.currentRowChanged.connect(self.setCurrentIndex) 32 | account_item = QListWidgetItem('PDF设置', self.listWidget) 33 | tool_item = QListWidgetItem('图片设置', self.listWidget) 34 | chat_item = QListWidgetItem('文件管理', self.listWidget) 35 | myinfo_item = QListWidgetItem('关于', self.listWidget) 36 | self.account_setting_window = QLabel('敬请期待', self) 37 | self.report_setting_window = QLabel('敬请期待', self) 38 | self.file_setting_window = QLabel('敬请期待', self) 39 | self.about_window = AboutDialog(main_window=self, parent=self) 40 | self.about_window.buttonBox.setVisible(False) 41 | self.listWidget.setCurrentRow(0) 42 | self.stackedWidget.addWidget(self.account_setting_window) 43 | self.stackedWidget.addWidget(self.report_setting_window) 44 | self.stackedWidget.addWidget(self.file_setting_window) 45 | self.stackedWidget.addWidget(self.about_window) 46 | style_qss_file = QFile(":/data/resources/QSS/style.qss") 47 | if style_qss_file.open(QIODevice.ReadOnly | QIODevice.Text): 48 | stream = QTextStream(style_qss_file) 49 | stream.setEncoding(QStringConverter.Encoding.System) 50 | style_content = stream.readAll() 51 | self.setStyleSheet(style_content) 52 | style_qss_file.close() 53 | 54 | def setCurrentIndex(self, row): 55 | self.stackedWidget.setCurrentIndex(row) 56 | 57 | def logout(self, flag): 58 | if flag: 59 | globalSignals.logout.emit(True) 60 | self.close() 61 | 62 | def reload(self): 63 | self.account_setting_window.init_ui() 64 | 65 | 66 | if __name__ == '__main__': 67 | app = QApplication(sys.argv) 68 | font = QFont('微软雅黑', 12) # 使用 Times New Roman 字体,字体大小为 14 69 | app.setFont(font) 70 | view = SettingWindow() 71 | view.show() 72 | sys.exit(app.exec()) 73 | -------------------------------------------------------------------------------- /app/ui/video_tools/__pycache__/video_tool.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/video_tools/__pycache__/video_tool.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/video_tools/__pycache__/video_tool_ui.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/ui/video_tools/__pycache__/video_tool_ui.cpython-313.pyc -------------------------------------------------------------------------------- /app/ui/video_tools/video_tool.py: -------------------------------------------------------------------------------- 1 | from PySide6.QtCore import Signal, QThread, QSize, QFile, QIODevice, QTextStream 2 | from PySide6.QtWidgets import QWidget 3 | 4 | from app.ui.Icon import Icon 5 | from app.ui.components.QCursorGif import QCursorGif 6 | from app.ui.global_signal import globalSignals 7 | from app.ui.video_tools.video_tool_ui import Ui_Form 8 | from app.ui.components.router import Router 9 | 10 | 11 | class VideoToolControl(QWidget, Ui_Form, QCursorGif): 12 | DecryptSignal = Signal(str) 13 | get_wxidSignal = Signal(str) 14 | versionErrorSignal = Signal(str) 15 | childRouterSignal = Signal(str) 16 | 17 | def __init__(self, router: Router, parent=None): 18 | super(VideoToolControl, self).__init__(parent) 19 | self.pdf2image_view = None 20 | self.router = router 21 | self.router_path = (self.parent().router_path if self.parent() else '') + '/视频工具' 22 | self.child_routes = {} 23 | self.merge_view = None 24 | self.running_flag = False 25 | self.setupUi(self) 26 | # 设置忙碌光标图片数组 27 | self.initCursor([':/icons/icons/Cursors/%d.png' % 28 | i for i in range(8)], self) 29 | self.setCursorTimeout(100) 30 | self.init_ui() 31 | 32 | def init_ui(self): 33 | if not self.parent(): 34 | pixmap = QPixmap(Icon.logo_ico_path) 35 | icon = QIcon(pixmap) 36 | self.setWindowIcon(icon) 37 | self.setWindowTitle('视频工具') 38 | style_qss_file = QFile(":/data/resources/QSS/style.qss") 39 | if style_qss_file.open(QIODevice.ReadOnly | QIODevice.Text): 40 | stream = QTextStream(style_qss_file) 41 | style_content = stream.readAll() 42 | self.setStyleSheet(style_content) 43 | style_qss_file.close() 44 | 45 | self.commandLinkButton_screenshoot.clicked.connect(globalSignals.not_support) 46 | self.commandLinkButton_convert.clicked.connect(globalSignals.not_support) 47 | 48 | self.resize(QSize(640, 480)) 49 | 50 | def __del__(self): 51 | self.merge_view = None 52 | 53 | 54 | if __name__ == '__main__': 55 | from PySide6.QtWidgets import QWidget, QApplication 56 | import sys 57 | from PySide6.QtGui import QFont, QPixmap, QIcon 58 | from PySide6.QtCore import Qt 59 | 60 | QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) 61 | QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) 62 | app = QApplication(sys.argv) 63 | font = QFont('微软雅黑', 10) # 使用 Times New Roman 字体,字体大小为 14 64 | app.setFont(font) 65 | router = Router(None) 66 | view = VideoToolControl(router) 67 | view.show() 68 | sys.exit(app.exec_()) 69 | -------------------------------------------------------------------------------- /app/util/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @Time : 2024/11/10 15:05 6 | @Author : SiYuan 7 | @Email : siyuan044@qq.com 8 | @File : EasyBox-__init__.py.py 9 | @Description : 10 | """ 11 | from . import common 12 | 13 | if __name__ == '__main__': 14 | pass 15 | -------------------------------------------------------------------------------- /app/util/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/util/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /app/util/__pycache__/common.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/app/util/__pycache__/common.cpython-313.pyc -------------------------------------------------------------------------------- /doc/images/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/doc/images/img.png -------------------------------------------------------------------------------- /doc/images/img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/doc/images/img_1.png -------------------------------------------------------------------------------- /doc/images/img_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/doc/images/img_2.png -------------------------------------------------------------------------------- /doc/images/img_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/doc/images/img_3.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import sys 3 | import traceback 4 | from app.config import version 5 | from app.log.exception_handling import ExceptionHanding 6 | from app.ui.Icon import Icon 7 | from multiprocessing import freeze_support 8 | 9 | widget = None 10 | 11 | 12 | def excepthook(exc_type, exc_value, traceback_): 13 | # 将异常信息转为字符串 14 | # 在这里处理全局异常 15 | error_message = ExceptionHanding(exc_type, exc_value, traceback_) 16 | txt = '' 17 | msg = f"Exception Type: {exc_type.__name__}\nException Value: {exc_value}\ndetails: {error_message}\n\n{txt}" 18 | 19 | logger.error(f'程序发生了错误:\n\n{msg}') 20 | # 创建一个 QMessageBox 对象 21 | error_box = QMessageBox() 22 | # 设置对话框的标题 23 | error_box.setWindowTitle("未知错误") 24 | pixmap = QPixmap(Icon.logo_ico_path) 25 | icon = QIcon(pixmap) 26 | error_box.setWindowIcon(icon) 27 | # 设置对话框的文本消息 28 | error_box.setText(msg) 29 | # 设置对话框的图标,使用 QMessageBox.Critical 作为图标类型 30 | error_box.setIcon(QMessageBox.Critical) 31 | # 添加一个“确定”按钮 32 | error_box.addButton(QMessageBox.Ok) 33 | # 显示对话框 34 | error_box.exec() 35 | # 调用原始的 excepthook,以便程序正常退出 36 | sys.__excepthook__(exc_type, exc_value, traceback_) 37 | 38 | 39 | # 设置 excepthook 40 | sys.excepthook = excepthook 41 | from PySide6.QtGui import QFont, QPixmap, QIcon 42 | from PySide6.QtWidgets import * 43 | from PySide6.QtCore import Qt 44 | 45 | from app.log import logger 46 | from app.ui import mainview 47 | 48 | import platform 49 | 50 | if platform.system() == "Windows": 51 | ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("EasyBox") 52 | QApplication.setHighDpiScaleFactorRoundingPolicy( 53 | Qt.HighDpiScaleFactorRoundingPolicy.PassThrough) 54 | 55 | 56 | class ViewController(QWidget): 57 | def __init__(self): 58 | super().__init__() 59 | pixmap = QPixmap(Icon.logo_ico_path) 60 | icon = QIcon(pixmap) 61 | self.setWindowIcon(icon) 62 | self.viewLogin = None 63 | self.viewMainWindow = None 64 | self.viewDecrypt = None 65 | 66 | def loadPCDecryptView(self): 67 | """ 68 | 登录界面 69 | :return: 70 | """ 71 | 72 | def loadLoginView(self): 73 | """ 74 | 登录界面 75 | :return: 76 | """ 77 | 78 | def login(self, username): 79 | self.loadMainWinView(username) 80 | 81 | def loadMainWinView(self, username=''): 82 | """ 83 | 聊天界面 84 | :param username: 账号 85 | :return: 86 | """ 87 | self.viewMainWindow = mainview.MainWinController() 88 | self.viewMainWindow.setWindowTitle(f"EasyBox-{version}") 89 | self.viewMainWindow.exitSignal.connect(self.close) 90 | try: 91 | self.viewMainWindow.show() 92 | except Exception as e: 93 | print(f"Exception: {e}") 94 | logger.error(traceback.format_exc()) 95 | 96 | def close(self) -> bool: 97 | super().close() 98 | 99 | 100 | if __name__ == '__main__': 101 | freeze_support() 102 | app = QApplication(sys.argv) 103 | font = QFont('微软雅黑', 12) # 使用 Times New Roman 字体,字体大小为 14 104 | app.setFont(font) 105 | view = ViewController() 106 | widget = view.viewMainWindow 107 | try: 108 | # view.loadPCDecryptView() 109 | # view.loadLoginView() 110 | view.loadMainWinView() 111 | # view.show() 112 | # view.show_success() 113 | sys.exit(app.exec()) 114 | except Exception as e: 115 | print(f"Exception: {e}") 116 | logger.error(traceback.format_exc()) 117 | -------------------------------------------------------------------------------- /main1.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | 3 | 4 | a = Analysis( 5 | ['main.py'], 6 | pathex=[], 7 | binaries=[], 8 | datas=[ 9 | (".\\.venv\\Lib\\site-packages\\docxcompose\\templates",'docxcompose/templates/'), 10 | ], 11 | hiddenimports=['cv2'], 12 | hookspath=[], 13 | hooksconfig={}, 14 | runtime_hooks=[], 15 | excludes=['PySide6.QtQuick', 'PySide6.QtQml', 'PySide6.QtOpenGL'], 16 | noarchive=False, 17 | ) 18 | pyz = PYZ(a.pure) 19 | 20 | exe1 = EXE( 21 | pyz, 22 | a.scripts, 23 | [], 24 | exclude_binaries=True, 25 | uac_admin=False, 26 | name='EasyBox', 27 | debug=False, 28 | bootloader_ignore_signals=False, 29 | strip=False, 30 | upx=True, 31 | upx_exclude=[], 32 | runtime_tmpdir=None, 33 | console=False, 34 | disable_windowed_traceback=True, 35 | argv_emulation=False, 36 | target_arch=None, 37 | codesign_identity=None, 38 | entitlements_file=None, 39 | icon=['./resources/images/logo.png'], 40 | version='version.txt', 41 | ) 42 | 43 | coll2 = COLLECT( 44 | exe1, 45 | a.binaries, 46 | a.datas, 47 | strip=False, 48 | upx=True, 49 | upx_exclude=[], 50 | name='EasyBox', 51 | ) 52 | 53 | import os 54 | import shutil 55 | 56 | # 删除 dist 目录中的 opengl32sw.dll 57 | dist_dir = './dist/EasyBox/_internal' # 替换成你的应用程序路径 58 | del_path = [ 59 | ['PySide6', 'opengl32sw.dll'], 60 | ['cv2', 'opencv_videoio_ffmpeg4100_64.dll'] 61 | ] 62 | 63 | for p in del_path: 64 | dll_path = os.path.join(dist_dir,*p) 65 | if os.path.exists(dll_path): 66 | os.remove(dll_path) 67 | -------------------------------------------------------------------------------- /main2.spec: -------------------------------------------------------------------------------- 1 | 2 | a3 = Analysis( 3 | ['./app/ui/pdf_tools/merge/merge.py'], 4 | pathex=[], 5 | binaries=[], 6 | datas=[], 7 | hiddenimports=['openpyxl.cell._writer'], 8 | hookspath=[], 9 | hooksconfig={}, 10 | runtime_hooks=[], 11 | excludes=[], 12 | noarchive=False, 13 | ) 14 | pyz3 = PYZ(a3.pure) 15 | exe3 = EXE( 16 | pyz3, 17 | a3.scripts, 18 | [], 19 | exclude_binaries=True, 20 | uac_admin=False, 21 | name='PDF合并', 22 | debug=False, 23 | bootloader_ignore_signals=False, 24 | strip=False, 25 | upx=True, 26 | upx_exclude=[], 27 | runtime_tmpdir=None, 28 | console=False, 29 | disable_windowed_traceback=True, 30 | argv_emulation=False, 31 | target_arch=None, 32 | codesign_identity=None, 33 | entitlements_file=None, 34 | icon=['./resources/logo.png'], 35 | version='version.txt', 36 | ) 37 | 38 | coll2 = COLLECT( 39 | exe1,exe2, 40 | a2.binaries, 41 | a2.datas, 42 | strip=False, 43 | upx=True, 44 | upx_exclude=[], 45 | name='EasyBox', 46 | ) -------------------------------------------------------------------------------- /pdf2docx/__init__.py: -------------------------------------------------------------------------------- 1 | from .converter import Converter 2 | from .page.Page import Page -------------------------------------------------------------------------------- /pdf2docx/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/__pycache__/converter.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/__pycache__/converter.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/common/Block.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | '''Base class for text/image/table blocks. 4 | ''' 5 | 6 | from .share import BlockType, TextAlignment 7 | from .Element import Element 8 | 9 | 10 | class Block(Element): 11 | '''Base class for text/image/table blocks. 12 | 13 | Attributes: 14 | raw (dict): initialize object from raw properties. 15 | parent (optional): parent object that this block belongs to. 16 | ''' 17 | def __init__(self, raw:dict=None, parent=None): 18 | self._type = BlockType.UNDEFINED 19 | 20 | # horizontal spacing 21 | if raw is None: raw = {} 22 | self.alignment = self._get_alignment(raw.get('alignment', 0)) 23 | self.left_space = raw.get('left_space', 0.0) 24 | self.right_space = raw.get('right_space', 0.0) 25 | self.first_line_space = raw.get('first_line_space', 0.0) 26 | 27 | # RELATIVE position of tab stops 28 | self.tab_stops = raw.get('tab_stops', []) 29 | 30 | # vertical spacing 31 | self.before_space = raw.get('before_space', 0.0) 32 | self.after_space = raw.get('after_space', 0.0) 33 | self.line_space = raw.get('line_space', 0.0) 34 | self.line_space_type = raw.get('line_space_type', 1) # 0-exactly, 1-relatively 35 | 36 | super().__init__(raw, parent) 37 | 38 | 39 | @property 40 | def is_text_block(self): 41 | '''Whether test block.''' 42 | return self._type==BlockType.TEXT 43 | 44 | @property 45 | def is_inline_image_block(self): 46 | '''Whether inline image block.''' 47 | return self._type==BlockType.IMAGE 48 | 49 | @property 50 | def is_float_image_block(self): 51 | '''Whether float image block.''' 52 | return self._type==BlockType.FLOAT_IMAGE 53 | 54 | @property 55 | def is_image_block(self): 56 | '''Whether inline or float image block.''' 57 | return self.is_inline_image_block or self.is_float_image_block 58 | 59 | @property 60 | def is_text_image_block(self): 61 | '''Whether text block or inline image block.''' 62 | return self.is_text_block or self.is_inline_image_block 63 | 64 | @property 65 | def is_lattice_table_block(self): 66 | '''Whether lattice table (explicit table borders) block.''' 67 | return self._type==BlockType.LATTICE_TABLE 68 | 69 | @property 70 | def is_stream_table_block(self): 71 | '''Whether stream table (implied by table content) block.''' 72 | return self._type==BlockType.STREAM_TABLE 73 | 74 | @property 75 | def is_table_block(self): 76 | '''Whether table (lattice or stream) block.''' 77 | return self.is_lattice_table_block or self.is_stream_table_block 78 | 79 | def set_text_block(self): 80 | '''Set block type.''' 81 | self._type = BlockType.TEXT 82 | 83 | def set_inline_image_block(self): 84 | '''Set block type.''' 85 | self._type = BlockType.IMAGE 86 | 87 | def set_float_image_block(self): 88 | '''Set block type.''' 89 | self._type = BlockType.FLOAT_IMAGE 90 | 91 | def set_lattice_table_block(self): 92 | '''Set block type.''' 93 | self._type = BlockType.LATTICE_TABLE 94 | 95 | def set_stream_table_block(self): 96 | '''Set block type.''' 97 | self._type = BlockType.STREAM_TABLE 98 | 99 | def _get_alignment(self, mode:int): 100 | for t in TextAlignment: 101 | if t.value==mode: 102 | return t 103 | return TextAlignment.LEFT 104 | 105 | def parse_horizontal_spacing(self, bbox, *args): 106 | """Set left alignment, and calculate left space. 107 | 108 | Override by :obj:`pdf2docx.text.TextBlock`. 109 | 110 | Args: 111 | bbox (fitz.rect): boundary box of this block. 112 | """ 113 | # NOTE: in PyMuPDF CS, horizontal text direction is same with positive x-axis, 114 | # while vertical text is on the contrary, so use f = -1 here 115 | idx, f = (0, 1.0) if self.is_horizontal_text else (3, -1.0) 116 | self.alignment = TextAlignment.LEFT 117 | self.left_space = (self.bbox[idx] - bbox[idx]) * f 118 | 119 | 120 | def store(self): 121 | '''Store attributes in json format.''' 122 | res = super().store() 123 | res.update({ 124 | 'type' : self._type.value, 125 | 'alignment' : self.alignment.value, 126 | 'left_space' : self.left_space, 127 | 'right_space' : self.right_space, 128 | 'first_line_space' : self.first_line_space, 129 | 'before_space' : self.before_space, 130 | 'after_space' : self.after_space, 131 | 'line_space' : self.line_space, 132 | 'line_space_type' : self.line_space_type, 133 | 'tab_stops' : self.tab_stops 134 | }) 135 | return res 136 | 137 | 138 | def make_docx(self, *args, **kwargs): 139 | """Create associated docx element. 140 | 141 | Raises: 142 | NotImplementedError 143 | """ 144 | raise NotImplementedError -------------------------------------------------------------------------------- /pdf2docx/common/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/common/__init__.py -------------------------------------------------------------------------------- /pdf2docx/common/__pycache__/Block.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/common/__pycache__/Block.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/common/__pycache__/Collection.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/common/__pycache__/Collection.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/common/__pycache__/Element.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/common/__pycache__/Element.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/common/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/common/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/common/__pycache__/algorithm.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/common/__pycache__/algorithm.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/common/__pycache__/constants.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/common/__pycache__/constants.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/common/__pycache__/docx.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/common/__pycache__/docx.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/common/__pycache__/share.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/common/__pycache__/share.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/common/constants.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | # ------------------------------------- 5 | # basic unit 6 | # ------------------------------------- 7 | PT = 1.0 # basic unit 8 | ITP = 72.0 # inch to point 9 | 10 | MAJOR_DIST = 5.0 * PT # significant distance exists between two block lines 11 | MINOR_DIST = 1.0 * PT # small distance 12 | TINY_DIST = 0.5 * PT # very small distance 13 | 14 | FACTOR_SAME = 0.99 15 | FACTOR_ALMOST = 0.95 16 | FACTOR_MOST = 0.90 17 | FACTOR_MAJOR = 0.75 18 | FACTOR_A_HALF = 0.5 19 | FACTOR_A_FEW = 0.1 20 | FACTOR_FEW = 0.01 21 | 22 | 23 | # ------------------------------------- 24 | # docx 25 | # ------------------------------------- 26 | HIDDEN_W_BORDER = 0.0 # do not show border 27 | MIN_LINE_SPACING = 0.7 # minimum line spacing available in MS word 28 | DEFAULT_LINE_SPACING = 1.02 29 | 30 | # punctuation implying end of a sentense 31 | SENTENCE_END_PUNC = '..。??!!' 32 | 33 | # control characters not supported by lxml 34 | # https://github.com/dothinking/pdf2docx/issues/126#issuecomment-1040034077 35 | INVALID_CHARS = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f' 36 | 37 | 38 | # ------------------------------------- 39 | # font parameters 40 | # ------------------------------------- 41 | 42 | # CJK font definition parameters 43 | CJK_CODEPAGE_BITS = { 44 | "JIS/Japan": 17, 45 | "Chinese: Simplified chars—PRC and Singapore": 18, 46 | "Korean Wansung": 19, 47 | "Chinese: Traditional chars—Taiwan and Hong Kong": 20, 48 | "Korean Johab": 21 49 | } 50 | 51 | CJK_UNICODE_RANGE_BITS = { 52 | 'Hangul Jamo': 28, 53 | 'Hiragana': 49, 54 | 'Katakana': 50, 55 | 'Bopomofo': 51, 56 | 'Hangul Compatibility Jamo': 52, 57 | 'Enclosed CJK Letters And Months': 54, 58 | 'CJK Compatibility': 55, 59 | 'Hangul Syllables': 56, 60 | 'CJK Unified Ideographs': 59, 61 | 'CJK Strokes': 61, 62 | 'Yi Syllables': 83 63 | } 64 | 65 | CJK_UNICODE_RANGES = [ 66 | [0x1100, 0x11FF], # Hangul Jamo 67 | [0x3040, 0x309F], # Hiragana 68 | [0x30A0, 0x30FF], # Katakana 69 | [0x31F0, 0x31FF], # Katakana Phonetic Extensions 70 | [0x3100, 0x312F], # Bopomofo 71 | [0x31A0, 0x31BF], # Bopomofo Extended (Bopomofo) 72 | [0x3130, 0x318F], # Hangul Compatibility Jamo 73 | [0x3200, 0x32FF], # Enclosed CJK Letters and Months 74 | [0x3300, 0x33FF], # CJK Compatibility 75 | [0xAC00, 0xD7AF], # Hangul Syllables 76 | [0x4E00, 0x9FFF], # CJK Unified Ideographs 77 | [0x2E80, 0x2EFF], # CJK Radicals Supplement (CJK Unified Ideographs) 78 | [0x2F00, 0x2FDF], # Kangxi Radicals (CJK Unified Ideographs) 79 | [0x2FF0, 0x2FFF], # Ideographic Description Characters (CJK Unified Ideographs) 80 | [0x3400, 0x4DBF], # CJK Unified Ideographs Extension A (CJK Unified Ideographs) 81 | [0x20000, 0x2A6DF], # CJK Unified Ideographs Extension B (CJK Unified Ideographs) 82 | [0x3190, 0x319F], # Kanbun (CJK Unified Ideographs) 83 | [0x31C0, 0x31EF], # CJK Strokes 84 | [0xF900, 0xFAFF], # CJK Compatibility Ideographs (CJK Strokes) 85 | [0x2F800, 0x2FA1F], # CJK Compatibility Ideographs Supplement (CJK Strokes) 86 | [0xA000, 0xA48F], # Yi Syllables 87 | [0xA490, 0xA4CF], # Yi Radicals 88 | ] 89 | 90 | DEFAULT_FONT_NAME = 'helv' -------------------------------------------------------------------------------- /pdf2docx/font/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/font/__init__.py -------------------------------------------------------------------------------- /pdf2docx/font/__pycache__/Fonts.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/font/__pycache__/Fonts.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/font/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/font/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/image/Image.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | '''Image object. 4 | 5 | Data structure defined in link https://pymupdf.readthedocs.io/en/latest/textpage.html:: 6 | 7 | { 8 | 'type': 1, 9 | 'bbox': (x0,y0,x1,y1), 10 | 'width': w, 11 | 'height': h, 12 | 'image': b'', 13 | 14 | # --- discard properties --- 15 | 'ext': 'png', 16 | 'colorspace': n, 17 | 'xref': xref, 'yref': yref, 'bpc': bpc 18 | } 19 | ''' 20 | 21 | import base64 22 | from io import BytesIO 23 | from ..common import docx 24 | from ..common.Element import Element 25 | 26 | 27 | class Image(Element): 28 | '''Base image object.''' 29 | 30 | def __init__(self, raw:dict=None): 31 | if raw is None: raw = {} 32 | self.width = raw.get('width', 0.0) 33 | self.height = raw.get('height', 0.0) 34 | 35 | # source image bytes 36 | # - image bytes passed from PyMuPDF -> use it directly 37 | # - base64 encoded string restored from json file -> encode to bytes and decode with base64 -> image bytes 38 | image = raw.get('image', b'') 39 | self.image = image if isinstance(image, bytes) else base64.b64decode(image.encode()) 40 | 41 | super().__init__(raw) 42 | 43 | 44 | @property 45 | def text(self): 46 | '''Get an image placeholder ````.''' 47 | return '' 48 | 49 | 50 | def from_image(self, image): 51 | '''Update with image block/span. 52 | 53 | Args: 54 | image (Image): Target image block/span. 55 | ''' 56 | self.width = image.width 57 | self.height = image.height 58 | self.image = image.image 59 | self.update_bbox(image.bbox) 60 | return self 61 | 62 | 63 | def store(self): 64 | '''Store image with base64 encode. 65 | 66 | * Encode image bytes with base64 -> base64 bytes 67 | * Decode base64 bytes -> str -> so can be serialized in json format 68 | ''' 69 | res = super().store() 70 | res.update({ 71 | 'width': self.width, 72 | 'height': self.height, 73 | 'image': base64.b64encode(self.image).decode() # serialize image with base64 74 | }) 75 | 76 | return res 77 | 78 | 79 | def plot(self, page, color:tuple): 80 | '''Plot image bbox with diagonal lines (for debug purpose). 81 | 82 | Args: 83 | page (fitz.Page): Plotting page. 84 | ''' 85 | x0, y0, x1, y1 = self.bbox 86 | page.draw_line((x0, y0), (x1, y1), color=color, width=0.5) 87 | page.draw_line((x0, y1), (x1, y0), color=color, width=0.5) 88 | super().plot(page, stroke=color) 89 | 90 | 91 | def make_docx(self, paragraph): 92 | '''Add image span to a docx paragraph.''' 93 | # add image 94 | docx.add_image(paragraph, BytesIO(self.image), self.bbox.x1-self.bbox.x0, self.bbox.y1-self.bbox.y0) -------------------------------------------------------------------------------- /pdf2docx/image/ImageBlock.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | '''Definition of Image block objects. 4 | 5 | **The raw image block will be merged into TextBlock > Line > Span.** 6 | ''' 7 | 8 | from io import BytesIO 9 | from ..text.Line import Line 10 | from ..text.TextBlock import TextBlock 11 | from .Image import Image 12 | from .ImageSpan import ImageSpan 13 | from ..common.Block import Block 14 | from ..common.docx import add_float_image 15 | 16 | 17 | class ImageBlock(Image, Block): 18 | '''Image block.''' 19 | def __init__(self, raw:dict=None): 20 | super().__init__(raw) 21 | 22 | # inline image type by default 23 | self.set_inline_image_block() 24 | 25 | 26 | def to_text_block(self): 27 | """Convert image block to a span under text block. 28 | 29 | Returns: 30 | TextBlock: New TextBlock instance containing this image. 31 | """ 32 | # image span 33 | span = ImageSpan().from_image(self) 34 | 35 | # add span to line 36 | image_line = Line() 37 | image_line.add(span) 38 | 39 | # insert line to block 40 | block = TextBlock() 41 | block.add(image_line) 42 | 43 | # NOTE: it's an image block even though in TextBlock type 44 | block.set_inline_image_block() 45 | 46 | return block 47 | 48 | 49 | def store(self): 50 | '''Store ImageBlock instance in raw dict.''' 51 | res = Block.store(self) 52 | res.update( 53 | Image.store(self) 54 | ) 55 | return res 56 | 57 | 58 | def plot(self, page): 59 | '''Plot image bbox with diagonal lines (for debug purpose). 60 | 61 | Args: 62 | page (fitz.Page): pdf page to plot. 63 | ''' 64 | super().plot(page, color=(1,0,0)) 65 | 66 | 67 | def make_docx(self, p): 68 | '''Create floating image behind text. 69 | 70 | Args: 71 | p (Paragraph): ``python-docx`` paragraph instance. 72 | 73 | .. note:: 74 | Inline image is created within TextBlock. 75 | ''' 76 | if self.is_float_image_block: 77 | x0, y0, x1, y1 = self.bbox 78 | add_float_image(p, BytesIO(self.image), width=x1-x0, pos_x=x0, pos_y=y0) 79 | else: 80 | super().make_docx(p) 81 | return p -------------------------------------------------------------------------------- /pdf2docx/image/ImageSpan.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | '''Image span based on same raw data structure with Image block. 4 | ''' 5 | 6 | from ..common import constants 7 | from .Image import Image 8 | 9 | 10 | class ImageSpan(Image): 11 | '''Image span.''' 12 | 13 | def intersects(self, rect): 14 | '''Create new ImageSpan object with image contained in given bbox. 15 | 16 | Args: 17 | rect (fitz.Rect): Target bbox. 18 | 19 | Returns: 20 | ImageSpan: A copy of itself if intersects with target; otherwise empty ImageSpan. 21 | ''' 22 | # add image span if most of of the image is contained in bbox 23 | if self.get_main_bbox(rect, constants.FACTOR_MAJOR): 24 | return self.copy() 25 | 26 | # otherwise, ignore image 27 | return ImageSpan() -------------------------------------------------------------------------------- /pdf2docx/image/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/image/__init__.py -------------------------------------------------------------------------------- /pdf2docx/image/__pycache__/Image.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/image/__pycache__/Image.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/image/__pycache__/ImageBlock.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/image/__pycache__/ImageBlock.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/image/__pycache__/ImageSpan.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/image/__pycache__/ImageSpan.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/image/__pycache__/ImagesExtractor.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/image/__pycache__/ImagesExtractor.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/image/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/image/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/layout/Column.py: -------------------------------------------------------------------------------- 1 | '''Column of Section. 2 | 3 | In most cases, one section per page. But in case multi-columns page, sections are used 4 | to distinguish these different layouts. 5 | 6 | .. note:: 7 | Currently, support at most two columns. 8 | 9 | :: 10 | 11 | { 12 | 'bbox': (x0, y0, x1, y1), 13 | 'blocks': [{ 14 | ... # block instances 15 | }, ...], 16 | 'shapes': [{ 17 | ... # shape instances 18 | }, ...] 19 | } 20 | ''' 21 | 22 | from ..common.Collection import Collection 23 | from ..layout.Layout import Layout 24 | from ..shape.Shape import Shape 25 | from ..text.Line import Line 26 | 27 | 28 | class Column(Layout): 29 | '''Column of Section.''' 30 | 31 | @property 32 | def working_bbox(self): return self.bbox 33 | 34 | 35 | def add_elements(self, elements:Collection): 36 | '''Add candidate elements, i.e. lines or shapes, to current column.''' 37 | blocks = [e for e in elements if isinstance(e, Line)] 38 | shapes = [e for e in elements if isinstance(e, Shape)] 39 | self.assign_blocks(blocks) 40 | self.assign_shapes(shapes) 41 | 42 | 43 | def make_docx(self, doc): 44 | '''Create Section Column in docx. 45 | 46 | Args: 47 | doc (Document): ``python-docx`` document object 48 | ''' 49 | self.blocks.make_docx(doc) 50 | -------------------------------------------------------------------------------- /pdf2docx/layout/Section.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | '''Section of Page. 4 | 5 | In most cases, one section per page. But in case multi-columns page, sections are used 6 | to distinguish these different layouts. 7 | 8 | .. note:: 9 | Currently, support at most two columns. 10 | 11 | :: 12 | 13 | { 14 | 'bbox': (x0,y0,x1,y1) 15 | 'num_cols': 1, 16 | 'space': 0, 17 | 'columns': [{ 18 | ... # column properties 19 | }, ...] 20 | } 21 | ''' 22 | 23 | from docx.enum.section import WD_SECTION 24 | from ..common.docx import set_columns 25 | from ..common.Collection import BaseCollection 26 | from .Column import Column 27 | 28 | 29 | class Section(BaseCollection): 30 | 31 | def __init__(self, space:int=0, columns:list=None, parent=None): 32 | """Initialize Section instance. 33 | 34 | Args: 35 | space (int, optional): Space between adjacent columns. Defaults to 0. 36 | columns (list, optional): A list of Column instances. Defaults to None. 37 | parent (Sections, optional): Parent element. Defaults to None. 38 | """ 39 | self.space = space 40 | self.before_space = 0.0 41 | super().__init__(columns, parent) 42 | 43 | 44 | @property 45 | def num_cols(self): return len(self) 46 | 47 | 48 | def store(self): 49 | '''Store parsed section layout in dict format.''' 50 | return { 51 | 'bbox' : tuple([x for x in self.bbox]), 52 | 'num_cols' : self.num_cols, 53 | 'space' : self.space, 54 | 'before_space' : self.before_space, 55 | 'columns': super().store() 56 | } 57 | 58 | 59 | def restore(self, raw:dict): 60 | '''Restore section from source dict.''' 61 | # bbox is maintained automatically based on columns 62 | self.space = raw.get('space', 0) # space between adjacent columns 63 | self.before_space = raw.get('before_space', 0) # space between adjacent columns 64 | 65 | # get each column 66 | for raw_col in raw.get('columns', []): 67 | column = Column().restore(raw_col) 68 | self.append(column) 69 | 70 | return self 71 | 72 | 73 | def parse(self, **settings): 74 | '''Parse section layout.''' 75 | for column in self: column.parse(**settings) 76 | return self 77 | 78 | 79 | def make_docx(self, doc): 80 | '''Create section in docx. 81 | 82 | Args: 83 | doc (Document): ``python-docx`` document object 84 | ''' 85 | # set section column 86 | section = doc.sections[-1] 87 | width_list = [c.bbox[2]-c.bbox[0] for c in self] 88 | set_columns(section, width_list, self.space) 89 | 90 | # add create each column 91 | for column in self: 92 | # column break to start new column 93 | if column != self[0]: 94 | doc.add_section(WD_SECTION.NEW_COLUMN) 95 | 96 | # make doc 97 | column.make_docx(doc) -------------------------------------------------------------------------------- /pdf2docx/layout/Sections.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | '''Collection of :py:class:`~pdf2docx.layout.Section` instances. 4 | ''' 5 | 6 | from docx.enum.section import WD_SECTION 7 | from docx.shared import Pt 8 | from ..common.Collection import BaseCollection 9 | from ..common.docx import reset_paragraph_format 10 | from .Section import Section 11 | from ..common import constants 12 | 13 | 14 | class Sections(BaseCollection): 15 | 16 | def restore(self, raws:list): 17 | """Restore sections from source dicts.""" 18 | self.reset() 19 | for raw in raws: 20 | section = Section().restore(raw) 21 | self.append(section) 22 | return self 23 | 24 | 25 | def parse(self, **settings): 26 | '''Parse layout under section level.''' 27 | for section in self: section.parse(**settings) 28 | return self 29 | 30 | 31 | def make_docx(self, doc): 32 | '''Create sections in docx.''' 33 | if not self: return 34 | 35 | # mark paragraph index before creating current page 36 | n = len(doc.paragraphs) 37 | 38 | def create_dummy_paragraph_for_section(section): 39 | p = doc.add_paragraph() 40 | line_height = min(section.before_space, 11) 41 | pf = reset_paragraph_format(p, line_spacing=Pt(line_height)) 42 | pf.space_after = Pt(section.before_space-line_height) 43 | 44 | # --------------------------------------------------- 45 | # first section 46 | # --------------------------------------------------- 47 | # vertical position: add dummy paragraph only if before space is required 48 | section = self[0] 49 | if section.before_space > constants.MINOR_DIST: 50 | create_dummy_paragraph_for_section(section) 51 | 52 | # create first section 53 | if section.num_cols==2: 54 | doc.add_section(WD_SECTION.CONTINUOUS) 55 | section.make_docx(doc) 56 | 57 | # --------------------------------------------------- 58 | # more sections 59 | # --------------------------------------------------- 60 | for section in self[1:]: 61 | # create new section symbol 62 | doc.add_section(WD_SECTION.CONTINUOUS) 63 | 64 | # set after space of last paragraph to define the vertical 65 | # position of current section 66 | # NOTE: the after space doesn't work if last paragraph is 67 | # image only (without any text). In this case, set after 68 | # space for the section break. 69 | p = doc.paragraphs[-2] # -1 is the section break 70 | if not p.text.strip() and 'graphicData' in p._p.xml: 71 | p = doc.paragraphs[-1] 72 | pf = p.paragraph_format 73 | pf.space_after = Pt(section.before_space) 74 | 75 | # section content 76 | section.make_docx(doc) 77 | 78 | # --------------------------------------------------- 79 | # create floating images 80 | # --------------------------------------------------- 81 | # lazy: assign all float images to first paragraph of current page 82 | for image in self.parent.float_images: 83 | image.make_docx(doc.paragraphs[n]) 84 | 85 | 86 | def plot(self, page): 87 | '''Plot all section blocks for debug purpose.''' 88 | for section in self: 89 | for column in section: 90 | column.plot(page, stroke=(1,1,0), width=1.5) # column bbox 91 | column.blocks.plot(page) # blocks -------------------------------------------------------------------------------- /pdf2docx/layout/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/layout/__init__.py -------------------------------------------------------------------------------- /pdf2docx/layout/__pycache__/Blocks.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/layout/__pycache__/Blocks.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/layout/__pycache__/Column.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/layout/__pycache__/Column.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/layout/__pycache__/Layout.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/layout/__pycache__/Layout.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/layout/__pycache__/Section.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/layout/__pycache__/Section.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/layout/__pycache__/Sections.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/layout/__pycache__/Sections.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/layout/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/layout/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/page/BasePage.py: -------------------------------------------------------------------------------- 1 | '''Base page with basic properties, e.g. width, height and margin.''' 2 | 3 | class BasePage: 4 | def __init__(self, width:float=0.0, height:float=0.0, margin:tuple=None): 5 | '''Initialize page layout. 6 | 7 | Args: 8 | width (float, optional): Page width. Defaults to 0.0. 9 | height (float, optional): Page height. Defaults to 0.0. 10 | margin (tuple, optional): Page margin. Defaults to None. 11 | ''' 12 | # page size and margin 13 | self.width = width 14 | self.height = height 15 | self.margin = margin or (0,) * 4 16 | 17 | 18 | @property 19 | def bbox(self): return (0.0, 0.0, self.width, self.height) 20 | 21 | 22 | @property 23 | def working_bbox(self): 24 | '''bbox with margin considered.''' 25 | x0, y0, x1, y1 = self.bbox 26 | L, R, T, B = self.margin 27 | return (x0+L, y0+T, x1-R, y1-B) -------------------------------------------------------------------------------- /pdf2docx/page/Pages.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | '''Collection of :py:class:`~pdf2docx.page.Page` instances.''' 4 | 5 | import logging 6 | 7 | from .RawPageFactory import RawPageFactory 8 | from ..common.Collection import BaseCollection 9 | from ..font.Fonts import Fonts 10 | 11 | 12 | class Pages(BaseCollection): 13 | '''A collection of ``Page``.''' 14 | 15 | def parse(self, fitz_doc, **settings): 16 | '''Analyze document structure, e.g. page section, header, footer. 17 | 18 | Args: 19 | fitz_doc (fitz.Document): ``PyMuPDF`` Document instance. 20 | settings (dict): Parsing parameters. 21 | ''' 22 | # --------------------------------------------- 23 | # 0. extract fonts properties, especially line height ratio 24 | # --------------------------------------------- 25 | fonts = Fonts.extract(fitz_doc) 26 | 27 | # --------------------------------------------- 28 | # 1. extract and then clean up raw page 29 | # --------------------------------------------- 30 | pages, raw_pages = [], [] 31 | words_found = False 32 | for page in self: 33 | if page.skip_parsing: continue 34 | 35 | # init and extract data from PDF 36 | raw_page = RawPageFactory.create(page_engine=fitz_doc[page.id], backend='PyMuPDF') 37 | raw_page.restore(**settings) 38 | 39 | # check if any words are extracted since scanned pdf may be directed 40 | if not words_found and raw_page.raw_text.strip(): 41 | words_found = True 42 | 43 | # process blocks and shapes based on bbox 44 | raw_page.clean_up(**settings) 45 | 46 | # process font properties 47 | raw_page.process_font(fonts) 48 | 49 | # after this step, we can get some basic properties 50 | # NOTE: floating images are detected when cleaning up blocks, so collect them here 51 | page.width = raw_page.width 52 | page.height = raw_page.height 53 | page.float_images.reset().extend(raw_page.blocks.floating_image_blocks) 54 | 55 | raw_pages.append(raw_page) 56 | pages.append(page) 57 | 58 | # show message if no words found 59 | if not words_found: 60 | logging.warning('Words count: 0. It might be a scanned pdf, which is not supported yet.') 61 | 62 | 63 | # --------------------------------------------- 64 | # 2. parse structure in document/pages level 65 | # --------------------------------------------- 66 | # NOTE: blocks structure might be changed in this step, e.g. promote page header/footer, 67 | # so blocks structure based process, e.g. calculating margin, parse section should be 68 | # run after this step. 69 | header, footer = Pages._parse_document(raw_pages) 70 | 71 | 72 | # --------------------------------------------- 73 | # 3. parse structure in page level, e.g. page margin, section 74 | # --------------------------------------------- 75 | # parse sections 76 | for page, raw_page in zip(pages, raw_pages): 77 | # page margin 78 | margin = raw_page.calculate_margin(**settings) 79 | raw_page.margin = page.margin = margin 80 | 81 | # page section 82 | sections = raw_page.parse_section(**settings) 83 | page.sections.extend(sections) 84 | 85 | 86 | @staticmethod 87 | def _parse_document(raw_pages:list): 88 | '''Parse structure in document/pages level, e.g. header, footer''' 89 | # TODO 90 | return '', '' -------------------------------------------------------------------------------- /pdf2docx/page/RawPageFactory.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Load :py:class:`~pdf2docx.page.RawPage` with specified pdf engine, 3 | e.g. PyMuPDF, pdfminer.six. For now, only PyMuPDF is implemented. 4 | ''' 5 | 6 | from .RawPageFitz import RawPageFitz 7 | 8 | 9 | class RawPageFactory: 10 | 11 | MAP = { 12 | 'PYMUPDF': RawPageFitz 13 | } 14 | 15 | @classmethod 16 | def create(cls, page_engine, backend:str='pymupdf'): 17 | '''Create RawPage class with specified backend.''' 18 | klass = cls.MAP.get(backend.upper(), None) 19 | if not klass: 20 | raise TypeError(f'Page with pdf engine "{backend}" is not implemented yet.') 21 | else: 22 | return klass(page_engine=page_engine) 23 | -------------------------------------------------------------------------------- /pdf2docx/page/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/page/__init__.py -------------------------------------------------------------------------------- /pdf2docx/page/__pycache__/BasePage.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/page/__pycache__/BasePage.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/page/__pycache__/Page.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/page/__pycache__/Page.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/page/__pycache__/Pages.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/page/__pycache__/Pages.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/page/__pycache__/RawPage.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/page/__pycache__/RawPage.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/page/__pycache__/RawPageFactory.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/page/__pycache__/RawPageFactory.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/page/__pycache__/RawPageFitz.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/page/__pycache__/RawPageFitz.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/page/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/page/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/shape/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/shape/__init__.py -------------------------------------------------------------------------------- /pdf2docx/shape/__pycache__/Path.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/shape/__pycache__/Path.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/shape/__pycache__/Paths.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/shape/__pycache__/Paths.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/shape/__pycache__/Shape.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/shape/__pycache__/Shape.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/shape/__pycache__/Shapes.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/shape/__pycache__/Shapes.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/shape/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/shape/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/table/Cells.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | '''Collection of Cell instances. 4 | ''' 5 | 6 | from .Cell import Cell 7 | from ..common.Collection import ElementCollection 8 | 9 | 10 | class Cells(ElementCollection): 11 | '''A group of Cells.''' 12 | def restore(self, raws:list): 13 | '''Restore Cells from source dict. 14 | 15 | Args: 16 | raws (list): A list of source dict. 17 | ''' 18 | for raw in raws: 19 | cell = Cell(raw) 20 | self.append(cell) 21 | return self 22 | 23 | def append(self, cell:Cell): 24 | '''Override. Append a cell (allow empty cell, i.e. merged cells) and update bbox accordingly.''' 25 | self._instances.append(cell) 26 | self._update_bbox(cell) 27 | cell.parent = self._parent # set parent 28 | -------------------------------------------------------------------------------- /pdf2docx/table/Row.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | '''Row in a table. 4 | ''' 5 | 6 | from docx.enum.table import WD_ROW_HEIGHT 7 | from docx.shared import Pt 8 | from .Cells import Cells 9 | from ..common.Element import Element 10 | 11 | 12 | class Row(Element): 13 | '''Row in a table.''' 14 | def __init__(self, raw:dict=None): 15 | if raw is None: raw = {} 16 | super().__init__(raw) 17 | 18 | # logical row height 19 | self.height = raw.get('height', 0.0) 20 | 21 | # cells in row 22 | self._cells = Cells(parent=self).restore(raw.get('cells', [])) 23 | 24 | 25 | def __getitem__(self, idx): 26 | try: 27 | cell = self._cells[idx] 28 | except IndexError: 29 | msg = f'Cell index {idx} out of range' 30 | raise IndexError(msg) 31 | else: 32 | return cell 33 | 34 | def __iter__(self): 35 | return (cell for cell in self._cells) 36 | 37 | def __len__(self): 38 | return len(self._cells) 39 | 40 | 41 | def append(self, cell): 42 | '''Append cell to row and update bbox accordingly.''' 43 | self._cells.append(cell) 44 | 45 | 46 | def store(self): 47 | res = super().store() 48 | res.update({ 49 | 'height': self.height, 50 | 'cells': self._cells.store() 51 | }) 52 | 53 | return res 54 | 55 | 56 | def make_docx(self, table, idx_row:int): 57 | '''Create row of docx table. 58 | 59 | Args: 60 | table (Table): ``python-docx`` table instance. 61 | idx_row (int): Current row index. 62 | ''' 63 | # set row height 64 | docx_row = table.rows[idx_row] 65 | 66 | # to control the layout precisely, set `exact` value, rather than `at least` value 67 | # the associated steps in MS word: Table Properties -> Row -> Row height -> exactly 68 | docx_row.height_rule = WD_ROW_HEIGHT.EXACTLY 69 | 70 | # NOTE: row height is counted from center-line of top border to center line of bottom border 71 | docx_row.height = Pt(self.height) 72 | 73 | # set cell style and contents 74 | for idx_col in range(len(table.columns)): 75 | self._cells[idx_col].make_docx(table, (idx_row, idx_col)) -------------------------------------------------------------------------------- /pdf2docx/table/Rows.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | '''A group of Row objects in a table. 4 | ''' 5 | 6 | from .Row import Row 7 | from ..common.Collection import ElementCollection 8 | 9 | 10 | class Rows(ElementCollection): 11 | '''A group of Rows.''' 12 | 13 | def restore(self, raws:list): 14 | """Restore Rows from source dicts. 15 | 16 | Args: 17 | raws (list): A list of source dicts representing each row. 18 | 19 | Returns: 20 | Rows: self 21 | """ 22 | for raw in raws: 23 | row = Row(raw) 24 | self.append(row) 25 | return self 26 | -------------------------------------------------------------------------------- /pdf2docx/table/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/table/__init__.py -------------------------------------------------------------------------------- /pdf2docx/table/__pycache__/Cell.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/table/__pycache__/Cell.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/table/__pycache__/Cells.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/table/__pycache__/Cells.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/table/__pycache__/Row.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/table/__pycache__/Row.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/table/__pycache__/Rows.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/table/__pycache__/Rows.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/table/__pycache__/TableBlock.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/table/__pycache__/TableBlock.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/table/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/table/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/text/Char.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | '''Char object based on PDF raw dict extracted with ``PyMuPDF``. 4 | 5 | Data structure refer to this `link `_:: 6 | 7 | { 8 | 'bbox' : (x0, y0, x1, y1), 9 | 'c' : str, 10 | 'origin': (x,y) 11 | } 12 | ''' 13 | 14 | 15 | from ..common.constants import INVALID_CHARS 16 | from ..common.Element import Element 17 | from ..shape.Shape import Shape 18 | 19 | 20 | class Char(Element): 21 | '''Object representing a character.''' 22 | def __init__(self, raw:dict=None): 23 | if raw is None: raw = {} 24 | 25 | # Note to filter control character avoiding error when making docx, #126 26 | c = raw.get('c', '') 27 | if c in INVALID_CHARS: c = '' 28 | self.c = c 29 | self.origin = raw.get('origin', None) 30 | 31 | super().__init__(raw) # NOTE: ignore parent element for Char instance 32 | 33 | 34 | def contained_in_rect(self, rect:Shape, horizontal:bool=True): 35 | """Detect whether it locates in a rect. 36 | 37 | Args: 38 | rect (Shape): Target rect to check. 39 | horizontal (bool, optional): Text direction is horizontal if True. Defaults to True. 40 | 41 | Returns: 42 | bool: Whether a Char locates in target rect. 43 | 44 | .. note:: 45 | It's considered as contained in the target rect if the intersection is larger than 46 | half of the char bbox. 47 | """ 48 | # char in rect? 49 | if self.bbox in rect.bbox: return True 50 | 51 | # intersection? 52 | s = self.bbox & rect.bbox 53 | if s.is_empty: return False 54 | if horizontal: return s.width > 0.5*self.bbox.width 55 | return s.height > 0.5*self.bbox.height 56 | 57 | 58 | def store(self): 59 | res = super().store() 60 | res.update({ 61 | 'c': self.c, 62 | 'origin': self.origin 63 | }) 64 | 65 | return res -------------------------------------------------------------------------------- /pdf2docx/text/Spans.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | '''A group of TextSpan and ImageSpan objects. 4 | ''' 5 | 6 | from ..common.Collection import ElementCollection 7 | from .TextSpan import TextSpan 8 | from ..image.ImageSpan import ImageSpan 9 | 10 | class Spans(ElementCollection): 11 | '''Collection of TextSpan and ImageSpan instances.''' 12 | 13 | def restore(self, raws:list): 14 | '''Recreate TextSpan or ImageSpan from source dict list.''' 15 | for raw_span in raws: 16 | if 'image' in raw_span: 17 | span = ImageSpan(raw_span) 18 | else: 19 | span = TextSpan(raw_span) 20 | if not span.text.strip() and not span.style: 21 | span = None 22 | 23 | self.append(span) 24 | return self 25 | 26 | @property 27 | def text_spans(self): 28 | '''Get TextSpan instances.''' 29 | spans = list(filter( 30 | lambda span: isinstance(span, TextSpan), self._instances 31 | )) 32 | return Spans(spans) 33 | 34 | @property 35 | def image_spans(self): 36 | '''Get ImageSpan instances.''' 37 | spans = list(filter( 38 | lambda span: isinstance(span, ImageSpan), self._instances 39 | )) 40 | return Spans(spans) 41 | 42 | 43 | def strip(self): 44 | '''Remove redundant blanks at the begin/end span.''' 45 | stripped = False 46 | if not self._instances: return stripped 47 | 48 | # left strip the first span 49 | left_span = self._instances[0] 50 | if isinstance(left_span, TextSpan): stripped = stripped or left_span.lstrip() 51 | 52 | # right strip the last span 53 | right_span = self._instances[-1] 54 | if isinstance(right_span, TextSpan): stripped = stripped or right_span.rstrip() 55 | 56 | # update bbox 57 | if stripped: self._parent.update_bbox(self.bbox) 58 | 59 | return stripped -------------------------------------------------------------------------------- /pdf2docx/text/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/text/__init__.py -------------------------------------------------------------------------------- /pdf2docx/text/__pycache__/Char.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/text/__pycache__/Char.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/text/__pycache__/Line.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/text/__pycache__/Line.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/text/__pycache__/Lines.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/text/__pycache__/Lines.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/text/__pycache__/Spans.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/text/__pycache__/Spans.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/text/__pycache__/TextBlock.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/text/__pycache__/TextBlock.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/text/__pycache__/TextSpan.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/text/__pycache__/TextSpan.cpython-313.pyc -------------------------------------------------------------------------------- /pdf2docx/text/__pycache__/__init__.cpython-313.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/pdf2docx/text/__pycache__/__init__.cpython-313.pyc -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # EasyBox——一个多功能工具箱 2 | 3 | ## 支持的功能 4 | 5 | * PDF工具箱 6 | * 合并PDF✅ 7 | * 拆分PDF✅ 8 | * PDF加密✅ 9 | * PDF解密✅ 10 | * 文档转换 11 | * PDF转Word 12 | * 网页转PDF 13 | * 图片工具 14 | * 根据文件名修改图片拍摄时间 15 | 16 | ## 相关文章 17 | 18 | * [Python合并多个PDF](https://blog.lc044.love/post/11) 19 | * [PyQt5/PySide6自定义可拖拽列表组件](https://blog.lc044.love/post/12) 20 | * [基于文件名修改图片的拍摄日期](https://blog.lc044.love/post/14) 21 | * [基于 PyQt5/PySide6 实现分组列表滚动吸顶效果](https://blog.lc044.love/post/15) 22 | 23 | ## 运行截图 24 | 25 | ![img.png](doc/images/img.png) 26 | ![img.png](doc/images/img_2.png) 27 | ![img_1.png](doc/images/img_1.png) 28 | ![img_3.png](doc/images/img_3.png) 29 | 30 | ## PDF工具箱功能说明 31 | 32 | ### PDF合并 33 | 将多个PDF文件合并为一个文件,支持设置文件顺序和页面范围,可以保留原PDF的书签。 34 | 35 | ### PDF拆分 36 | 支持多种拆分方式:按页数拆分、按页码范围拆分、提取单页、拆分为单页文件。输出文件格式为"原文件名_拆分文件_页码.pdf"。 37 | 38 | ### PDF加密 39 | 为PDF文件添加密码保护,支持设置用户密码(打开文档)和所有者密码(编辑文档),可自定义权限控制(打印、复制、修改等),支持多种加密方式(AES-128, RC4-128, RC4-40)。 40 | 41 | ### PDF解密 42 | 提供多种解密方式: 43 | - 常规解密:使用用户提供的密码解密 44 | - 密码破解:使用John the Ripper自动破解PDF密码(需额外安装),支持字典攻击和暴力破解 45 | 46 | ## 计划中功能 47 | 48 | * PDF工具箱 49 | * 添加水印 50 | * 去除水印 51 | * 文档转换 52 | * PDF转Word 53 | * PDF转图片 54 | * PDF转TXT 55 | * PDF转MarkDown 56 | * 图片转PDF 57 | * 批量工具 58 | * 批量重命名 59 | * 图片批量改格式 60 | * 文档批量加水印 61 | * 批量替换文本 62 | * 视频工具 63 | * 屏幕录制(没有分辨率限制) 64 | * 定时录制 65 | * 视频转GIF 66 | * 视频转avi 67 | * 视频转MP4 68 | 69 | ## 安装 70 | 71 | ```shell 72 | git clone https://github.com/LC044/EasyBox.git 73 | cd EaseBox 74 | pip install -r requirements.txt 75 | ``` 76 | 77 | ## 运行 78 | 79 | ```shell 80 | python main.py 81 | ``` 82 | 83 | ## 打包 84 | 85 | ```shell 86 | pip install pyinstaller 87 | pyinstaller main1.spec 88 | ``` 89 | 90 | ## 证书 91 | 92 | [AGPL3.0](./LICENSE) -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | PyQtWebEngine 2 | python-docx 3 | docxcompose 4 | pillow~=11.0.0 5 | pymupdf~=1.24.13 6 | PySide6~=6.8.0.2 7 | pdf2docx~=0.5.8 8 | piexif~=1.1.3 9 | numpy~=2.1.3 10 | opencv-python~=4.10.0.84 11 | fonttools~=4.54.1 12 | setuptools~=75.3.0 13 | Cython~=3.0.11 14 | PyPDF2~=3.0.1 15 | pdf2john~=0.2.0 16 | -------------------------------------------------------------------------------- /resource.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | resources/icons/Cursors/0.png 4 | resources/icons/Cursors/1.png 5 | resources/icons/Cursors/2.png 6 | resources/icons/Cursors/3.png 7 | resources/icons/Cursors/4.png 8 | resources/icons/Cursors/5.png 9 | resources/icons/Cursors/6.png 10 | resources/icons/Cursors/7.png 11 | resources/icons/back.svg 12 | resources/icons/logo.png 13 | resources/icons/logo.ico 14 | resources/icons/setting.svg 15 | resources/icons/select.svg 16 | resources/icons/unselect.svg 17 | resources/icons/arrow-left.svg 18 | resources/icons/arrow-right.svg 19 | resources/icons/down.svg 20 | resources/icons/up.svg 21 | resources/icons/增加.svg 22 | resources/icons/批量添加.svg 23 | resources/icons/加.svg 24 | resources/icons/html.svg 25 | resources/icons/markdown.svg 26 | resources/icons/减.svg 27 | resources/icons/工具箱.svg 28 | resources/icons/左展开.svg 29 | resources/icons/右展开.svg 30 | resources/icons/图片.svg 31 | resources/icons/pdf.svg 32 | resources/icons/文档转换.svg 33 | resources/icons/批量操作.svg 34 | resources/icons/weixin.png 35 | resources/icons/录屏.svg 36 | resources/icons/合并.svg 37 | resources/icons/视频.svg 38 | resources/icons/拆分.svg 39 | resources/icons/钥匙.svg 40 | resources/icons/锁.svg 41 | resources/icons/删除.svg 42 | resources/icons/Excel.svg 43 | resources/icons/word.svg 44 | resources/icons/水印.svg 45 | resources/icons/txt.svg 46 | 47 | 48 | resources/QSS/style.qss 49 | 50 | 51 | -------------------------------------------------------------------------------- /resources/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @Time : 2024/11/29 16:48 6 | @Author : SiYuan 7 | @Email : siyuan044@qq.com 8 | @File : EasyBox-__init__.py.py 9 | @Description : 10 | """ 11 | 12 | if __name__ == '__main__': 13 | pass 14 | -------------------------------------------------------------------------------- /resources/icons/Cursors/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/resources/icons/Cursors/0.png -------------------------------------------------------------------------------- /resources/icons/Cursors/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/resources/icons/Cursors/1.png -------------------------------------------------------------------------------- /resources/icons/Cursors/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/resources/icons/Cursors/2.png -------------------------------------------------------------------------------- /resources/icons/Cursors/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/resources/icons/Cursors/3.png -------------------------------------------------------------------------------- /resources/icons/Cursors/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/resources/icons/Cursors/4.png -------------------------------------------------------------------------------- /resources/icons/Cursors/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/resources/icons/Cursors/5.png -------------------------------------------------------------------------------- /resources/icons/Cursors/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/resources/icons/Cursors/6.png -------------------------------------------------------------------------------- /resources/icons/Cursors/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/resources/icons/Cursors/7.png -------------------------------------------------------------------------------- /resources/icons/Excel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/resources/icons/__init__.py -------------------------------------------------------------------------------- /resources/icons/arrow-left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/arrow-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/back.svg: -------------------------------------------------------------------------------- 1 | 3 | 6 | 8 | -------------------------------------------------------------------------------- /resources/icons/down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/help.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/html.svg: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /resources/icons/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/resources/icons/logo.ico -------------------------------------------------------------------------------- /resources/icons/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/resources/icons/logo.png -------------------------------------------------------------------------------- /resources/icons/markdown.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/pdf.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/select.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/txt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/unselect.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/up.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/weixin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/resources/icons/weixin.png -------------------------------------------------------------------------------- /resources/icons/word.svg: -------------------------------------------------------------------------------- 1 | 3 | 5 | 7 | 9 | 10 | 12 | 14 | 16 | 18 | -------------------------------------------------------------------------------- /resources/icons/减.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/删除.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/加.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/右展开.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/合并.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/图片.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/增加.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/工具箱.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/左展开.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/录屏.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/批量操作.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/批量添加.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/拆分.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/文档转换.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/水印.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/视频.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/icons/锁.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LC044/EasyBox/974f0be3c8524109b9bce93921831ae53421e585/resources/images/logo.png -------------------------------------------------------------------------------- /screenshot/screenrecord.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import subprocess 3 | from PySide6.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QLabel 4 | 5 | class ScreenRecorderApp(QWidget): 6 | def __init__(self): 7 | super().__init__() 8 | 9 | self.setWindowTitle("Screen Recorder") 10 | self.setGeometry(200, 200, 300, 150) 11 | 12 | # 创建按钮和标签 13 | self.start_button = QPushButton("Start Recording", self) 14 | self.stop_button = QPushButton("Stop Recording", self) 15 | self.status_label = QLabel("Status: Idle", self) 16 | 17 | # 布局 18 | layout = QVBoxLayout() 19 | layout.addWidget(self.status_label) 20 | layout.addWidget(self.start_button) 21 | layout.addWidget(self.stop_button) 22 | 23 | self.setLayout(layout) 24 | 25 | # 按钮事件 26 | self.start_button.clicked.connect(self.start_recording) 27 | self.stop_button.clicked.connect(self.stop_recording) 28 | 29 | self.recording_process = None 30 | 31 | def start_recording(self): 32 | self.status_label.setText("Status: Recording...") 33 | # 使用 FFmpeg 开始屏幕录制 34 | command = [ 35 | r"E:\Project\Python\MemoTrace\app\resources\data\ffmpeg.exe", 36 | "-f", "x11grab", # X11屏幕抓取 37 | "-s", "1920x1080", # 分辨率 38 | "-i", ":0.0", # 输入设备,屏幕 39 | r"E:\Project\Python\EasyBox\screenshot\output.mp4" # 输出文件 40 | ] 41 | # 启动子进程 42 | self.recording_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 43 | self.start_button.setEnabled(False) 44 | self.stop_button.setEnabled(True) 45 | 46 | def stop_recording(self): 47 | self.status_label.setText("Status: Stopped") 48 | if self.recording_process: 49 | self.recording_process.terminate() # 终止录制进程 50 | self.recording_process = None 51 | self.start_button.setEnabled(True) 52 | self.stop_button.setEnabled(False) 53 | 54 | if __name__ == "__main__": 55 | app = QApplication(sys.argv) 56 | window = ScreenRecorderApp() 57 | window.show() 58 | sys.exit(app.exec_()) 59 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | from Cython.Build import cythonize 3 | import os 4 | 5 | 6 | def find_py_files(path): 7 | py_files = [] 8 | for root, dirs, files in os.walk(path): 9 | for file in files: 10 | if file.endswith(".py") and file != "setup.py": 11 | py_files.append(os.path.join(root, file)) 12 | return py_files 13 | 14 | 15 | py_files = find_py_files("./app/") 16 | 17 | # 如果工程名和模块无冲突,直接编译 18 | setup( 19 | ext_modules=cythonize(py_files, language_level="3") 20 | ) 21 | 22 | --------------------------------------------------------------------------------