add package and docs

This commit is contained in:
zhukang 2024-12-13 15:29:23 +08:00
parent c5850d67fd
commit c0b593958d
8 changed files with 538 additions and 9 deletions

View File

@ -1,8 +1,69 @@
# 说明
Prompt
我的目标是写一个Python程序可以通过选择任何windows应用的一段富文本点击鼠标右键后弹出一个选项点击这个选项可以把富文本保存在配置路径的.markdown文件里这个markdown会保持选中富文本的图文等相对样式不变可以正常显示。请给出完整的实现步骤和代码应用.
以管理员权限打开命令提示符,进入脚本所在目录,运行:
python app.py --install
# 应用步骤
1、激活venv的python环境.venv\Scripts\activate
2、配置config.ini文件配置保存路径
3、启动程序python main.py
4、选择需要保存的富文本右键双击
如果要卸载右键菜单,运行:
python app.py --uninstall
# 本地可执行文件打包
# 创建虚拟环境
python -m venv .venv
# 激活虚拟环境
# 对于 Windows:
source .venv/Scripts/activate
# 对于 Unix 或 MacOS:
source .venv/bin/activate
1. 项目目录结构:
llmclipboard/
├── llmclipboard/
│ ├── __init__.py
│ ├── app.py
├── config.ini
├── README.md
├── pyproject.toml
2. pyproject.toml 文件:
[project]
name = "llmclipboard"
version = "0.1.0"
description = "A text capture tool for saving formatted text from clipboard to markdown files."
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"pynput",
"pywin32",
"html2text",
"keyboard",
"configparser"
]
[project.scripts]
llmclipboard = "llmclipboard.app:main"
3. app.py 文件(确保有 main 函数)
4. 安装项目:
# 在编辑模式下安装项目
uv pip install -e .
5. 测试入口点:
llmclipboard
# 分发构建
1. 构建分发包
uv pip install build
python -m build
这将在项目根目录下生成一个 dist 目录,里面包含 .tar.gz 和 .whl 文件,这些就是你的分发包。
2. 其他python环境安装
pip install dist\llmclipboard-0.1.0.tar.gz
pip install dist\llmclipboard-0.1.0-py3-none-any.whl
3. 程序启动
llmclipboard

View File

@ -0,0 +1,87 @@
为了实现应用程序的快速启动,可以考虑以下几种方案:
### 1. 使用 PyInstaller 创建独立的可执行文件 (.exe)
- **描述**PyInstaller 可以将你的应用程序打包成一个独立的可执行文件。这个方法可以让应用程序在没有预先安装 Python 的机器上运行。
- **优点**:对终端用户来说非常方便,无需额外的设置和依赖安装。
- **缺点**:生成的可执行文件可能比较大。
### 2. 使用 `setuptools` 进行打包
- **描述**:使用 `setuptools` 定义你的包,并在 `setup.py``pyproject.toml` 文件中创建控制台脚本入口点。这个方法允许用户通过 `pip` 安装应用程序,并使其可用作命令行工具。
- **优点**:用户可以通过 `pip` 安装和升级此包。
- **缺点**:需要用户的机器上预先安装 Python 和 `pip`
### 3. 使用 Docker 将应用程序容器化
- **描述**:将应用程序打包到一个 Docker 容器中,这个容器包含所有的依赖和运行时环境。
- **优点**:在不同环境和机器间保证一致性。
- **缺点**:需要用户的机器上安装 Docker。
### 4. 创建一个 Zipapp
- **描述**Python 的 `zipapp` 模块允许你将整个应用程序打包成一个 zip 文件,且可以通过 Python 执行。
- **优点**分发简单zip 文件包含所有依赖。
- **缺点**:需要用户安装正确版本的 Python。
### 5. 使用 `shiv` 创建 PEX 文件
- **描述**`shiv` 是一个用于构建完全自包含的 Python zipappsPEP 441的工具实际应用中是单文件 Python 应用程序。
- **优点**:单文件,包含所有依赖,可以直接用 Python 执行。
- **缺点**:需要用户安装正确版本的 Python。
### 6. 平台特定安装程序
- **描述**:使用例如 Windows 的 WiX Toolset 等工具创建平台特定的安装程序(例如 Windows MSImacOS DMG
- **优点**:用户友好的安装体验。
- **缺点**:需要为每个平台创建和维护不同的安装程序。
### 7. 使用 `pkg_resources` 进行运行时依赖管理
- **描述**:如果你更倾向于在运行时安装依赖,可以使用 `pkg_resources` 或类似工具来管理应用程序内部的依赖。然而由于复杂性和潜在的运行时错误,这种方法一般不推荐。
- **优点**:对用户来说设置最小化。
- **缺点**:高复杂性和潜在的运行时问题。
### 总结:
- **为了便于分发和用户友好性**,使用 PyInstaller 创建独立的可执行文件是一个常见选择。
- **对于更 Pythonic 的方法**,利用 `setuptools` 创建一个控制台脚本入口点,并通过 `pip` 安装会更好。
- **为了环境一致性和高级部署能力**,使用 Docker 是非常有益的。
- **在分发简便性和依赖管理之间取得平衡**,使用 `shiv` 创建的 zipapp 提供了一个好的折中方案。
每种方法都有其使用场景和优点,具体选择取决于你的应用程序和用户的需求和限制。

View File

@ -0,0 +1,50 @@
使用方案2即使用 `setuptools` 对应用进行打包,并创建一个控制台脚本入口点,具体步骤如下:
### 具体步骤:
1. **准备项目目录结构**
- 确保项目目录结构规范,包含必要的文件,如 `app.py`、`__init__.py`、`setup.py` 或 `pyproject.toml`、`README.md` 等。
- 添加文件 `__init__.py` 以使目录被识别为 Python 包。
2. **创建 `setup.py` 或更新 `pyproject.toml`**
- 如果使用 `setup.py`,需要定义项目的元数据和依赖项,同时指定入口点。
- 如果使用 `pyproject.toml`,同样需要定义元数据和依赖项,并在 `[project.scripts]` 中声明入口点。
3. **配置入口点**
-`setup.py``pyproject.toml` 中设置控制台脚本入口点,使用户可以通过命令行启动应用程序。
4. **更新应用主模块**
- 确保在应用的主模块(如 `app.py`)中有一个 `main` 函数,作为入口点函数。
5. **创建并激活虚拟环境**
- 使用 `venv` 模块创建并激活一个虚拟环境以进行隔离和管理依赖。
6. **安装项目**
- 通过 `pip install -e .` 命令在编辑模式下安装项目。这会根据 `setup.py``pyproject.toml` 中的配置,建立项目到虚拟环境的符号链接,并自动添加控制台脚本。
7. **测试入口点**
- 安装完成后,确保在命令行中可以使用项目名称启动应用,并验证功能是否正常。
8. **编写 `README.md` 文件**
- 添加使用说明和安装步骤,以便用户能够快速上手使用该应用。
以上步骤中所用到的文件和目录可能包括:
```
llmclipboard/
├── llmclipboard/
│ ├── __init__.py
│ ├── app.py
├── config.ini
├── README.md
├── pyproject.toml (或 setup.py
```
### 详细步骤实现:
- 编写 `setup.py``pyproject.toml` 文件包含项目元数据。
-`pyproject.toml` 中使用 `[project.scripts]` 声明控制台入口点,或者在 `setup.py` 中使用 `entry_points` 参数进行配置。
-确保 `app.py` 中具有 `main` 函数,该函数作为入口点函数。
- 创建虚拟环境并激活,安装项目。
- 测试入口点调用以确保其正常工作。
通过这些步骤,你的应用程序可以打包成一个可通过命令行快速启动的工具,用户只需安装一次,即可通过配置的命令启动应用程序。

View File

@ -0,0 +1,200 @@
在打包后的命令中,配置文件 `config.ini` 可以有几种处理方式。我们处理配置文件的方法包括:
1. **将 config.ini 文件作为项目的一部分进行分发**
确保配置文件在项目结构中的正确位置。
2. **在代码中指定配置文件的路径**
通过代码确保应用程序能够找到并加载配置文件。
3. **提供默认配置文件**
在缺失时创建配置文件,确保应用程序有初始配置。
### 详细步骤:
1. **项目目录结构**
确保 `config.ini` 文件和应用程序的入口文件在同级目录中,如下所示:
```
llmclipboard/
├── llmclipboard/
│ ├── __init__.py
│ ├── app.py
├── config.ini
├── README.md
├── pyproject.toml
```
2. **在 `app.py` 中加载配置文件**
修改 `load_config` 方法,确保程序能找到 `config.ini` 文件,通常可以使用相对路径或环境变量。
### 示例代码实现:
**项目目录结构**
```
llmclipboard/
├── llmclipboard/
│ ├── __init__.py
│ ├── app.py
├── config.ini
├── README.md
├── pyproject.toml
```
**在 `app.py` 中加载配置文件**
```python project/llmclipboard/app.py
import os
import time
import configparser
from pynput import mouse
import win32clipboard
import win32con
from html2text import html2text
import keyboard
import threading
import sys
import logging
class TextCaptureApp:
def __init__(self):
self.load_config()
self.running = True
self.last_right_click_time = 0
self.setup_logging()
def load_config(self):
"""Loads the configuration file."""
config = configparser.ConfigParser()
config_path = os.path.join(os.path.dirname(__file__), '..', 'config.ini')
if not os.path.isfile(config_path):
self.logger.error(f"配置文件未找到: {config_path}")
sys.exit(1)
config.read(config_path)
self.double_click_threshold = config.getfloat('Settings', 'double_click_threshold', fallback=0.3)
self.save_location = config.get('Settings', 'save_location', fallback=os.path.join(os.path.expanduser('~'), 'Desktop'))
self.copy_delay = config.getfloat('Settings', 'copy_delay', fallback=0.1)
def setup_logging(self):
logging.basicConfig(
filename='text_capture.log',
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
self.logger = logging.getLogger(__name__)
def save_to_markdown(self, content):
try:
timestamp = time.strftime("%Y%m%d_%H%M%S")
file_path = os.path.join(self.save_location, f'captured_text_{timestamp}.md')
with open(file_path, 'w', encoding='utf-8') as f:
f.write(content)
self.logger.info(f"Content saved to: {file_path}")
except Exception as e:
self.logger.error(f"Error saving file: {e}")
def get_clipboard_content(self):
try:
win32clipboard.OpenClipboard()
try:
content = win32clipboard.GetClipboardData(win32con.CF_HTML)
self.logger.info("Clipboard HTML content captured")
return html2text(content)
except:
pass
try:
content = win32clipboard.GetClipboardData(win32con.CF_TEXT)
self.logger.info("Clipboard TEXT content captured")
return content.decode('gbk')
except:
pass
try:
content = win32clipboard.GetClipboardData(win32con.CF_UNICODETEXT)
self.logger.info("Clipboard UNICODE TEXT content captured")
return content
except:
return None
except Exception as e:
self.logger.error(f"Error getting clipboard content: {e}")
return None
finally:
try:
win32clipboard.CloseClipboard()
except Exception as e:
self.logger.error(f"Error closing clipboard: {e}")
def simulate_copy(self):
keyboard.press('ctrl')
keyboard.press('c')
keyboard.release('c')
keyboard.release('ctrl')
time.sleep(self.copy_delay) # 等待复制操作完成
def on_click(self, x, y, button, pressed):
if not pressed:
return
if button == mouse.Button.right:
current_time = time.time()
if (current_time - self.last_right_click_time) < self.double_click_threshold:
self.simulate_copy()
content = self.get_clipboard_content()
if content:
self.save_to_markdown(content)
else:
self.logger.warning("No content captured from clipboard")
self.last_right_click_time = current_time
def on_keyboard_event(self, event):
# 监听Esc键退出程序
if event.name == 'esc':
self.running = False
return False
def run(self):
self.logger.info("Text Capture App started")
print("文本捕获程序已启动...")
print("使用说明:")
print("1. 选中要保存的文本")
print("2. 快速双击鼠标右键来保存选中的文本")
print("3. 按ESC键退出程序")
mouse_listener = mouse.Listener(on_click=self.on_click)
mouse_listener.start()
# 启动键盘监听
keyboard.on_press(self.on_keyboard_event)
# Wait until the running flag becomes False
while self.running:
time.sleep(0.1)
print("\n程序已退出")
mouse_listener.stop()
mouse_listener.join()
keyboard_listener.stop()
keyboard_listener.join()
self.logger.info("Text Capture App stopped")
def main():
try:
app = TextCaptureApp()
app.run()
except Exception as e:
logging.error(f"程序发生错误: {e}")
input("按Enter键退出...")
if __name__ == "__main__":
main()
```
### 其他注意事项:
- **配置文件路径**:在 `load_config` 方法中使用 `os.path` 来获取配置文件的路径,并确保路径的正确性。
- **默认配置**:如果配置文件丢失,可以选择在初始化时生成一个默认的配置文件,确保应用程序能够正常启动。
- **文档说明**:在 `README.md` 文件中添加关于如何找到和编辑 `config.ini` 文件的说明,以帮助用户自定义他们的配置。
通过以上步骤, `config.ini` 文件将在包安装过程中被正确识别,并能在应用程序运行时被读取和加载。

View File

@ -0,0 +1,119 @@
要将打包后的应用程序在其他 Python 环境中安装,可以执行以下步骤:
### 1. 创建安装包并上传到 PyPI可选
如果你不想上传到 PyPI可以直接跳过此步骤直接创建安装包并手动分发。
### 2. 构建分发包
你可以使用 `build` 工具来创建一个源码分发包(`sdist`)和一个 Wheel 分发包(`bdist_wheel`)。
首先,确保你已经安装了 `build` 工具:
```sh
pip install build
```
然后,在项目根目录下运行以下命令来构建分发包:
```sh
python -m build
```
这将在项目根目录下生成一个 `dist` 目录,里面包含 `.tar.gz``.whl` 文件,这些就是你的分发包。
### 3. 在目标环境中安装分发包
你可以将分发包(`.tar.gz` 或 `.whl` 文件)复制到目标环境机器上,然后使用 `pip` 安装:
```sh
pip install path/to/your-package.whl
```
或者:
```sh
pip install path/to/your-package.tar.gz
```
### 详细步骤示例
假设你的项目目录结构如下:
```
llmclipboard/
├── llmclipboard/
│ ├── __init__.py
│ ├── app.py
├── config.ini
├── README.md
├── pyproject.toml
```
### 项目打包
确保 `pyproject.toml` 文件如下配置:
```toml project/llmclipboard/pyproject.toml
[project]
name = "llmclipboard"
version = "0.1.0"
description = "A text capture tool for saving formatted text from clipboard to markdown files."
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"pynput",
"pywin32",
"html2text",
"keyboard",
"configparser",
"logging"
]
[project.scripts]
llmclipboard = "llmclipboard.app:main"
```
### 构建分发包
在项目根目录下运行以下命令:
```sh
python -m build
```
这将在项目根目录下生成一个 `dist` 目录,里面包含构建好的分发包, 如:
```
dist/
│── llmclipboard-0.1.0.tar.gz
│── llmclipboard-0.1.0-py3-none-any.whl
```
### 在目标环境中安装
你可以将这些分发包文件复制到目标环境的机器上,然后运行以下命令进行安装:
```sh
pip install llmclipboard-0.1.0-py3-none-any.whl
```
或者安装源码分发包:
```sh
pip install llmclipboard-0.1.0.tar.gz
```
### 使用打包后的应用
安装完成后,在目标环境的命令行中运行以下命令启动应用程序:
```sh
llmclipboard
```
### 注意事项
1. **配置文件路径**:在目标环境中,配置文件 `config.ini` 应该与应用程序一起分发,可以通过如下方式确保应用知道配置文件的位置:
- 可以在程序启动时通过环境变量或命令行参数指定配置文件路径。
- 修改代码,让应用在启动时在相对路径或用户目录下寻找配置文件。
2. **依赖安装**:确保目标环境已经安装所有依赖包。部署时可以为目标环境创建一个新的虚拟环境,然后在其中安装应用分发包。
3. **适配不同平台**:考虑到 Python 环境可能跨多个操作系统,确保在不同平台上进行足够的测试,尤其是涉及到特定平台的库(如 `pywin32`)。
通过以上步骤,你可以在其他 Python 环境中安装和运行打包后的应用程序。

View File

@ -129,11 +129,14 @@ class TextCaptureApp:
keyboard_listener.stop()
keyboard_listener.join()
self.logger.info("Text Capture App stopped")
if __name__ == "__main__":
def main():
try:
app = TextCaptureApp()
app.run()
except Exception as e:
logging.error(f"程序发生错误: {e}")
input("按Enter键退出...")
input("按Enter键退出...")
if __name__ == "__main__":
main()

View File

@ -1,7 +1,16 @@
[project]
name = "llmclipboard"
version = "0.1.0"
description = "Add your description here"
description = "A text capture tool for saving formatted text from clipboard to markdown files."
readme = "README.md"
requires-python = ">=3.10"
dependencies = []
dependencies = [
"pynput",
"pywin32",
"html2text",
"keyboard",
"configparser"
]
[project.scripts]
llmclipboard = "llmclipboard.app:main"