【PyTorch项目实战】OpenNMT本地机器翻译框架 —— 支持本地部署和自定义训练

文章目录

  • 一、OpenNMT(Neural Machine Translation,NMT)
    • 1. 概述
    • 2. 核心特性
    • 3. 系统架构
    • 4. 与其他翻译工具的区别
  • 二、基于 OpenNMT-py 的机器翻译框架
    • 1. 环境配置(以OpenNMT-py版本为例)
      • (1)pip安装:pip install OpenNMT-py
      • (2)源码安装
    • 2. 模型选择
  • 三、通过 Hugging Face 调用 MarianMT 模型
    • (1)环境配置
    • (2)本地翻译工具 —— 输入固定文本
    • (3)本地翻译工具 —— 支持多种文档输入(TXT、PDF、Word)
    • (4)本地翻译工具(图形界面版本) —— 只支持文本输入
    • (5)本地翻译工具(图形界面版本) —— 支持文本输入 + 多种文档输入(TXT、PDF、Word)

一、OpenNMT(Neural Machine Translation,NMT)

官方网站:https://opennmt.net/

1. 概述

OpenNMT(Open Neural Machine Translation)是一个开源的神经机器翻译(NMT)框架,最初由 Harvard NLP 实验室与 Systran 团队合作开发,目标是提供高性能、可扩展、可定制、可本地部署翻译模型训练与推理工具,既适合科研,也适合工业应用。

需要注意:

  • OpenNMT不是桌面翻译软件
    • 它本身没有GUI界面,也没有即点即用的翻译功能。
    • 功能核心是训练、评估和推理神经机器翻译模型。
    • 用户需要通过命令行或Python API来调用模型完成翻译任务。
  • OpenNMT没有官方预训练模型
    • 框架只提供模型结构(Transformer、LSTM、GRU)、训练与推理工具。
    • 要进行实际翻译,需要用户:自行训练模型(用平行语料)或下载第三方社区预训练模型(Hugging Face、GitHub等)
    • 官方仓库提供的示例模型仅供学习和验证流程,通常翻译质量有限。

2. 核心特性

特性详细说明
开源协议采用 MIT License,完全免费,可商用,无使用限制。
部署方式支持本地离线部署(不依赖云服务),也可在服务器、集群、GPU加速环境运行。
语言支持理论上支持任意语言对,只要提供平行语料;可处理翻译、文本生成、摘要等任务。
多框架支持提供两个实现版本:
OpenNMT-py(PyTorch版)——更新活跃、易于定制。
OpenNMT-tf(TensorFlow版)——适合生产部署。
模型架构支持多种深度学习结构:
Transformer(高精度主流架构)
LSTM / GRU(适合低资源环境)
③ CNN encoder-decoder(实验性)
扩展性可自定义数据预处理、分词、词表、模型层数、注意力机制、损失函数等。
训练模式支持从零训练、增量训练(fine-tuning)、迁移学习、多GPU分布式训练。
推理模式支持批量翻译、交互式翻译、服务器API模式(REST API / gRPC)。
性能优化支持混合精度训练(FP16)、动态batch、beam search解码、缓存机制。

3. 系统架构

OpenNMT采用典型的Encoder-Decoder结构:

[输入文本][分词/子词编码][Encoder][Attention][Decoder][输出文本]"""
Encoder(编码器):将源语言文本转为上下文表示(embedding序列)。
Attention(注意力机制):在解码时动态关注源文本的不同部分。
Decoder(解码器):生成目标语言文本,逐步输出token。
Beam Search:在解码过程中保留多个候选,提高翻译质量。
"""

4. 与其他翻译工具的区别

工具部署方式模型可定制性是否需联网适合人群
Google Translate云端API不可定制必须联网普通用户
DeepL云端API/客户端不可定制必须联网普通用户
Marian NMT本地部署可离线开发者、研究者
OpenNMT本地部署非常高可离线研究人员、企业

二、基于 OpenNMT-py 的机器翻译框架

1. 环境配置(以OpenNMT-py版本为例)

https://github.com/OpenNMT/OpenNMT-py

# 基本环境:
Python >= 3.8
PyTorch >= 2.0 <2.2# 虚拟环境配置:
conda create -n opennmt -y
conda activate opennmt

(1)pip安装:pip install OpenNMT-py

(2)源码安装

git clone https://github.com/OpenNMT/OpenNMT-py.git
cd OpenNMT-py
pip install -e .
###############################################################
# (1)部分高级功能(例如工作预处理的模型或特定的转换)需要额外的包装
# pip install -r requirements.opt.txt
###############################################################
# (2)强烈建议手动安装APEX,其具有快速的性能(尤其是传统的Fusedadam Optimizer和Fusedrmsnorm)
# git clone https://github.com/NVIDIA/apex
# cd apex
# pip3 install -v --no-build-isolation --config-settings --build-option="--cpp_ext --cuda_ext --deprecated_fused_adam --xentropy --fast_multihead_attn" ./
# cd ..
###############################################################

2. 模型选择

通过 Hugging Face 下载开源模型

分类情况说明
官方OpenNMT 官方仓库不直接发布预训练权重,只给出示例数据(如 WMT 数据集),示例模型只是演示用,翻译效果有限。
社区社区有人在 Hugging Face、GitHub 上传基于 OpenNMT 训练好的权重(例如英-法、英-中等),但质量和数据来源需要自己验证。
替代方案如果想直接用现成的高质量模型,可以考虑 MarianMT(Hugging Face)、M2M-100(Facebook)等,它们都是开箱即用的翻译模型,不必自己训练。
本地部署如果一定要本地离线运行高质量翻译模型,可以通过 Hugging Face Transformers 加载 MarianMT 或 M2M-100,再结合 OpenNMT 的数据处理流程做微调。

三、通过 Hugging Face 调用 MarianMT 模型

下面给出一个基于 Hugging Face Transformers + MarianMT(或 M2M100)的本地论文翻译完整流程无需依赖 OpenNMT 的自行训练,可以直接离线推理

模型类型模型示例(Hugging Face)支持语种优点缺点
MarianMTHelsinki-NLP/opus-mt-en-zh双语对(英2中)速度快、模型小、易部署需要为每个语对下载单独模型
M2M100facebook/m2m100_418M多语种一个模型支持多语言占用显存和磁盘较大

(1)环境配置

# 虚拟环境配置:
conda create -n translation -y
conda activate translation
############################################
cd translation
Python >= 3.8
# CPU:pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple
# GPU:pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
pip install transformers sentencepiece PyMuPDF python-docx reportlab -i https://pypi.tuna.tsinghua.edu.cn/simple"""说明:
torch				PyTorch 深度学习框架,模型推理
transformers		Hugging Face 模型库
sentencepiece		分词器,支持 BPE 模型
PyMuPDF	PDF 		文本提取
python-docx	Word 	文档处理
reportlab			PDF 写入生成
"""

(2)本地翻译工具 —— 输入固定文本

import os
import torch
from transformers import MarianMTModel, MarianTokenizerMODEL_NAME = "Helsinki-NLP/opus-mt-en-zh"  # 英->中
LOCAL_MODEL_DIR = r"./opus_mt_model"        # 本地模型存放目录device = "cuda" if torch.cuda.is_available() else "cpu"def is_model_complete(model_dir):"""检查本地模型目录是否包含必要文件"""required_files = ["config.json", "source.spm", "target.spm", "pytorch_model.bin"]return all(os.path.exists(os.path.join(model_dir, f)) for f in required_files)def load_model():"""加载本地 MarianMT 模型,如果缺失则自动下载。返回:tokenizer, model"""global deviceif os.path.exists(LOCAL_MODEL_DIR) and is_model_complete(LOCAL_MODEL_DIR):print(f"检测到完整本地模型:{LOCAL_MODEL_DIR},加载中...")tokenizer = MarianTokenizer.from_pretrained(LOCAL_MODEL_DIR)model = MarianMTModel.from_pretrained(LOCAL_MODEL_DIR).to(device)else:print("本地模型不完整或不存在,开始下载 MarianMT 模型...")tokenizer = MarianTokenizer.from_pretrained(MODEL_NAME, cache_dir=LOCAL_MODEL_DIR)model = MarianMTModel.from_pretrained(MODEL_NAME, cache_dir=LOCAL_MODEL_DIR).to(device)print(f"模型已下载完成,可将目录 '{LOCAL_MODEL_DIR}' 移动到脚本目录实现离线加载。")return tokenizer, model# -----------------------------
# 测试加载
# -----------------------------
if __name__ == "__main__":tokenizer, model = load_model()text = ["Deep learning significantly improves image recognition performance."]inputs = tokenizer(text, return_tensors="pt", padding=True).to(device)translated = model.generate(**inputs)print("翻译结果:", [tokenizer.decode(t, skip_special_tokens=True) for t in translated])
模型下载后的文件目录
# opus_mt_model/
# ├─ models--Helsinki-NLP--opus-mt-en-zh/
# │    ├─ blobs/
# │    ├─ refs/
# │    └─ snapshots/
# │         └─ 408d9bc410a388e1d9aef112a2daba955b945255/
# │              ├─ config.json
# │              ├─ generation_config.json
# │              ├─ pytorch_model.bin
# │              ├─ source.spm
# │              ├─ target.spm
# │              ├─ tokenizer_config.json
# │              └─ vocab.json"""将【路径1】中所有文件复制或剪贴到【路径2】下,即可实现自动识别本地模型。
【路径1】LOCAL_MODEL_DIR = r".\opus_mt_model\models--Helsinki-NLP--opus-mt-en-zh\snapshots\408d9bc410a388e1d9aef112a2daba955b945255"
【路径2】LOCAL_MODEL_DIR = r"./opus_mt_model"
"""

(3)本地翻译工具 —— 支持多种文档输入(TXT、PDF、Word)

#####################################################################################
# 本地论文翻译工具(支持 TXT / PDF / Word)
# 功能概述:
# 1. 模型管理:自动检测本地 MarianMT 模型,首次运行会下载模型,并提示移动到脚本目录以实现离线使用
# 2. 运行环境:自动检测 GPU/CPU,GPU 可加速推理,CPU 可运行但速度较慢
# 3. 输入输出格式:支持 TXT、PDF、Word,输出与输入格式保持一致
# 4. 批量处理:可在 input_files 中添加多个文件,循环处理
# 5. 可扩展性:
#    - 可替换 MarianMT 为 M2M100,实现多语种翻译
#    - 可增加 PDF 原排版保留功能,保持段落、标题和表格样式
#####################################################################################import os
import torch
from transformers import MarianMTModel, MarianTokenizer# PDF/Word 处理
import fitz  # PyMuPDF
from docx import Document
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter# -----------------------------
# 1. 配置模型
# -----------------------------
MODEL_NAME = "Helsinki-NLP/opus-mt-en-zh"  # 英->中
LOCAL_MODEL_DIR = "./opus_mt_model"        # 本地模型存放目录device = "cuda" if torch.cuda.is_available() else "cpu"# -----------------------------
# 2. 加载模型
# -----------------------------
def load_model():global tokenizer, model# 检查本地模型是否存在if os.path.exists(LOCAL_MODEL_DIR):print(f"加载本地模型: {LOCAL_MODEL_DIR}")tokenizer = MarianTokenizer.from_pretrained(LOCAL_MODEL_DIR)model = MarianMTModel.from_pretrained(LOCAL_MODEL_DIR).to(device)else:print("本地模型不存在,首次运行需要下载模型,请耐心等待...")tokenizer = MarianTokenizer.from_pretrained(MODEL_NAME, cache_dir=LOCAL_MODEL_DIR)model = MarianMTModel.from_pretrained(MODEL_NAME, cache_dir=LOCAL_MODEL_DIR).to(device)print(f"模型已下载到默认缓存目录,请将 {LOCAL_MODEL_DIR} 移动到当前脚本目录,以便离线使用。")return tokenizer, model# -----------------------------
# 3. 文本翻译函数
# -----------------------------
def translate_text(text_list, batch_size=8):results = []for i in range(0, len(text_list), batch_size):batch = text_list[i:i+batch_size]inputs = tokenizer(batch, return_tensors="pt", padding=True, truncation=True).to(device)translated = model.generate(**inputs, max_length=1024)decoded = [tokenizer.decode(t, skip_special_tokens=True) for t in translated]results.extend(decoded)return results# -----------------------------
# 4. PDF 文本提取/保存
# -----------------------------
def pdf_to_text(pdf_path):doc = fitz.open(pdf_path)paragraphs = []for page in doc:paragraphs.extend(page.get_text("text").split("\n"))paragraphs = [p.strip() for p in paragraphs if p.strip()]return paragraphsdef text_to_pdf(text_list, out_pdf_path):c = canvas.Canvas(out_pdf_path, pagesize=letter)width, height = lettery = height - 50for para in text_list:lines = []while len(para) > 0:lines.append(para[:100])para = para[100:]for line in lines:c.drawString(50, y, line)y -= 15if y < 50:c.showPage()y = height - 50c.save()# -----------------------------
# 5. Word 文本提取/保存
# -----------------------------
def docx_to_text(docx_path):doc = Document(docx_path)paragraphs = [p.text.strip() for p in doc.paragraphs if p.text.strip()]return paragraphsdef text_to_docx(text_list, out_docx_path):doc = Document()for para in text_list:doc.add_paragraph(para)doc.save(out_docx_path)# -----------------------------
# 6. TXT 文本处理
# -----------------------------
def txt_to_text(txt_path):with open(txt_path, "r", encoding="utf-8") as f:paragraphs = [line.strip() for line in f.readlines() if line.strip()]return paragraphsdef text_to_txt(text_list, out_txt_path):with open(out_txt_path, "w", encoding="utf-8") as f:for para in text_list:f.write(para + "\n")# -----------------------------
# 7. 主函数:翻译文件
# -----------------------------
def translate_file(input_path, output_path=None):ext = os.path.splitext(input_path)[1].lower()if ext == ".pdf":paragraphs = pdf_to_text(input_path)elif ext in [".docx", ".doc"]:paragraphs = docx_to_text(input_path)elif ext in [".txt"]:paragraphs = txt_to_text(input_path)else:raise ValueError(f"不支持的文件格式: {ext}")# 翻译文本translated_paras = translate_text(paragraphs)# 输出路径if output_path is None:base, extname = os.path.splitext(input_path)output_path = f"{base}_translated{extname}"# 根据类型保存if ext == ".pdf":text_to_pdf(translated_paras, output_path)elif ext in [".docx", ".doc"]:text_to_docx(translated_paras, output_path)elif ext == ".txt":text_to_txt(translated_paras, output_path)print(f"翻译完成,已保存至: {output_path}")# -----------------------------
# 8. 示例运行
# -----------------------------
if __name__ == "__main__":load_model()  # 加载或下载模型input_files = ["../paper/example.pdf","../paper/example.docx","../paper/example.txt"]for file in input_files:if os.path.exists(file):translate_file(file)else:print(f"文件不存在: {file}")

(4)本地翻译工具(图形界面版本) —— 只支持文本输入

在这里插入图片描述

"""
功能特点
首次运行联网下载模型,下次启动直接用本地缓存。
离线可用,支持 GPU/CPU 自动切换。
实时翻译(键盘停止输入 0.5 秒自动翻译)。
双栏布局:左输入、右输出,方便对照。
批量翻译段落(按换行分段)。
"""
import os
import sys
import torch
from PyQt5.QtWidgets import (QApplication, QWidget, QHBoxLayout, QVBoxLayout, QTextEdit,QPushButton, QLabel, QSplitter
)
from PyQt5.QtCore import Qt
from transformers import MarianMTModel, MarianTokenizer# -----------------------------
# 配置模型
# -----------------------------
MODEL_NAME = "Helsinki-NLP/opus-mt-en-zh"
LOCAL_MODEL_DIR = r"./opus_mt_model"
device = "cuda" if torch.cuda.is_available() else "cpu"# -----------------------------
# 加载模型
# -----------------------------
def load_model():global tokenizer, modelif os.path.exists(LOCAL_MODEL_DIR):print(f"加载本地模型: {LOCAL_MODEL_DIR}")tokenizer = MarianTokenizer.from_pretrained(LOCAL_MODEL_DIR)model = MarianMTModel.from_pretrained(LOCAL_MODEL_DIR).to(device)else:print("首次运行,正在下载模型...")tokenizer = MarianTokenizer.from_pretrained(MODEL_NAME, cache_dir=LOCAL_MODEL_DIR)model = MarianMTModel.from_pretrained(MODEL_NAME, cache_dir=LOCAL_MODEL_DIR).to(device)print(f"模型已下载到缓存: {LOCAL_MODEL_DIR}")return tokenizer, model# -----------------------------
# 翻译函数
# -----------------------------
def translate_text(text_list, batch_size=8):results = []for i in range(0, len(text_list), batch_size):batch = text_list[i:i+batch_size]inputs = tokenizer(batch, return_tensors="pt", padding=True, truncation=True).to(device)translated = model.generate(**inputs, max_length=1024)decoded = [tokenizer.decode(t, skip_special_tokens=True) for t in translated]results.extend(decoded)return results# -----------------------------
# GUI 界面
# -----------------------------
class TranslatorGUI(QWidget):def __init__(self):super().__init__()self.init_ui()def init_ui(self):self.setWindowTitle("本地翻译工具")self.resize(1200, 700)# 主布局main_layout = QVBoxLayout(self)# 分割器实现左右布局splitter = QSplitter(Qt.Horizontal)# 左侧:输入文本left_widget = QWidget()left_layout = QVBoxLayout(left_widget)self.input_label = QLabel("输入文本(英文):")self.input_label.setStyleSheet("font: 14pt '微软雅黑';")left_layout.addWidget(self.input_label)self.input_box = QTextEdit()self.input_box.setStyleSheet("font: 14pt 'Consolas';")left_layout.addWidget(self.input_box)splitter.addWidget(left_widget)# 右侧:输出文本right_widget = QWidget()right_layout = QVBoxLayout(right_widget)self.output_label = QLabel("翻译结果(中文):")self.output_label.setStyleSheet("font: 14pt '微软雅黑';")right_layout.addWidget(self.output_label)self.output_box = QTextEdit()self.output_box.setStyleSheet("font: 14pt 'Consolas';")self.output_box.setReadOnly(True)right_layout.addWidget(self.output_box)splitter.addWidget(right_widget)splitter.setSizes([600, 600])main_layout.addWidget(splitter)# 按钮布局self.translate_btn = QPushButton("翻译 (Ctrl+Enter)")self.translate_btn.setStyleSheet("font: 12pt '微软雅黑'; padding:8px;")self.translate_btn.clicked.connect(self.on_translate)main_layout.addWidget(self.translate_btn)# 快捷键self.input_box.keyPressEvent = self.key_press_eventdef key_press_event(self, event):# Ctrl+Enter 翻译if event.key() == Qt.Key_Return and (event.modifiers() & Qt.ControlModifier):self.on_translate()else:QTextEdit.keyPressEvent(self.input_box, event)def on_translate(self):src_text = self.input_box.toPlainText().strip()if not src_text:returnparagraphs = [line.strip() for line in src_text.split("\n") if line.strip()]translated_paras = translate_text(paragraphs)self.output_box.setPlainText("\n".join(translated_paras))# -----------------------------
# 主程序
# -----------------------------
if __name__ == "__main__":tokenizer, model = load_model()app = QApplication(sys.argv)gui = TranslatorGUI()gui.show()sys.exit(app.exec_())

(5)本地翻译工具(图形界面版本) —— 支持文本输入 + 多种文档输入(TXT、PDF、Word)

在这里插入图片描述

  • 不支持保存为pdf格式
  • 加载文档的翻译模式(局限性),是将其转换为文本,然后翻译。
  • 文本/文档翻译是有输入上限的,因此需要分阶段/拆分翻译!!!
  • MarianMTModel不支持图像中的文字翻译
import sys
import os
import torch
from PyQt5.QtWidgets import (QApplication, QWidget, QHBoxLayout, QVBoxLayout, QTextEdit,QPushButton, QLabel, QSplitter, QFileDialog, QMessageBox,QProgressBar
)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from transformers import MarianMTModel, MarianTokenizer
from docx import Document
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont# -----------------------------
# 模型配置
# -----------------------------
MODEL_NAME = "Helsinki-NLP/opus-mt-en-zh"
LOCAL_MODEL_DIR = r"./opus_mt_model"
device = "cuda" if torch.cuda.is_available() else "cpu"# -----------------------------
# 加载模型
# -----------------------------
def load_model():global tokenizer, modelif os.path.exists(LOCAL_MODEL_DIR):print(f"加载本地模型: {LOCAL_MODEL_DIR}")tokenizer = MarianTokenizer.from_pretrained(LOCAL_MODEL_DIR)model = MarianMTModel.from_pretrained(LOCAL_MODEL_DIR).to(device)else:print("首次运行,正在下载模型...")tokenizer = MarianTokenizer.from_pretrained(MODEL_NAME, cache_dir=LOCAL_MODEL_DIR)model = MarianMTModel.from_pretrained(MODEL_NAME, cache_dir=LOCAL_MODEL_DIR).to(device)print(f"模型已下载到缓存: {LOCAL_MODEL_DIR}")return tokenizer, model# -----------------------------
# 文本翻译函数(支持批量)
# -----------------------------
def translate_text(text_list, batch_size=8, progress_callback=None):results = []total = len(text_list)for i in range(0, total, batch_size):batch = text_list[i:i+batch_size]inputs = tokenizer(batch, return_tensors="pt", padding=True, truncation=True).to(device)translated = model.generate(**inputs, max_length=1024)decoded = [tokenizer.decode(t, skip_special_tokens=True) for t in translated]results.extend(decoded)if progress_callback:progress_callback(min(i+batch_size, total), total)return results# -----------------------------
# 文档处理函数
# -----------------------------
def docx_to_text(docx_path):doc = Document(docx_path)return [p.text.strip() for p in doc.paragraphs if p.text.strip()]def text_to_docx(text_list, out_docx_path):doc = Document()for para in text_list:doc.add_paragraph(para)doc.save(out_docx_path)def txt_to_text(txt_path):with open(txt_path, "r", encoding="utf-8") as f:return [line.strip() for line in f.readlines() if line.strip()]def text_to_txt(text_list, out_txt_path):with open(out_txt_path, "w", encoding="utf-8") as f:for para in text_list:f.write(para + "\n")def pdf_to_text(pdf_path):try:import fitzdoc = fitz.open(pdf_path)paragraphs = []for page in doc:paragraphs.extend(page.get_text("text").split("\n"))return [p.strip() for p in paragraphs if p.strip()]except Exception:return []def text_to_pdf(text_list, out_pdf_path):try:# 注册中文字体(需系统存在SimSun.ttf或替换为其他)pdfmetrics.registerFont(TTFont("SimSun", "SimSun.ttf"))c = canvas.Canvas(out_pdf_path, pagesize=A4)width, height = A4y = height - 50for para in text_list:lines = []# 按长度换行,防止文字超出页面while len(para) > 0:lines.append(para[:50])para = para[50:]for line in lines:c.setFont("SimSun", 12)c.drawString(50, y, line)y -= 18if y < 50:c.showPage()y = height - 50c.save()except Exception as e:raise RuntimeError(f"PDF保存失败: {e}")# -----------------------------
# 翻译线程
# -----------------------------
class TranslateThread(QThread):progress_signal = pyqtSignal(int, int)finished_signal = pyqtSignal(list)def __init__(self, paragraphs):super().__init__()self.paragraphs = paragraphsdef run(self):translated = translate_text(self.paragraphs, batch_size=8, progress_callback=self.emit_progress)self.finished_signal.emit(translated)def emit_progress(self, current, total):self.progress_signal.emit(current, total)# -----------------------------
# PyQt GUI
# -----------------------------
class TranslatorGUI(QWidget):def __init__(self):super().__init__()self.init_ui()def init_ui(self):self.setWindowTitle("本地翻译工具")self.resize(1200, 700)main_layout = QVBoxLayout(self)splitter = QSplitter(Qt.Horizontal)# 左侧输入left_widget = QWidget()left_layout = QVBoxLayout(left_widget)self.input_label = QLabel("输入文本(英文):")self.input_label.setStyleSheet("font: 14pt '微软雅黑';")left_layout.addWidget(self.input_label)self.input_box = QTextEdit()self.input_box.setStyleSheet("font: 14pt 'Consolas';")left_layout.addWidget(self.input_box)splitter.addWidget(left_widget)# 右侧输出right_widget = QWidget()right_layout = QVBoxLayout(right_widget)self.output_label = QLabel("翻译结果(中文):")self.output_label.setStyleSheet("font: 14pt '微软雅黑';")right_layout.addWidget(self.output_label)self.output_box = QTextEdit()self.output_box.setStyleSheet("font: 14pt 'Consolas';")self.output_box.setReadOnly(False)  # 可编辑right_layout.addWidget(self.output_box)splitter.addWidget(right_widget)splitter.setSizes([600, 600])main_layout.addWidget(splitter)# 进度条self.progress_bar = QProgressBar()self.progress_bar.setValue(0)main_layout.addWidget(self.progress_bar)# 按钮布局btn_layout = QHBoxLayout()self.translate_btn = QPushButton("翻译 (Ctrl+Enter)")self.translate_btn.setStyleSheet("font: 12pt '微软雅黑'; padding:8px;")self.translate_btn.clicked.connect(self.on_translate)btn_layout.addWidget(self.translate_btn)self.load_doc_btn = QPushButton("加载文档 (.pdf/.docx/.txt)")self.load_doc_btn.setStyleSheet("font: 12pt '微软雅黑'; padding:8px;")self.load_doc_btn.clicked.connect(self.load_document)btn_layout.addWidget(self.load_doc_btn)self.save_doc_btn = QPushButton("保存翻译结果")self.save_doc_btn.setStyleSheet("font: 12pt '微软雅黑'; padding:8px;")self.save_doc_btn.clicked.connect(self.save_document)btn_layout.addWidget(self.save_doc_btn)main_layout.addLayout(btn_layout)self.input_box.keyPressEvent = self.key_press_eventdef key_press_event(self, event):if event.key() == Qt.Key_Return and (event.modifiers() & Qt.ControlModifier):self.on_translate()else:QTextEdit.keyPressEvent(self.input_box, event)def on_translate(self):src_text = self.input_box.toPlainText().strip()if not src_text:returnparagraphs = [line.strip() for line in src_text.split("\n") if line.strip()]self.progress_bar.setValue(0)self.translate_thread = TranslateThread(paragraphs)self.translate_thread.progress_signal.connect(self.update_progress)self.translate_thread.finished_signal.connect(self.display_result)self.translate_thread.start()def update_progress(self, current, total):self.progress_bar.setMaximum(total)self.progress_bar.setValue(current)def display_result(self, translated_paras):self.output_box.setPlainText("\n".join(translated_paras))self.progress_bar.setValue(0)def load_document(self):file_path, _ = QFileDialog.getOpenFileName(self, "选择文档", "", "文档 (*.pdf *.docx *.txt)")if not file_path:returnext = os.path.splitext(file_path)[1].lower()try:if ext == ".pdf":text = "\n".join(pdf_to_text(file_path))elif ext == ".docx":text = "\n".join(docx_to_text(file_path))elif ext == ".txt":text = "\n".join(txt_to_text(file_path))else:QMessageBox.warning(self, "错误", "不支持的文档格式")returnexcept Exception as e:QMessageBox.critical(self, "加载失败", f"文档加载出错: {e}")returnself.input_box.setPlainText(text)def save_document(self):file_path, _ = QFileDialog.getSaveFileName(self, "保存翻译结果", "", "文档 (*.pdf *.docx *.txt)")if not file_path:returntext = self.output_box.toPlainText().strip()if not text:returnext = os.path.splitext(file_path)[1].lower()paragraphs = [line.strip() for line in text.split("\n") if line.strip()]try:if ext == ".pdf":text_to_pdf(paragraphs, file_path)elif ext == ".docx":text_to_docx(paragraphs, file_path)elif ext == ".txt":text_to_txt(paragraphs, file_path)else:QMessageBox.warning(self, "错误", "不支持的保存格式")returnexcept Exception as e:QMessageBox.critical(self, "保存失败", f"保存文档出错: {e}")# -----------------------------
# 主程序
# -----------------------------
if __name__ == "__main__":tokenizer, model = load_model()app = QApplication(sys.argv)gui = TranslatorGUI()gui.show()sys.exit(app.exec_())

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/93703.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/93703.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于prompt的生物信息学:多组学分析的新界面

以前总以为综述/评论是假大空&#xff0c;最近在朋友的影响下才发现&#xff0c;大佬的综述/评论内容的确很值得一读&#xff0c;也值得分享的。比如这篇讲我比较感兴趣的AI辅助生信分析的&#xff0c;相信大家都是已经实践中用上了&#xff0c;看看大佬的评论&#xff0c;拓宽…

Nacos-8--分析一下nacos中的AP和CP模式

Nacos支持两种模式来满足不同场景下的需求&#xff1a;AP模式&#xff08;强调可用性&#xff09;和CP模式&#xff08;强调一致性&#xff09;。 这两种模式的选择主要基于CAP理论&#xff0c;该理论指出在一个分布式系统中&#xff0c;无法同时保证一致性&#xff08;Consist…

水闸安全监测的主要核心内容

水闸安全监测是指通过一系列技术手段和管理措施&#xff0c;对水闸的结构状态、运行性能及环境条件进行实时或定期的观测与评估&#xff0c;以确保水闸在设计寿命期内的安全性和可靠性。其核心目标是及时发现潜在的安全隐患&#xff0c;防止事故发生&#xff0c;保障水利工程的…

嵌入式系统学习Day19(数据结构)

数据结构的概念&#xff1a; 相互之间存在一种或多种特定关系的数据元素的集合。数据之间关系&#xff1a;逻辑关系&#xff1a;集合&#xff0c;线性&#xff08;1对1&#xff0c;中间位置的值有且仅有一个前驱&#xff0c;一个后继&#xff09;&#xff0c;树&#xff08;1对…

Pandas中数据清理、连接数据以及合并多个数据集的方法

一、简介1.数据清理的重要性&#xff1a;在进行数据分析前&#xff0c;需进行数据清理&#xff0c;使每个观测值成一行、每个变量成一列、每种观测单元构成一张表格。2.数据组合的必要性&#xff1a;数据整理好后&#xff0c;可能需要将多张表格组合才能进行某些分析&#xff0…

JavaSSM框架从入门到精通!第二天(MyBatis(一))!

一、 Mybatis 框架1. Mybatis 框架简介Mybatis 是 apache 的一个开源项目&#xff0c;名叫 iBatis &#xff0c;2010 年这个项目由 apache 迁移到了 google&#xff0c;并命名为 Mybatis&#xff0c;2013 年迁移到了 GitHub&#xff0c;可以在 GitHub 下载源码。2. Mybatis 的下…

Linux下Mysql命令,创建mysql,删除mysql

在 Linux 系统下&#xff0c;您可以通过命令行来创建和删除 MySQL 数据库。以下是详细的操作步骤&#xff0c;包括创建和删除数据库、用户&#xff0c;以及常见的相关管理命令。1. 登录 MySQL在执行任何 MySQL 操作之前&#xff0c;需要先登录 MySQL。1.1 使用 root 用户登录 M…

假设检验的原理

假设检验是统计学中用于判断样本数据是否支持某个特定假设的方法。其核心思想是通过样本数据对总体参数或分布提出假设&#xff0c;并利用统计量来判断这些假设的合理性。假设检验的基本步骤如下&#xff1a;1. 假设&#xff08;Hypothesis&#xff09;在统计学中&#xff0c;假…

信号、内存共享等实现

信号&#xff08;signal&#xff09;#include <signal.h> #include <stdio.h> #include <unistd.h>void handler(int sig) {printf("收到信号: %d\n", sig); }int main() {signal(SIGUSR1, handler); // 注册用户自定义信号printf("进程 PI…

《从日常到前沿:AI 在教育、医疗、制造业的真实落地案例》文章提纲

引言&#xff1a;AI 落地的多元图景​简述 AI 从实验室走向实际应用的发展趋势​说明选择教育、医疗、制造业的原因 —— 覆盖民生与基础产业&#xff0c;落地场景具有代表性​AI 在教育领域的落地案例​个性化学习&#xff1a;如某在线教育平台利用 AI 分析学生学习数据&#…

决策树(1)

一、树模型与决策树基础决策树概念&#xff1a;从根节点开始一步步走到叶子节点得出决策&#xff0c;所有数据最终都会落到叶子节点&#xff0c;既可用于分类&#xff0c;也可用于回归。树的组成根节点&#xff1a;第一个选择点。非叶子节点与分支&#xff1a;中间决策过程。叶…

电视系统:开启视听新时代

在当今数字化浪潮席卷的时代&#xff0c;电视领域正经历着一场深刻的变革&#xff0c;而电视系统无疑是这场变革中的耀眼明星。简单来讲&#xff0c;电视系统就是互联网协议电视&#xff0c;它宛如一座桥梁&#xff0c;巧妙地利用宽带有线电视网&#xff0c;将多媒体、互联网、…

字节开源了一款具备长期记忆能力的多模态智能体:M3-Agent

猫头虎AI分享&#xff5c;字节开源了一款具备长期记忆能力的多模态智能体&#xff1a;M3-Agent 近年来&#xff0c;多模态大模型的发展迅猛&#xff0c;但如何赋予智能体类似人类的长期记忆能力&#xff0c;一直是研究中的核心挑战。字节跳动开源的 M3-Agent&#xff0c;正是面…

第十六届蓝桥杯青少组C++省赛[2025.8.10]第二部分编程题(6、魔术扑克牌排列)

参考程序&#xff1a;#include<bits/stdc.h> using namespace std; long long dp[105]; long long c(int n) {dp[0] 1;for(int i1; i< n; i){for(int j0; j<i; j){dp[i] dp[j] * dp[i -1-j];}}return dp[n]; } int main() {int n;cin >> n;cout <<c(n…

【实时Linux实战系列】实时平台下的图像识别技术

在当今数字化时代&#xff0c;图像识别技术已经广泛应用于各个领域&#xff0c;如自动驾驶、安防监控、智能医疗等。它通过计算机对图像进行分析和处理&#xff0c;从而实现对物体、场景或人的识别。实时Linux作为一种高效的实时操作系统&#xff0c;为图像识别技术提供了强大的…

IPD流程执行检查表

IPD流程执行检查表 稽查

Jmeter的安装与使用教程

基于jdk1.8版本的Jmeter的下载与安装和使用教程。 一.安装jmeter 官网下载就行下载压缩包解压就行 Jmeter下载官网&#xff1a;http://jmeter.apache.org/download_jmeter.cgi找到安装包的下载位置&#xff0c;解压进入文件夹的bin文件夹下jmeter.bat。二.配置环境变量 1、“此…

docker 数据卷、自定义镜像操作演示分享(第二期)

数据卷1.1、背景前面有个docker go web demo应用示例&#xff0c;每次为了部署go_web_demo工程&#xff0c; 需要将使用到的cp的命令将宿主主机内的go_web_demo目录下的代码文件&#xff08;一般是编译后的二进制执行文件&#xff09;复制到容器内部。 数据卷&#xff1a;将宿主…

Pandas 入门到实践:核心数据结构与基础操作全解析(Day1 学习笔记)

目录 一、Pandas 概述 1. 什么是 Pandas 二、核心数据结构 1. Series 索引 显示索引 隐式索引 创建方式 属性与方法 数据访问 索引访问 切片访问 布尔索引 2. DataFrame 创建方式 属性与数据访问 数据修改 三、索引操作 1. 索引类型 2. 核心索引方法 3. 切…

hadoop技术栈(九)Hbase替代方案

一、 核心替代方向 ‌云原生托管NoSQL服务&#xff1a;‌ ‌Google Cloud Bigtable&#xff1a;‌ 这是HBase在云端的“官方”替代品&#xff0c;兼容HBase API&#xff0c;底层存储和架构高度优化&#xff0c;提供高吞吐、低延迟、无缝扩展、完全托管的服务。‌如果追求兼容性…