Skip to content
Toggle navigation
Toggle navigation
This project
Loading...
Sign in
柴进
/
GoogleNanoBananaApp
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
41026f54
authored
2025-12-12 16:48:49 +0800
by
shady
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
增加部分log日志文件处理
1 parent
ab1a3122
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
146 additions
and
96 deletions
image_generator.py
image_generator.py
View file @
41026f5
...
...
@@ -379,7 +379,8 @@ class HistoryManager:
# 保存生成的图片 - 使用PNG格式验证
generated_image_path
=
record_dir
/
"generated.png"
if
not
save_png_with_validation
(
str
(
generated_image_path
),
image_bytes
):
# Pillow处理失败,回退到原始方法
#
# ¬,回退到原始方法
with
open
(
generated_image_path
,
'wb'
)
as
f
:
f
.
write
(
image_bytes
)
self
.
logger
.
warning
(
f
"使用原始保存方法: {generated_image_path}"
)
...
...
@@ -1679,6 +1680,7 @@ class ImageGeneratorWindow(QMainWindow):
def
upload_images
(
self
):
"""Upload reference images"""
self
.
logger
.
info
(
"开始上传参考图片"
)
files
,
_
=
QFileDialog
.
getOpenFileNames
(
self
,
"选择参考图片"
,
...
...
@@ -1687,134 +1689,205 @@ class ImageGeneratorWindow(QMainWindow):
)
if
files
:
self
.
logger
.
info
(
f
"选择了 {len(files)} 个文件: {files}"
)
valid_count
=
0
for
file_path
in
files
:
try
:
if
self
.
validate_image_file
(
file_path
):
self
.
uploaded_images
.
append
(
file_path
)
valid_count
+=
1
self
.
logger
.
info
(
f
"成功添加图片: {file_path}"
)
else
:
self
.
logger
.
warning
(
f
"图片验证失败: {file_path}"
)
except
Exception
as
e
:
self
.
logger
.
error
(
f
"加载图片失败: {file_path}, 错误: {str(e)}"
,
exc_info
=
True
)
QMessageBox
.
critical
(
self
,
"错误"
,
f
"无法加载图片: {file_path}
\n
{str(e)}"
)
self
.
update_image_preview
()
self
.
image_count_label
.
setText
(
f
"已选择 {len(self.uploaded_images)} 张"
)
self
.
status_label
.
setText
(
f
"● 已添加 {
len(files)
} 张参考图片"
)
self
.
status_label
.
setText
(
f
"● 已添加 {
valid_count
} 张参考图片"
)
self
.
status_label
.
setStyleSheet
(
"QLabel { color: #34C759; }"
)
self
.
logger
.
info
(
f
"图片上传完成,有效图片: {valid_count} 张"
)
else
:
self
.
logger
.
info
(
"用户取消了图片选择"
)
def
add_image_files
(
self
,
file_paths
):
"""添加图像文件到上传列表(用于拖拽功能)"""
self
.
logger
.
info
(
f
"开始处理拖拽文件,共 {len(file_paths) if file_paths else 0} 个"
)
if
not
file_paths
:
self
.
logger
.
warning
(
"拖拽文件列表为空"
)
return
added_count
=
0
for
file_path
in
file_paths
:
self
.
logger
.
info
(
f
"处理拖拽文件: {file_path}"
)
try
:
if
self
.
validate_image_file
(
file_path
):
self
.
uploaded_images
.
append
(
file_path
)
added_count
+=
1
self
.
logger
.
info
(
f
"成功添加拖拽图片: {file_path}"
)
else
:
self
.
logger
.
warning
(
f
"无效的图像文件: {file_path}"
)
except
Exception
as
e
:
self
.
logger
.
error
(
f
"添加图片失败: {file_path}, 错误: {str(e)}"
)
self
.
logger
.
error
(
f
"添加图片失败: {file_path}, 错误: {str(e)}"
,
exc_info
=
True
)
if
added_count
>
0
:
self
.
logger
.
info
(
f
"拖拽添加成功,共 {added_count} 张图片"
)
self
.
update_image_preview
()
self
.
image_count_label
.
setText
(
f
"已选择 {len(self.uploaded_images)} 张"
)
self
.
status_label
.
setText
(
f
"● 已通过拖拽添加 {added_count} 张参考图片"
)
self
.
status_label
.
setStyleSheet
(
"QLabel { color: #34C759; }"
)
else
:
self
.
logger
.
warning
(
"没有找到有效的拖拽图片文件"
)
QMessageBox
.
warning
(
self
,
"警告"
,
"没有找到有效的图片文件"
)
def
add_clipboard_image
(
self
,
image
):
"""添加剪贴板图像(用于拖拽和粘贴功能)"""
try
:
self
.
logger
.
info
(
"开始处理剪贴板图像"
)
# 生成临时文件名
timestamp
=
datetime
.
now
()
.
strftime
(
"
%
Y
%
m
%
d_
%
H
%
M
%
S"
)
temp_dir
=
Path
(
tempfile
.
gettempdir
())
/
"nano_banana_app"
temp_dir
.
mkdir
(
exist_ok
=
True
)
self
.
logger
.
info
(
f
"临时目录: {temp_dir}"
)
# 根据图像格式选择文件扩展名
file_extension
=
".png"
# 默认使用PNG格式
if
image
.
format
()
==
QImage
.
Format_RGB32
:
file_extension
=
".bmp"
elif
image
.
format
()
==
QImage
.
Format_RGB888
:
file_extension
=
".jpg"
# 统一使用PNG格式保存,确保跨平台兼容
file_extension
=
".png"
temp_file_path
=
temp_dir
/
f
"clipboard_{timestamp}{file_extension}"
# 保存图像到临时文件
if
image
.
save
(
str
(
temp_file_path
)):
# 尝试多种保存方式,确保兼容性
success
=
False
try
:
# 方法1: 指定PNG格式
success
=
image
.
save
(
str
(
temp_file_path
),
"PNG"
)
self
.
logger
.
info
(
f
"方法1保存结果: {success}"
)
except
:
try
:
# 方法2: 自动格式
success
=
image
.
save
(
str
(
temp_file_path
))
self
.
logger
.
info
(
f
"方法2保存结果: {success}"
)
except
:
# 方法3: 转换格式再保存
converted_image
=
QImage
(
image
)
success
=
converted_image
.
save
(
str
(
temp_file_path
),
"PNG"
)
self
.
logger
.
info
(
f
"方法3保存结果: {success}"
)
if
success
and
temp_file_path
.
exists
():
self
.
uploaded_images
.
append
(
str
(
temp_file_path
))
self
.
update_image_preview
()
self
.
image_count_label
.
setText
(
f
"已选择 {len(self.uploaded_images)} 张"
)
self
.
status_label
.
setText
(
"● 已添加剪贴板图片"
)
self
.
status_label
.
setStyleSheet
(
"QLabel { color: #34C759; }"
)
self
.
logger
.
info
(
f
"剪贴板图片已保存到: {temp_file_path}"
)
self
.
logger
.
info
(
f
"剪贴板图片已
成功
保存到: {temp_file_path}"
)
else
:
self
.
logger
.
error
(
"图片保存失败"
)
QMessageBox
.
critical
(
self
,
"错误"
,
"无法保存剪贴板图片"
)
except
Exception
as
e
:
self
.
logger
.
error
(
f
"添加剪贴板图片失败: {str(e)}"
)
self
.
logger
.
error
(
f
"添加剪贴板图片失败: {str(e)}"
,
exc_info
=
True
)
QMessageBox
.
critical
(
self
,
"错误"
,
f
"添加剪贴板图片失败: {str(e)}"
)
def
paste_from_clipboard
(
self
):
"""从剪贴板粘贴图像"""
try
:
self
.
logger
.
info
(
"开始粘贴剪贴板图片"
)
clipboard
=
QApplication
.
clipboard
()
# 获取剪贴板MIME数据
mime_data
=
clipboard
.
mimeData
()
self
.
logger
.
info
(
f
"剪贴板MIME类型: {[mime for mime in mime_data.formats()]}"
)
# 检查剪贴板中是否有图像
if
clipboard
.
mimeData
()
.
hasImage
():
if
mime_data
.
hasImage
():
self
.
logger
.
info
(
"检测到剪贴板中有图像数据"
)
image
=
clipboard
.
image
()
if
not
image
.
isNull
():
self
.
logger
.
info
(
f
"图像尺寸: {image.width()}x{image.height()}, 格式: {image.format()}"
)
self
.
add_clipboard_image
(
image
)
else
:
self
.
logger
.
warning
(
"剪贴板图像为空"
)
QMessageBox
.
information
(
self
,
"信息"
,
"剪贴板中没有有效的图片"
)
else
:
self
.
logger
.
warning
(
f
"剪贴板中没有图像,可用格式: {mime_data.formats()}"
)
QMessageBox
.
information
(
self
,
"信息"
,
"剪贴板中没有图片,请先复制一张图片"
)
except
Exception
as
e
:
self
.
logger
.
error
(
f
"粘贴剪贴板图片时发生错误: {str(e)}"
,
exc_info
=
True
)
QMessageBox
.
critical
(
self
,
"错误"
,
f
"粘贴失败: {str(e)}"
)
def
validate_image_file
(
self
,
file_path
:
str
)
->
bool
:
"""验证图像文件"""
self
.
logger
.
info
(
f
"开始验证图像文件: {file_path}"
)
try
:
# 检查文件是否存在
if
not
Path
(
file_path
)
.
exists
():
self
.
logger
.
warning
(
f
"文件不存在: {file_path}"
)
return
False
# 检查文件扩展名
valid_extensions
=
{
'.png'
,
'.jpg'
,
'.jpeg'
,
'.gif'
,
'.bmp'
,
'.webp'
}
if
Path
(
file_path
)
.
suffix
.
lower
()
not
in
valid_extensions
:
file_extension
=
Path
(
file_path
)
.
suffix
.
lower
()
if
file_extension
not
in
valid_extensions
:
self
.
logger
.
warning
(
f
"不支持的文件格式: {file_extension}, 文件: {file_path}"
)
return
False
# 尝试加载图像以验证文件完整性
pixmap
=
QPixmap
(
file_path
)
if
pixmap
.
isNull
():
return
False
# 检查文件大小(限制为10MB)
# 检查文件大小
file_size
=
Path
(
file_path
)
.
stat
()
.
st_size
self
.
logger
.
info
(
f
"文件大小: {file_size} bytes, 格式: {file_extension}"
)
if
file_size
>
10
*
1024
*
1024
:
# 10MB
self
.
logger
.
warning
(
f
"文件过大: {file_size} bytes, 文件: {file_path}"
)
QMessageBox
.
warning
(
self
,
"警告"
,
f
"图片文件过大: {file_path}
\n
请选择小于10MB的图片"
)
return
False
if
file_size
==
0
:
self
.
logger
.
warning
(
f
"文件为空: {file_path}"
)
return
False
# 尝试加载图像以验证文件完整性
self
.
logger
.
info
(
f
"尝试加载图片验证完整性: {file_path}"
)
pixmap
=
QPixmap
(
file_path
)
if
pixmap
.
isNull
():
self
.
logger
.
error
(
f
"图片加载失败或损坏: {file_path}"
)
return
False
self
.
logger
.
info
(
f
"图片验证成功,尺寸: {pixmap.width()}x{pixmap.height()}, 文件: {file_path}"
)
return
True
except
Exception
as
e
:
self
.
logger
.
error
(
f
"图像文件验证
失败: {file_path}, 错误: {str(e)}"
)
self
.
logger
.
error
(
f
"图像文件验证
异常: {file_path}, 错误: {str(e)}"
,
exc_info
=
True
)
return
False
def
keyPressEvent
(
self
,
event
):
"""处理键盘事件"""
# 检测键盘组合键
key_text
=
event
.
text
()
key_int
=
event
.
key
()
modifiers
=
event
.
modifiers
()
self
.
logger
.
info
(
f
"键盘事件: key={key_int}, text='{key_text}', modifiers={modifiers}"
)
# Ctrl+V 粘贴
if
event
.
key
()
==
Qt
.
Key_V
and
event
.
modifiers
()
==
Qt
.
ControlModifier
:
self
.
logger
.
info
(
"检测到 Ctrl+V 粘贴组合键"
)
self
.
paste_from_clipboard
()
event
.
accept
()
return
# Cmd+V 粘贴 (macOS)
elif
event
.
key
()
==
Qt
.
Key_V
and
event
.
modifiers
()
==
Qt
.
MetaModifier
:
self
.
logger
.
info
(
"检测到 Cmd+V 粘贴组合键 (macOS)"
)
self
.
paste_from_clipboard
()
event
.
accept
()
return
self
.
logger
.
debug
(
f
"未处理的键盘事件: {key_text}"
)
super
()
.
keyPressEvent
(
event
)
def
update_image_preview
(
self
):
"""Update image preview thumbnails"""
self
.
logger
.
info
(
f
"更新图片预览,共 {len(self.uploaded_images)} 张图片"
)
try
:
# Clear existing previews
while
self
.
img_layout
.
count
()
>
1
:
# Keep the stretch
item
=
self
.
img_layout
.
takeAt
(
0
)
...
...
@@ -1823,10 +1896,16 @@ class ImageGeneratorWindow(QMainWindow):
# Add thumbnails
for
idx
,
file_path
in
enumerate
(
self
.
uploaded_images
):
self
.
logger
.
info
(
f
"创建第 {idx + 1} 张图片缩略图: {file_path}"
)
try
:
# Load and create thumbnail
pixmap
=
QPixmap
(
file_path
)
if
pixmap
.
isNull
():
self
.
logger
.
warning
(
f
"无法加载图片进行预览: {file_path}"
)
continue
pixmap
=
pixmap
.
scaled
(
100
,
100
,
Qt
.
KeepAspectRatio
,
Qt
.
SmoothTransformation
)
self
.
logger
.
info
(
f
"缩略图创建成功: {pixmap.width()}x{pixmap.height()}"
)
# Container
container
=
QWidget
()
...
...
@@ -1868,17 +1947,27 @@ class ImageGeneratorWindow(QMainWindow):
container
.
setLayout
(
container_layout
)
self
.
img_layout
.
insertWidget
(
self
.
img_layout
.
count
()
-
1
,
container
)
self
.
logger
.
info
(
f
"缩略图UI组件创建完成: {file_path}"
)
except
Exception
as
e
:
self
.
logger
.
error
(
f
"创建缩略图失败: {file_path}, 错误: {str(e)}"
,
exc_info
=
True
)
except
Exception
as
e
:
print
(
f
"Failed to create thumbnail for {file_path}: {e}"
)
self
.
logger
.
error
(
f
"更新图片预览失败: {str(e)}"
,
exc_info
=
True
)
def
delete_image
(
self
,
index
):
"""Delete an image by index"""
self
.
logger
.
info
(
f
"尝试删除第 {index + 1} 张图片"
)
if
0
<=
index
<
len
(
self
.
uploaded_images
):
deleted_file
=
self
.
uploaded_images
[
index
]
self
.
uploaded_images
.
pop
(
index
)
self
.
logger
.
info
(
f
"已删除图片: {deleted_file}"
)
self
.
update_image_preview
()
self
.
image_count_label
.
setText
(
f
"已选择 {len(self.uploaded_images)} 张"
)
self
.
status_label
.
setText
(
"● 已删除图片"
)
self
.
status_label
.
setStyleSheet
(
"QLabel { color: #FF9500; }"
)
else
:
self
.
logger
.
warning
(
f
"无效的图片索引: {index}"
)
def
update_saved_prompts_list
(
self
):
"""Update the saved prompts dropdown"""
...
...
@@ -2749,98 +2838,57 @@ class JewelryLibraryManager:
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
config_dir
=
config_dir
self
.
config_path
=
config_dir
/
"jewelry_library.json"
# 获取默认词库文件的路径
self
.
default_library_path
=
Path
(
__file__
)
.
parent
/
"data"
/
"default_jewelry_library.json"
self
.
library
=
self
.
load_library
()
def
load_library
(
self
)
->
Dict
[
str
,
List
[
str
]]:
"""加载词库,
不存在则创建
默认词库"""
"""加载词库,
优先使用用户配置,不存在则使用
默认词库"""
if
self
.
config_path
.
exists
():
try
:
with
open
(
self
.
config_path
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
library
=
json
.
load
(
f
)
# 检查是否需要进行数据迁移
library
=
self
.
_migrate_library_if_needed
(
library
)
# 验证数据完整性,补全缺失的类别
needs_update
=
False
for
category
,
default_items
in
DEFAULT_JEWELRY_LIBRARY
.
items
():
if
category
not
in
library
:
self
.
logger
.
warning
(
f
"检测到缺失类别: {category},从默认配置补全"
)
library
[
category
]
=
default_items
.
copy
()
needs_update
=
True
elif
not
library
[
category
]
or
len
(
library
[
category
])
==
0
:
self
.
logger
.
warning
(
f
"检测到空类别: {category},从默认配置补全"
)
library
[
category
]
=
default_items
.
copy
()
needs_update
=
True
elif
len
(
library
[
category
])
<
len
(
default_items
)
*
0.5
:
# 如果类别数据量少于默认值的50%,认为数据不完整,使用默认数据
self
.
logger
.
warning
(
f
"检测到类别 {category} 数据不完整(仅{len(library[category])}项,默认{len(default_items)}项),从默认配置补全"
)
library
[
category
]
=
default_items
.
copy
()
needs_update
=
True
# 如果数据被补全,保存更新后的配置
if
needs_update
:
self
.
logger
.
info
(
"词库数据已补全,保存更新"
)
self
.
save_library
(
library
)
self
.
logger
.
info
(
f
"珠宝词库加载成功: {self.config_path}"
)
return
library
except
Exception
as
e
:
self
.
logger
.
error
(
f
"珠宝词库加载失败: {e},使用默认词库"
)
return
DEFAULT_JEWELRY_LIBRARY
.
copy
()
else
:
# 首次使用,创建默认词库
self
.
logger
.
info
(
"未找到珠宝词库文件,创建默认词库"
)
library
=
DEFAULT_JEWELRY_LIBRARY
.
copy
()
# 尝试保存默认配置,覆盖损坏的文件
try
:
self
.
save_library
(
library
)
except
:
pass
return
library
def
_migrate_library_if_needed
(
self
,
library
:
Dict
[
str
,
List
[
str
]])
->
Dict
[
str
,
List
[
str
]]:
"""检查并执行数据迁移"""
# 如果存在旧的"主石"字段,需要拆分
if
"主石"
in
library
:
self
.
logger
.
info
(
"检测到旧版主石字段,执行数据迁移..."
)
# 创建新字段
shapes
=
[]
materials
=
[]
# 分析现有数据
for
item
in
library
[
"主石"
]:
# 提取形状信息
shape
=
self
.
_extract_shape
(
item
)
if
shape
and
shape
not
in
shapes
:
shapes
.
append
(
shape
)
# 提取材质信息
material
=
self
.
_extract_material
(
item
)
if
material
and
material
not
in
materials
:
materials
.
append
(
material
)
# 更新词库
library
[
"主石形状"
]
=
shapes
if
shapes
else
DEFAULT_JEWELRY_LIBRARY
[
"主石形状"
]
library
[
"主石材质"
]
=
materials
if
materials
else
DEFAULT_JEWELRY_LIBRARY
[
"主石材质"
]
# 保留旧字段作为备份
# library.pop("主石", None) # 可以选择是否移除
# 保存迁移后的数据
else
:
# 首次使用,从代码中的默认配置创建用户配置文件
self
.
logger
.
info
(
"首次使用,创建用户配置文件"
)
library
=
DEFAULT_JEWELRY_LIBRARY
.
copy
()
self
.
save_library
(
library
)
self
.
logger
.
info
(
f
"数据迁移完成:形状 {len(shapes)} 个,材质 {len(materials)} 个"
)
return
library
def
_extract_shape
(
self
,
item
:
str
)
->
Optional
[
str
]:
"""从主石描述中提取形状"""
# 直接从 DEFAULT_JEWELRY_LIBRARY 获取形状列表,确保同步
shapes
=
DEFAULT_JEWELRY_LIBRARY
.
get
(
"主石形状"
,
[])
for
shape
in
shapes
:
# 处理带括号的形状(如"子弹形(Baguette 子弹刻面)")
if
"("
in
shape
:
shape_base
=
shape
.
split
(
"("
)[
0
]
if
item
.
startswith
(
shape_base
):
return
shape_base
elif
item
.
startswith
(
shape
):
return
shape
return
None
def
_extract_material
(
self
,
item
:
str
)
->
Optional
[
str
]:
"""从主石描述中提取材质"""
# 直接从 DEFAULT_JEWELRY_LIBRARY 获取形状列表,确保同步
shapes
=
DEFAULT_JEWELRY_LIBRARY
.
get
(
"主石形状"
,
[])
for
shape
in
shapes
:
# 处理带括号的形状(如"子弹形(Baguette 子弹刻面)")
shape_base
=
shape
.
split
(
"("
)[
0
]
if
"("
in
shape
else
shape
if
item
.
startswith
(
shape_base
):
material
=
item
[
len
(
shape_base
):]
# 去除可能的连接词
if
material
.
startswith
(
"的"
):
material
=
material
[
1
:]
return
material
if
material
else
None
return
item
# 如果没有形状前缀,返回整个字符串
def
save_library
(
self
,
library
:
Dict
[
str
,
List
[
str
]]
=
None
):
"""保存词库到
配置文件
"""保存词库到
用户配置目录
Args:
library: 要保存的词库,如果为None则保存当前词库
...
...
@@ -3096,6 +3144,8 @@ class StyleDesignerTab(QWidget):
self
.
prompt_preview
.
setReadOnly
(
True
)
prompt_layout
.
addWidget
(
self
.
prompt_preview
)
prompt_group
.
setLayout
(
prompt_layout
)
prompt_group
.
setStyleSheet
(
"font-size: 16px;"
)
content_row
.
addWidget
(
prompt_group
,
2
)
# Settings section
...
...
Please
register
or
sign in
to post a comment