121 lines
3.8 KiB
Python
121 lines
3.8 KiB
Python
|
|
"""
|
||
|
|
模板缓存引擎。解决 QgsProject 单例问题。
|
||
|
|
|
||
|
|
流程:
|
||
|
|
1. 首次请求:project.read() 加载模板(慢,仅一次)
|
||
|
|
2. 加载后 project.write() 保存到临时文件
|
||
|
|
3. 后续同模板请求:从临时文件恢复(快,连接复用)
|
||
|
|
4. 手动恢复文本/过滤/缩放(毫秒级)
|
||
|
|
"""
|
||
|
|
import os
|
||
|
|
import time
|
||
|
|
import tempfile
|
||
|
|
|
||
|
|
from qgis.core import QgsProject, QgsRectangle
|
||
|
|
|
||
|
|
from app.config.paths import get_logger
|
||
|
|
|
||
|
|
logger = get_logger("qgis.cache")
|
||
|
|
|
||
|
|
|
||
|
|
class TemplateCache:
|
||
|
|
def __init__(self):
|
||
|
|
self._cache: dict[str, dict] = {}
|
||
|
|
|
||
|
|
def is_loaded(self, template_path: str) -> bool:
|
||
|
|
return template_path in self._cache
|
||
|
|
|
||
|
|
def load_template(self, template_path: str, layout_name: str = "A4") -> None:
|
||
|
|
"""首次加载模板"""
|
||
|
|
start = time.time()
|
||
|
|
project = QgsProject.instance()
|
||
|
|
|
||
|
|
logger.info(f"首次加载: {os.path.basename(template_path)}")
|
||
|
|
project.read(template_path)
|
||
|
|
logger.info(f"project.read() 耗时: {time.time() - start:.1f}s")
|
||
|
|
|
||
|
|
# 保存到临时文件
|
||
|
|
tmp_file = tempfile.NamedTemporaryFile(
|
||
|
|
suffix=".qgz", delete=False,
|
||
|
|
dir=os.path.dirname(template_path),
|
||
|
|
)
|
||
|
|
tmp_path = tmp_file.name
|
||
|
|
tmp_file.close()
|
||
|
|
|
||
|
|
t_save = time.time()
|
||
|
|
project.write(tmp_path)
|
||
|
|
logger.info(f"项目保存耗时: {time.time() - t_save:.1f}s")
|
||
|
|
|
||
|
|
# 记录初始状态
|
||
|
|
texts = {}
|
||
|
|
extent = None
|
||
|
|
layout = project.layoutManager().layoutByName(layout_name)
|
||
|
|
if layout:
|
||
|
|
for item_id in ["mapTitle", "mapTime", "mapUnit", "info"]:
|
||
|
|
item = layout.itemById(item_id)
|
||
|
|
if item:
|
||
|
|
texts[item_id] = item.text()
|
||
|
|
map_item = layout.itemById("Map")
|
||
|
|
if map_item:
|
||
|
|
extent = QgsRectangle(map_item.extent())
|
||
|
|
|
||
|
|
self._cache[template_path] = {
|
||
|
|
"file": tmp_path,
|
||
|
|
"texts": texts,
|
||
|
|
"extent": extent,
|
||
|
|
"layout": layout_name,
|
||
|
|
}
|
||
|
|
logger.info(f"模板加载完成,总耗时: {time.time() - start:.1f}s")
|
||
|
|
|
||
|
|
def restore_template(self, template_path: str) -> tuple:
|
||
|
|
"""从缓存恢复模板"""
|
||
|
|
cached = self._cache.get(template_path)
|
||
|
|
if not cached:
|
||
|
|
raise RuntimeError(f"模板未缓存: {template_path}")
|
||
|
|
|
||
|
|
start = time.time()
|
||
|
|
project = QgsProject.instance()
|
||
|
|
|
||
|
|
logger.info(f"恢复模板: {os.path.basename(template_path)}")
|
||
|
|
project.read(cached["file"])
|
||
|
|
logger.info(f"project.read() 耗时: {time.time() - start:.1f}s")
|
||
|
|
|
||
|
|
return project, cached["texts"], cached["extent"]
|
||
|
|
|
||
|
|
def reset_project_state(self, project: QgsProject, texts: dict, extent) -> None:
|
||
|
|
"""重置项目到干净状态"""
|
||
|
|
start = time.time()
|
||
|
|
|
||
|
|
for layer in project.mapLayers().values():
|
||
|
|
if layer.subsetString():
|
||
|
|
layer.setSubsetString("")
|
||
|
|
|
||
|
|
# 获取 layout 名称(从缓存中)
|
||
|
|
layout_name = "A4"
|
||
|
|
for cached in self._cache.values():
|
||
|
|
layout_name = cached.get("layout", "A4")
|
||
|
|
break
|
||
|
|
|
||
|
|
layout = project.layoutManager().layoutByName(layout_name)
|
||
|
|
if layout:
|
||
|
|
for item_id, text in texts.items():
|
||
|
|
item = layout.itemById(item_id)
|
||
|
|
if item:
|
||
|
|
item.setText(text)
|
||
|
|
if extent:
|
||
|
|
map_item = layout.itemById("Map")
|
||
|
|
if map_item:
|
||
|
|
map_item.zoomToExtent(extent)
|
||
|
|
|
||
|
|
logger.info(f"状态重置耗时: {time.time() - start:.3f}s")
|
||
|
|
|
||
|
|
def cleanup(self) -> None:
|
||
|
|
"""清理所有临时文件"""
|
||
|
|
for cached in self._cache.values():
|
||
|
|
try:
|
||
|
|
os.remove(cached["file"])
|
||
|
|
except OSError:
|
||
|
|
pass
|
||
|
|
self._cache.clear()
|
||
|
|
logger.info("已清理所有缓存")
|