Skip to content

Commit 4885264

Browse files
committed
v1.9.3: Web UI管理系统重构, Qt UI预设功能, 编辑器稳定性和内存优化
1 parent 2085f0a commit 4885264

486 files changed

Lines changed: 369034 additions & 7369 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build-and-release.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,17 +186,19 @@ jobs:
186186
cp -r models_cpu dist/manga-translator-cpu/_internal/models
187187
cp -r dict dist/manga-translator-cpu/_internal/
188188
cp -r doc dist/manga-translator-cpu/_internal/
189-
cp -r desktop_qt_ui/styles dist/manga-translator-cpu/_internal/desktop_qt_ui/
190-
cp -r desktop_qt_ui/locales dist/manga-translator-cpu/_internal/desktop_qt_ui/
189+
mkdir -p dist/manga-translator-cpu/_internal/desktop_qt_ui
190+
cp -r desktop_qt_ui/styles dist/manga-translator-cpu/_internal/desktop_qt_ui/styles
191+
cp -r desktop_qt_ui/locales dist/manga-translator-cpu/_internal/desktop_qt_ui/locales
191192
cp packaging/VERSION dist/manga-translator-cpu/_internal/
192193
193194
cp -r examples dist/manga-translator-gpu/_internal/
194195
cp -r fonts dist/manga-translator-gpu/_internal/
195196
cp -r models_gpu dist/manga-translator-gpu/_internal/models
196197
cp -r dict dist/manga-translator-gpu/_internal/
197198
cp -r doc dist/manga-translator-gpu/_internal/
198-
cp -r desktop_qt_ui/styles dist/manga-translator-gpu/_internal/desktop_qt_ui/
199-
cp -r desktop_qt_ui/locales dist/manga-translator-gpu/_internal/desktop_qt_ui/
199+
mkdir -p dist/manga-translator-gpu/_internal/desktop_qt_ui
200+
cp -r desktop_qt_ui/styles dist/manga-translator-gpu/_internal/desktop_qt_ui/styles
201+
cp -r desktop_qt_ui/locales dist/manga-translator-gpu/_internal/desktop_qt_ui/locales
200202
cp packaging/VERSION dist/manga-translator-gpu/_internal/
201203
202204
- name: Create Release Archives using 7-Zip with Splitting

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ Miniconda3/
1313
.env
1414
.env.*
1515

16+
# Presets directory (contains API keys and sensitive configuration)
17+
presets/
18+
1619
# Claude settings
1720
.claude/
1821

@@ -201,3 +204,6 @@ originals/
201204
# Manga chapter splitter tool (漫画章节分割工具 - 独立工具)
202205
desktop_qt_ui/manga_chapter_splitter/
203206
启动章节分割工具.bat
207+
208+
# User uploaded resources (用户上传的字体和提示词)
209+
manga_translator/server/user_resources/

README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ python -m manga_translator --help
208208

209209
- 🔍 **智能文本检测** - 自动识别漫画中的文字区域
210210
- 📝 **多语言 OCR** - 支持日语、中文、英语等多种语言
211-
- 🌐 **30+ 翻译引擎** - 离线/在线翻译器任选
211+
- 🌐 **5 种翻译引擎** - OpenAI、Gemini(普通+高质量)、Sakura
212212
- 🎯 **高质量翻译** - 支持 GPT-4o、Gemini 多模态 AI 翻译
213213
- 🎨 **智能嵌字** - 自动排版译文,支持多种字体
214214
- 📦 **批量处理** - 一次处理整个文件夹
@@ -239,11 +239,10 @@ python -m manga_translator --help
239239

240240
## ⚙️ 常用翻译器
241241

242-
### 离线翻译器(无需网络)
243-
- Sugoi、NLLB、M2M100、Qwen2 等
244-
245242
### 在线翻译器(需要 API Key)
246-
- Google Gemini、OpenAI、DeepL、百度翻译等
243+
- **OpenAI** - 使用 GPT 系列模型
244+
- **Gemini** - 使用 Google Gemini 模型
245+
- **Sakura** - 专门针对日语优化的翻译模型
247246

248247
### 高质量翻译器(推荐)
249248
- **高质量翻译 OpenAI** - 使用 GPT-4o 多模态模型

desktop_qt_ui/app_logic.py

Lines changed: 290 additions & 116 deletions
Large diffs are not rendered by default.

desktop_qt_ui/core/config_models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class TranslatorSettings(BaseModel):
1212
gpt_config: Optional[str] = "examples/gpt_config-example.yaml"
1313
high_quality_prompt_path: Optional[str] = "dict/prompt_example.json"
1414
max_requests_per_minute: int = 0
15+
attempts: int = -1 # 翻译重试次数,-1 表示无限重试
1516

1617
@property
1718
def chatgpt_config(self):
@@ -130,6 +131,7 @@ class AppSection(BaseModel):
130131
favorite_folders: Optional[List[str]] = None
131132
theme: str = "light" # 主题:light, dark, gray
132133
ui_language: str = "auto" # UI语言:auto(自动检测), zh_CN, en_US, ja_JP, ko_KR 等
134+
current_preset: str = "默认" # 当前使用的预设名称
133135

134136
class AppSettings(BaseModel):
135137
app: AppSection = Field(default_factory=AppSection)

desktop_qt_ui/editor/commands.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,15 @@ def _apply_data(self, data_to_apply: Dict[str, Any]):
4747
# 直接更新模型中的区域字典
4848
self._model._regions[self._index] = data_to_apply
4949

50-
# 【关键修复】同时更新resource_manager中的regions(延迟导入避免循环)
50+
# 【关键修复】同时更新resource_manager中的regions
51+
# resource_manager使用索引而不是ID,因为_set_regions按顺序添加区域
5152
from services import get_resource_manager
5253
resource_manager = get_resource_manager()
53-
resource_manager.update_region(self._index, data_to_apply)
54+
all_regions = resource_manager.get_all_regions()
55+
if self._index < len(all_regions):
56+
# 获取对应索引的region_id
57+
region_resource = all_regions[self._index]
58+
resource_manager.update_region(region_resource.region_id, data_to_apply)
5459

5560
# 如果 center 改变了,需要触发完全更新,重新创建 item
5661
# 否则只触发单个 item 更新
@@ -92,13 +97,28 @@ def execute(self):
9297
"""执行添加操作"""
9398
self._model._regions.append(copy.deepcopy(self._region_data))
9499
self._index = len(self._model._regions) - 1
100+
101+
# 【关键修复】同时更新resource_manager中的regions
102+
from services import get_resource_manager
103+
resource_manager = get_resource_manager()
104+
resource_manager.add_region(self._region_data)
105+
95106
# 添加操作触发完全更新
96107
self._model.regions_changed.emit(self._model._regions)
97108

98109
def undo(self):
99110
"""撤销添加操作:删除最后添加的区域"""
100111
if self._index is not None and 0 <= self._index < len(self._model._regions):
101112
self._model._regions.pop(self._index)
113+
114+
# 【关键修复】同时更新resource_manager中的regions
115+
# 由于ResourceManager使用字典存储,需要重新同步整个列表
116+
from services import get_resource_manager
117+
resource_manager = get_resource_manager()
118+
resource_manager.clear_regions()
119+
for region_data in self._model._regions:
120+
resource_manager.add_region(region_data)
121+
102122
# 删除操作会改变后续区域的索引,必须触发完全更新
103123
self._model.regions_changed.emit(self._model._regions)
104124
# 清除选择

desktop_qt_ui/editor/core/resource_manager.py

Lines changed: 106 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
统一管理编辑器的所有资源,包括图片、蒙版、区域等。
44
"""
55

6+
import gc
67
import logging
78
import os
89
from typing import Dict, List, Optional
@@ -14,6 +15,19 @@
1415
from .types import MaskType
1516

1617

18+
def _release_gpu_memory():
19+
"""释放GPU显存"""
20+
try:
21+
import torch
22+
if torch.cuda.is_available():
23+
torch.cuda.empty_cache()
24+
torch.cuda.synchronize()
25+
except ImportError:
26+
pass
27+
except Exception:
28+
pass
29+
30+
1731
class ResourceManager:
1832
"""资源管理器
1933
@@ -68,7 +82,7 @@ def load_image(self, image_path: str, json_data: Optional[Dict] = None) -> Image
6882

6983
# 加载图片
7084
try:
71-
self.logger.info(f"Loading image: {image_path}")
85+
self.logger.debug(f"Loading image: {image_path}")
7286
image = Image.open(image_path)
7387

7488
# 创建资源对象
@@ -86,7 +100,7 @@ def load_image(self, image_path: str, json_data: Optional[Dict] = None) -> Image
86100
# 设置为当前图片
87101
self._current_image = resource
88102

89-
self.logger.info(f"Image loaded successfully: {image_path} ({image.width}x{image.height})")
103+
self.logger.debug(f"Image loaded successfully: {image_path} ({image.width}x{image.height})")
90104
return resource
91105

92106
except Exception as e:
@@ -107,18 +121,66 @@ def _add_to_cache(self, path: str, resource: ImageResource) -> None:
107121
old_resource = self._image_cache.pop(oldest_path)
108122
old_resource.release()
109123
self.logger.debug(f"Removed oldest image from cache: {oldest_path}")
124+
125+
# 释放内存
126+
gc.collect()
110127

111128
self._image_cache[path] = resource
112129

113-
def unload_image(self) -> None:
114-
"""卸载当前图片及所有关联资源"""
130+
def release_image_from_cache(self, path: str) -> bool:
131+
"""从缓存中释放指定图片
132+
133+
Args:
134+
path: 图片路径
135+
136+
Returns:
137+
bool: 是否成功释放
138+
"""
139+
path = os.path.normpath(path)
140+
if path in self._image_cache:
141+
resource = self._image_cache.pop(path)
142+
resource.release()
143+
gc.collect()
144+
self.logger.debug(f"Released image from cache: {path}")
145+
return True
146+
return False
147+
148+
def clear_image_cache(self) -> None:
149+
"""清空所有图片缓存"""
150+
for resource in self._image_cache.values():
151+
resource.release()
152+
self._image_cache.clear()
153+
gc.collect()
154+
_release_gpu_memory()
155+
self.logger.info("Cleared all image cache")
156+
157+
def unload_image(self, release_from_cache: bool = False) -> None:
158+
"""卸载当前图片及所有关联资源
159+
160+
Args:
161+
release_from_cache: 是否同时从缓存中释放该图片
162+
"""
115163
if self._current_image:
116-
self.logger.info(f"Unloading image: {self._current_image.path}")
164+
current_path = self._current_image.path
165+
166+
# 如果需要从缓存中释放
167+
if release_from_cache and current_path in self._image_cache:
168+
resource = self._image_cache.pop(current_path)
169+
resource.release()
170+
self.logger.debug(f"Released image from cache: {current_path}")
171+
117172
self._current_image = None
118173

119174
# 清空所有关联资源
120175
self.clear_masks()
121176
self.clear_regions()
177+
self.clear_cache()
178+
179+
# 强制垃圾回收
180+
gc.collect()
181+
_release_gpu_memory()
182+
183+
self.logger.debug("Image unloaded and memory released")
122184

123185
def get_current_image(self) -> Optional[ImageResource]:
124186
"""获取当前图片资源
@@ -249,12 +311,13 @@ def get_region(self, region_id: int) -> Optional[RegionResource]:
249311
return self._regions.get(region_id)
250312

251313
def get_all_regions(self) -> List[RegionResource]:
252-
"""获取所有区域
314+
"""获取所有区域(按region_id排序)
253315
254316
Returns:
255-
List[RegionResource]: 区域列表
317+
List[RegionResource]: 区域列表,按region_id升序排列
256318
"""
257-
return list(self._regions.values())
319+
# 按region_id排序,确保顺序正确
320+
return [self._regions[rid] for rid in sorted(self._regions.keys())]
258321

259322
def clear_regions(self) -> None:
260323
"""清空所有区域"""
@@ -306,16 +369,48 @@ def cleanup_all(self) -> None:
306369
"""清理所有资源"""
307370
self.logger.info("Cleaning up all resources")
308371

309-
# 卸载当前图片
310-
self.unload_image()
372+
# 卸载当前图片(不从缓存释放,因为下面会清空缓存)
373+
if self._current_image:
374+
self._current_image = None
375+
376+
# 清空蒙版
377+
self.clear_masks()
378+
379+
# 清空区域
380+
self.clear_regions()
381+
382+
# 清空临时缓存
383+
self._temp_cache.clear()
311384

312-
# 清理缓存
385+
# 清理图片缓存
313386
for resource in self._image_cache.values():
314387
resource.release()
315388
self._image_cache.clear()
316389

390+
# 强制垃圾回收和GPU显存释放
391+
gc.collect()
392+
_release_gpu_memory()
393+
317394
self.logger.info("All resources cleaned up")
318395

396+
def release_memory_after_export(self) -> None:
397+
"""导出后释放内存
398+
399+
清理临时缓存和GPU显存,但保留图片缓存以便快速切换
400+
"""
401+
self.logger.info("Releasing memory after export")
402+
403+
# 清空临时缓存(inpainted图片等)
404+
self._temp_cache.clear()
405+
406+
# 强制垃圾回收
407+
gc.collect()
408+
409+
# 释放GPU显存
410+
_release_gpu_memory()
411+
412+
self.logger.info("Memory released after export")
413+
319414
def get_memory_usage_estimate(self) -> int:
320415
"""估算内存使用量(字节)
321416

0 commit comments

Comments
 (0)