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
994a9ea2
authored
2025-12-10 16:32:10 +0800
by
柴进
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
增加款式设计的部分标签
1 parent
32c7b837
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
972 additions
and
5 deletions
.idea/GoogleNanoBananaApp.iml
image_generator.py
jewelry_library.json
zb100_icon.png
.idea/GoogleNanoBananaApp.iml
View file @
994a9ea
...
...
@@ -3,8 +3,9 @@
<component
name=
"NewModuleRootManager"
>
<content
url=
"file://$MODULE_DIR$"
>
<excludeFolder
url=
"file://$MODULE_DIR$/.venv"
/>
<excludeFolder
url=
"file://$MODULE_DIR$/venv"
/>
</content>
<orderEntry
type=
"
jdk"
jdkName=
"Python 3.11 (GoogleNanoBananaApp)"
jdkType=
"Python SDK
"
/>
<orderEntry
type=
"
inheritedJdk
"
/>
<orderEntry
type=
"sourceFolder"
forTests=
"false"
/>
</component>
<component
name=
"PyDocumentationSettings"
>
...
...
image_generator.py
View file @
994a9ea
...
...
@@ -10,7 +10,7 @@ from PySide6.QtWidgets import (
QLabel
,
QLineEdit
,
QPushButton
,
QCheckBox
,
QTextEdit
,
QComboBox
,
QScrollArea
,
QGroupBox
,
QFileDialog
,
QMessageBox
,
QListWidget
,
QListWidgetItem
,
QTabWidget
,
QSplitter
,
QMenu
,
QProgressBar
QMenu
,
QProgressBar
,
QInputDialog
)
from
PySide6.QtCore
import
Qt
,
QThread
,
Signal
,
QSize
,
QTimer
,
QMimeData
from
PySide6.QtGui
import
QPixmap
,
QFont
,
QIcon
,
QDesktopServices
,
QAction
,
QImage
,
QDragEnterEvent
,
QDropEvent
...
...
@@ -25,6 +25,7 @@ import shutil
import
tempfile
import
platform
import
logging
import
random
from
pathlib
import
Path
from
google
import
genai
from
google.genai
import
types
...
...
@@ -1264,6 +1265,16 @@ class ImageGeneratorWindow(QMainWindow):
generation_tab
=
self
.
setup_generation_tab
()
self
.
tab_widget
.
addTab
(
generation_tab
,
"图片生成"
)
# Create style designer tab
try
:
jewelry_lib_manager
=
JewelryLibraryManager
(
self
.
get_config_dir
())
style_designer_tab
=
StyleDesignerTab
(
jewelry_lib_manager
,
parent
=
self
)
self
.
tab_widget
.
addTab
(
style_designer_tab
,
"款式设计"
)
self
.
logger
.
info
(
"款式设计 Tab 创建成功"
)
except
Exception
as
e
:
self
.
logger
.
error
(
f
"款式设计 Tab 创建失败: {e}"
)
# 即使失败也继续运行,不阻止应用启动
# Create history tab
history_tab
=
self
.
setup_history_tab
()
self
.
tab_widget
.
addTab
(
history_tab
,
"历史记录"
)
...
...
@@ -1286,7 +1297,7 @@ class ImageGeneratorWindow(QMainWindow):
# Upload button and count
upload_header
=
QHBoxLayout
()
upload_btn
=
QPushButton
(
"
+
添加图片"
)
upload_btn
=
QPushButton
(
"添加图片"
)
upload_btn
.
clicked
.
connect
(
self
.
upload_images
)
upload_header
.
addWidget
(
upload_btn
)
...
...
@@ -1357,7 +1368,7 @@ class ImageGeneratorWindow(QMainWindow):
self
.
saved_prompts_combo
=
QComboBox
()
prompt_toolbar
.
addWidget
(
self
.
saved_prompts_combo
)
delete_prompt_btn
=
QPushButton
(
"
🗑️
删除"
)
delete_prompt_btn
=
QPushButton
(
"删除"
)
delete_prompt_btn
.
clicked
.
connect
(
self
.
delete_saved_prompt
)
prompt_toolbar
.
addWidget
(
delete_prompt_btn
)
prompt_toolbar
.
addStretch
()
...
...
@@ -2090,7 +2101,7 @@ class ImageGeneratorWindow(QMainWindow):
processing_method
=
"格式转换"
if
processed_bytes
!=
self
.
generated_image_bytes
else
"原始数据"
self
.
logger
.
info
(
f
"图片下载完成 - 方法: {processing_method}, 文件: {file_path}, 大小: {file_size} 字节"
)
QMessageBox
.
information
(
self
,
"成功"
,
f
"图片已保存到:
\n
{file_path}
\n\n
文件大小: {file_size:,} 字节"
)
# 只使用状态栏提示,不显示弹窗
self
.
status_label
.
setText
(
"● 图片已保存"
)
self
.
status_label
.
setStyleSheet
(
"QLabel { color: #34C759; }"
)
except
Exception
as
e
:
...
...
@@ -2527,6 +2538,874 @@ class ImageGenerationWorker(QThread):
self
.
error
.
emit
(
error_msg
)
# ============================================================================
# 珠宝设计功能模块
# ============================================================================
# 默认珠宝词库(纯中文)
DEFAULT_JEWELRY_LIBRARY
=
{
"主石形状"
:
[
"椭圆形"
,
"圆形"
,
"祖母绿形"
,
"梨形"
,
"垫形"
,
"公主方形"
,
"心形"
],
"主石材质"
:
[
"黑发晶"
,
"莫桑石"
,
"红宝石"
,
"蓝宝石"
,
"绿碧玺"
,
"黄水晶带天然包裹体"
,
"钻石"
,
"粉红蓝宝石"
],
"金属"
:
[
"14K黄金"
,
"18K玫瑰金"
,
"14K白金"
,
"铂金(PT950)"
,
"双色14K黄金+白金"
,
"950银镀铑"
,
"钛金属"
],
"花头形式"
:
[
"花卉风格光环"
,
"经典圆形光环"
,
"复古风格米粒边光环"
,
"双层光环"
,
"几何六边形光环"
,
"非对称光环"
,
"简约爪镶无光环"
],
"戒臂结构"
:
[
"扭转戒臂"
,
"分裂戒臂"
,
"刀刃戒臂"
,
"大教堂戒臂"
,
"交叉戒臂"
,
"直线平滑戒臂"
,
"三股编织戒臂"
],
"戒臂处理"
:
[
"雕刻镂空花丝工艺"
,
"密钉镶戒臂"
,
"抛光平滑戒臂"
,
"浮雕雕刻"
,
"编织纹理"
,
"极简干净戒臂"
,
"穿孔镂空细节"
,
"锤纹处理"
],
"特殊元素"
:
[
"凯尔特结图案"
,
"装饰艺术几何元素"
,
"自然风格叶子图案"
,
"复古米粒边细节"
,
"哥特式图案元素"
,
"天体图案(星星、月亮)"
,
"藤蔓装饰曲线"
,
"蝴蝶结装饰"
],
"辅石镶嵌"
:
[
"密钉镶"
,
"微密钉镶"
,
"槽镶"
,
"珠粒镶"
,
"共爪镶"
,
"包镶"
,
"轨道镶"
]
}
class
JewelryLibraryManager
:
"""珠宝词库管理器"""
def
__init__
(
self
,
config_dir
:
Path
):
"""初始化词库管理器
Args:
config_dir: 配置文件目录
"""
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
config_dir
=
config_dir
self
.
config_path
=
config_dir
/
"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
)
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
()
self
.
save_library
(
library
)
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) # 可以选择是否移除
# 保存迁移后的数据
self
.
save_library
(
library
)
self
.
logger
.
info
(
f
"数据迁移完成:形状 {len(shapes)} 个,材质 {len(materials)} 个"
)
return
library
def
_extract_shape
(
self
,
item
:
str
)
->
Optional
[
str
]:
"""从主石描述中提取形状"""
shapes
=
[
"椭圆形"
,
"圆形"
,
"祖母绿形"
,
"梨形"
,
"垫形"
,
"公主方形"
,
"心形"
]
for
shape
in
shapes
:
if
item
.
startswith
(
shape
):
return
shape
return
None
def
_extract_material
(
self
,
item
:
str
)
->
Optional
[
str
]:
"""从主石描述中提取材质"""
shapes
=
[
"椭圆形"
,
"圆形"
,
"祖母绿形"
,
"梨形"
,
"垫形"
,
"公主方形"
,
"心形"
]
for
shape
in
shapes
:
if
item
.
startswith
(
shape
):
material
=
item
[
len
(
shape
):]
# 去除可能的连接词
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则保存当前词库
"""
if
library
is
None
:
library
=
self
.
library
try
:
self
.
config_dir
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
with
open
(
self
.
config_path
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
json
.
dump
(
library
,
f
,
ensure_ascii
=
False
,
indent
=
2
)
self
.
logger
.
info
(
f
"珠宝词库保存成功: {self.config_path}"
)
except
Exception
as
e
:
self
.
logger
.
error
(
f
"珠宝词库保存失败: {e}"
)
raise
def
add_item
(
self
,
category
:
str
,
value
:
str
):
"""添加词库项
Args:
category: 类别名称(如"主石")
value: 词条内容(纯中文)
"""
if
category
not
in
self
.
library
:
self
.
library
[
category
]
=
[]
if
value
not
in
self
.
library
[
category
]:
self
.
library
[
category
]
.
append
(
value
)
self
.
save_library
()
self
.
logger
.
info
(
f
"添加词库项: {category} -> {value}"
)
else
:
self
.
logger
.
warning
(
f
"词库项已存在: {category} -> {value}"
)
def
remove_item
(
self
,
category
:
str
,
value
:
str
):
"""删除词库项
Args:
category: 类别名称
value: 词条内容
"""
if
category
in
self
.
library
and
value
in
self
.
library
[
category
]:
self
.
library
[
category
]
.
remove
(
value
)
self
.
save_library
()
self
.
logger
.
info
(
f
"删除词库项: {category} -> {value}"
)
else
:
self
.
logger
.
warning
(
f
"词库项不存在: {category} -> {value}"
)
def
reset_category
(
self
,
category
:
str
):
"""恢复单个类别的默认词库
Args:
category: 类别名称
"""
if
category
in
DEFAULT_JEWELRY_LIBRARY
:
self
.
library
[
category
]
=
DEFAULT_JEWELRY_LIBRARY
[
category
]
.
copy
()
self
.
save_library
()
self
.
logger
.
info
(
f
"恢复默认词库: {category}"
)
else
:
self
.
logger
.
warning
(
f
"未知的类别: {category}"
)
def
reset_all
(
self
):
"""恢复所有类别的默认词库"""
self
.
library
=
DEFAULT_JEWELRY_LIBRARY
.
copy
()
self
.
save_library
()
self
.
logger
.
info
(
"恢复所有默认词库"
)
class
PromptAssembler
:
"""Prompt 组装器(纯中文)"""
BASE_TEMPLATE
=
"""一款高端精品珠宝戒指设计,主石为{主石},镶嵌于{金属}中。戒头采用{花头形式}围绕主石。戒臂采用{戒臂结构}设计,表面处理为{戒臂处理}。风格元素包括{特殊元素}。辅石采用{辅石镶嵌}增加光彩。高端珠宝渲染,干净的摄影棚光线,精准的金属抛光,强调宝石清晰度,不要出现手部。"""
@staticmethod
def
assemble
(
form_data
:
Dict
[
str
,
str
])
->
str
:
"""智能组装 prompt,处理空值
Args:
form_data: 表单数据,key为字段名,value为选中的值
Returns:
组装后的 prompt 字符串
"""
# 提取字段值(支持新旧两种格式)
center_stone
=
form_data
.
get
(
"主石"
,
""
)
.
strip
()
# 向后兼容
# 新格式:拆分形状和材质
center_stone_shape
=
form_data
.
get
(
"主石形状"
,
""
)
.
strip
()
center_stone_material
=
form_data
.
get
(
"主石材质"
,
""
)
.
strip
()
metal
=
form_data
.
get
(
"金属"
,
""
)
.
strip
()
halo_style
=
form_data
.
get
(
"花头形式"
,
""
)
.
strip
()
shank_structure
=
form_data
.
get
(
"戒臂结构"
,
""
)
.
strip
()
shank_treatment
=
form_data
.
get
(
"戒臂处理"
,
""
)
.
strip
()
special_motifs
=
form_data
.
get
(
"特殊元素"
,
""
)
.
strip
()
accent_setting
=
form_data
.
get
(
"辅石镶嵌"
,
""
)
.
strip
()
# 构建 prompt 片段
parts
=
[]
# 构建主石描述(优先使用新格式)
if
center_stone_shape
and
center_stone_material
:
# 新格式:形状+材质
main_stone_desc
=
f
"{center_stone_shape}{center_stone_material}"
elif
center_stone
:
# 旧格式:直接使用
main_stone_desc
=
center_stone
elif
center_stone_shape
:
# 只有形状
main_stone_desc
=
center_stone_shape
elif
center_stone_material
:
# 只有材质
main_stone_desc
=
center_stone_material
else
:
main_stone_desc
=
""
# 主体部分(主石和金属)
if
main_stone_desc
and
metal
:
parts
.
append
(
f
"一款高端精品珠宝戒指设计,主石为{main_stone_desc},镶嵌于{metal}中。"
)
elif
metal
:
parts
.
append
(
f
"一款高端精品珠宝戒指设计,镶嵌于{metal}中。"
)
elif
main_stone_desc
:
parts
.
append
(
f
"一款高端精品珠宝戒指设计,主石为{main_stone_desc}。"
)
else
:
parts
.
append
(
"一款高端精品珠宝戒指设计。"
)
# 戒头部分
if
halo_style
:
if
main_stone_desc
:
parts
.
append
(
f
"戒头采用{halo_style}围绕主石。"
)
else
:
parts
.
append
(
f
"戒头采用{halo_style}设计。"
)
# 戒臂部分
if
shank_structure
and
shank_treatment
:
parts
.
append
(
f
"戒臂采用{shank_structure}设计,表面处理为{shank_treatment}。"
)
elif
shank_structure
:
parts
.
append
(
f
"戒臂采用{shank_structure}设计。"
)
elif
shank_treatment
:
parts
.
append
(
f
"戒臂表面处理为{shank_treatment}。"
)
# 特殊元素
if
special_motifs
:
parts
.
append
(
f
"风格元素包括{special_motifs}。"
)
# 辅石镶嵌
if
accent_setting
:
parts
.
append
(
f
"辅石采用{accent_setting}增加光彩。"
)
# 固定的渲染要求
parts
.
append
(
"高端珠宝渲染,干净的摄影棚光线,精准的金属抛光,强调宝石清晰度,不要出现手部。"
)
return
""
.
join
(
parts
)
class
StyleDesignerTab
(
QWidget
):
"""款式设计 Tab(纯中文)"""
def
__init__
(
self
,
library_manager
:
JewelryLibraryManager
,
parent
=
None
):
super
()
.
__init__
(
parent
)
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
library_manager
=
library_manager
self
.
parent_window
=
parent
# 字段顺序
self
.
categories
=
[
"主石形状"
,
"主石材质"
,
"金属"
,
"花头形式"
,
"戒臂结构"
,
"戒臂处理"
,
"特殊元素"
,
"辅石镶嵌"
]
# 存储每个类别的 ComboBox
self
.
combo_boxes
=
{}
# 生成相关
self
.
generated_image_bytes
=
None
self
.
setup_ui
()
def
setup_ui
(
self
):
"""创建 UI 布局"""
main_layout
=
QVBoxLayout
()
main_layout
.
setContentsMargins
(
10
,
10
,
10
,
10
)
main_layout
.
setSpacing
(
15
)
# 表单区域 - 每行两列布局
form_group
=
QGroupBox
(
"珠宝元素选择"
)
form_layout
=
QVBoxLayout
()
# 使用网格布局实现每行两列
grid_layout
=
QGridLayout
()
grid_layout
.
setHorizontalSpacing
(
20
)
# 列间距
grid_layout
.
setVerticalSpacing
(
10
)
# 行间距
for
i
,
category
in
enumerate
(
self
.
categories
):
field_widget
=
self
.
create_field_widget
(
category
)
row
=
i
//
2
col
=
i
%
2
grid_layout
.
addWidget
(
field_widget
,
row
,
col
)
form_layout
.
addLayout
(
grid_layout
)
# 全局按钮区域
buttons_layout
=
QHBoxLayout
()
# 随机生成按钮
random_btn
=
QPushButton
(
"🎲 随机生成参数"
)
random_btn
.
clicked
.
connect
(
self
.
randomize_parameters
)
random_btn
.
setStyleSheet
(
"""
QPushButton {
background-color: #4CAF50;
color: white;
padding: 8px;
border-radius: 4px;
font-weight: bold;
}
QPushButton:hover {
background-color: #45a049;
}
"""
)
buttons_layout
.
addWidget
(
random_btn
)
# 全局恢复按钮
reset_all_btn
=
QPushButton
(
"🔄 恢复所有默认词库"
)
reset_all_btn
.
clicked
.
connect
(
self
.
reset_all_library
)
reset_all_btn
.
setStyleSheet
(
"""
QPushButton {
background-color: #ff9800;
color: white;
padding: 8px;
border-radius: 4px;
font-weight: bold;
}
QPushButton:hover {
background-color: #f57c00;
}
"""
)
buttons_layout
.
addWidget
(
reset_all_btn
)
buttons_layout
.
addStretch
()
form_layout
.
addLayout
(
buttons_layout
)
form_group
.
setLayout
(
form_layout
)
main_layout
.
addWidget
(
form_group
)
# Content row: Prompt (left) + Settings (right) - 与图片生成页面保持一致
content_row
=
QHBoxLayout
()
# Prompt section
prompt_group
=
QGroupBox
(
"Prompt 预览"
)
prompt_layout
=
QVBoxLayout
()
self
.
prompt_preview
=
QTextEdit
()
self
.
prompt_preview
.
setReadOnly
(
True
)
prompt_layout
.
addWidget
(
self
.
prompt_preview
)
prompt_group
.
setLayout
(
prompt_layout
)
content_row
.
addWidget
(
prompt_group
,
2
)
# Settings section
settings_group
=
QGroupBox
(
"生成设置"
)
settings_layout
=
QVBoxLayout
()
aspect_label
=
QLabel
(
"宽高比"
)
aspect_label
.
setStyleSheet
(
"QLabel { font-size: 14px; line-height: 18px; }"
)
settings_layout
.
addWidget
(
aspect_label
)
self
.
aspect_ratio
=
QComboBox
()
self
.
aspect_ratio
.
addItems
([
"1:1"
,
"2:3"
,
"3:2"
,
"3:4"
,
"4:3"
,
"16:9"
])
settings_layout
.
addWidget
(
self
.
aspect_ratio
)
settings_layout
.
addSpacing
(
10
)
size_label
=
QLabel
(
"图片尺寸"
)
size_label
.
setStyleSheet
(
"QLabel { font-size: 14px; line-height: 18px; }"
)
settings_layout
.
addWidget
(
size_label
)
self
.
image_size
=
QComboBox
()
self
.
image_size
.
addItems
([
"1K"
,
"2K"
,
"4K"
])
self
.
image_size
.
setCurrentIndex
(
1
)
settings_layout
.
addWidget
(
self
.
image_size
)
settings_layout
.
addSpacing
(
10
)
settings_layout
.
addStretch
()
settings_group
.
setLayout
(
settings_layout
)
content_row
.
addWidget
(
settings_group
,
1
)
main_layout
.
addLayout
(
content_row
)
# Action buttons - 与图片生成页面保持一致
action_layout
=
QHBoxLayout
()
self
.
generate_btn
=
QPushButton
(
"🎨 生成珠宝图片"
)
self
.
generate_btn
.
clicked
.
connect
(
self
.
generate_image
)
self
.
generate_btn
.
setStyleSheet
(
"""
QPushButton {
background-color: #007AFF;
color: white;
padding: 12px;
border-radius: 4px;
font-size: 14px;
font-weight: bold;
}
QPushButton:hover {
background-color: #0051D5;
}
"""
)
action_layout
.
addWidget
(
self
.
generate_btn
)
self
.
download_btn
=
QPushButton
(
"💾 下载图片"
)
self
.
download_btn
.
clicked
.
connect
(
self
.
download_image
)
self
.
download_btn
.
setEnabled
(
False
)
action_layout
.
addWidget
(
self
.
download_btn
)
self
.
status_label
=
QLabel
(
"● 就绪"
)
self
.
status_label
.
setStyleSheet
(
"QLabel { font-size: 14px; line-height: 18px; }"
)
action_layout
.
addWidget
(
self
.
status_label
)
action_layout
.
addStretch
()
main_layout
.
addLayout
(
action_layout
)
# Preview section - 与图片生成页面保持一致
preview_group
=
QGroupBox
(
"预览"
)
preview_layout
=
QVBoxLayout
()
self
.
result_label
=
QLabel
(
"生成的图片将在这里显示
\n
双击用系统查看器打开"
)
self
.
result_label
.
setAlignment
(
Qt
.
AlignCenter
)
self
.
result_label
.
setMinimumHeight
(
300
)
self
.
result_label
.
setMinimumWidth
(
400
)
self
.
result_label
.
setStyleSheet
(
"""
QLabel {
color: #999999;
font-size: 14px;
line-height: 18px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: #f9f9f9;
}
"""
)
self
.
result_label
.
setScaledContents
(
False
)
# 保存原始的双击处理方法引用
self
.
original_mouseDoubleClickEvent
=
self
.
result_label
.
mouseDoubleClickEvent
# 重写双击事件
self
.
result_label
.
mouseDoubleClickEvent
=
self
.
open_fullsize_view
preview_layout
.
addWidget
(
self
.
result_label
)
preview_group
.
setLayout
(
preview_layout
)
main_layout
.
addWidget
(
preview_group
,
1
)
main_layout
.
addStretch
()
self
.
setLayout
(
main_layout
)
# 初始化 prompt 预览
self
.
update_prompt_preview
()
def
randomize_parameters
(
self
):
"""随机生成一套参数"""
for
category
,
combo
in
self
.
combo_boxes
.
items
():
if
combo
.
count
()
>
0
:
random_index
=
random
.
randint
(
0
,
combo
.
count
()
-
1
)
combo
.
setCurrentIndex
(
random_index
)
# 触发prompt预览更新
self
.
update_prompt_preview
()
def
create_field_widget
(
self
,
category
:
str
)
->
QWidget
:
"""创建单个字段的 UI 组件"""
widget
=
QWidget
()
layout
=
QHBoxLayout
()
layout
.
setContentsMargins
(
0
,
0
,
0
,
0
)
# 标签
label
=
QLabel
(
f
"{category}:"
)
label
.
setMinimumWidth
(
70
)
# 减小标签宽度以适应两列布局
label
.
setStyleSheet
(
"QLabel { font-size: 14px; line-height: 18px; }"
)
layout
.
addWidget
(
label
)
# 下拉框
combo
=
QComboBox
()
combo
.
addItem
(
""
)
# 空选项
items
=
self
.
library_manager
.
library
.
get
(
category
,
[])
combo
.
addItems
(
items
)
combo
.
currentTextChanged
.
connect
(
self
.
update_prompt_preview
)
self
.
combo_boxes
[
category
]
=
combo
layout
.
addWidget
(
combo
,
3
)
# 添加按钮
add_btn
=
QPushButton
(
"添加"
)
add_btn
.
clicked
.
connect
(
lambda
:
self
.
add_library_item
(
category
))
add_btn
.
setFixedWidth
(
60
)
layout
.
addWidget
(
add_btn
)
# 删除按钮
del_btn
=
QPushButton
(
"删除"
)
del_btn
.
clicked
.
connect
(
lambda
:
self
.
remove_library_item
(
category
))
del_btn
.
setFixedWidth
(
60
)
layout
.
addWidget
(
del_btn
)
widget
.
setLayout
(
layout
)
return
widget
def
update_prompt_preview
(
self
):
"""实时更新 prompt 预览"""
form_data
=
{}
for
category
,
combo
in
self
.
combo_boxes
.
items
():
form_data
[
category
]
=
combo
.
currentText
()
prompt
=
PromptAssembler
.
assemble
(
form_data
)
self
.
prompt_preview
.
setPlainText
(
prompt
)
def
add_library_item
(
self
,
category
:
str
):
"""添加词库项 UI"""
value
,
ok
=
QInputDialog
.
getText
(
self
,
f
"添加{category}词库项"
,
f
"请输入新的{category}词条(纯中文):"
,
QLineEdit
.
Normal
)
if
ok
and
value
.
strip
():
value
=
value
.
strip
()
try
:
self
.
library_manager
.
add_item
(
category
,
value
)
# 刷新下拉框
self
.
refresh_combo_box
(
category
)
QMessageBox
.
information
(
self
,
"成功"
,
f
"已添加词库项: {value}"
)
except
Exception
as
e
:
self
.
logger
.
error
(
f
"添加词库项失败: {e}"
)
QMessageBox
.
warning
(
self
,
"错误"
,
f
"添加失败: {e}"
)
else
:
self
.
logger
.
info
(
f
"用户取消了添加{category}词库项的操作"
)
def
remove_library_item
(
self
,
category
:
str
):
"""删除词库项 UI"""
combo
=
self
.
combo_boxes
.
get
(
category
)
if
not
combo
:
return
current_value
=
combo
.
currentText
()
if
not
current_value
:
QMessageBox
.
warning
(
self
,
"提示"
,
"请先选择要删除的词库项"
)
return
reply
=
QMessageBox
.
question
(
self
,
"确认删除"
,
f
"确定要删除词库项
\"
{current_value}
\"
吗?"
,
QMessageBox
.
Yes
|
QMessageBox
.
No
)
if
reply
==
QMessageBox
.
Yes
:
try
:
self
.
library_manager
.
remove_item
(
category
,
current_value
)
# 刷新下拉框
self
.
refresh_combo_box
(
category
)
QMessageBox
.
information
(
self
,
"成功"
,
f
"已删除词库项: {current_value}"
)
except
Exception
as
e
:
QMessageBox
.
warning
(
self
,
"错误"
,
f
"删除失败: {e}"
)
def
reset_category_library
(
self
,
category
:
str
):
"""恢复单个类别的默认词库 UI"""
reply
=
QMessageBox
.
question
(
self
,
"确认恢复"
,
f
"确定要恢复
\"
{category}
\"
的默认词库吗?
\n
这将删除所有自定义词条。"
,
QMessageBox
.
Yes
|
QMessageBox
.
No
)
if
reply
==
QMessageBox
.
Yes
:
try
:
self
.
library_manager
.
reset_category
(
category
)
# 刷新下拉框
self
.
refresh_combo_box
(
category
)
QMessageBox
.
information
(
self
,
"成功"
,
f
"已恢复 {category} 的默认词库"
)
except
Exception
as
e
:
QMessageBox
.
warning
(
self
,
"错误"
,
f
"恢复失败: {e}"
)
def
reset_all_library
(
self
):
"""恢复所有字段的默认词库 UI"""
reply
=
QMessageBox
.
question
(
self
,
"确认恢复所有默认词库"
,
"确定要恢复所有字段的默认词库吗?
\n
这将删除所有自定义词条。"
,
QMessageBox
.
Yes
|
QMessageBox
.
No
)
if
reply
==
QMessageBox
.
Yes
:
try
:
self
.
library_manager
.
reset_all
()
# 刷新所有下拉框
for
category
in
self
.
categories
:
self
.
refresh_combo_box
(
category
)
QMessageBox
.
information
(
self
,
"成功"
,
"已恢复所有默认词库"
)
except
Exception
as
e
:
QMessageBox
.
warning
(
self
,
"错误"
,
f
"恢复失败: {e}"
)
def
open_fullsize_view
(
self
,
event
):
"""双击打开完整尺寸查看器"""
if
not
self
.
generated_image_bytes
:
QMessageBox
.
warning
(
self
,
"提示"
,
"没有可预览的图片"
)
return
# 保存为临时文件并使用系统查看器打开
import
tempfile
with
tempfile
.
NamedTemporaryFile
(
suffix
=
'.png'
,
delete
=
False
)
as
tmp_file
:
tmp_file
.
write
(
self
.
generated_image_bytes
)
temp_path
=
Path
(
tmp_file
.
name
)
# 使用系统默认程序打开
QDesktopServices
.
openUrl
(
QUrl
.
fromLocalFile
(
str
(
temp_path
)))
def
refresh_combo_box
(
self
,
category
:
str
):
"""刷新指定类别的下拉框"""
combo
=
self
.
combo_boxes
.
get
(
category
)
if
not
combo
:
return
# 保存当前选中值
current_value
=
combo
.
currentText
()
# 清空并重新加载
combo
.
clear
()
combo
.
addItem
(
""
)
# 空选项
items
=
self
.
library_manager
.
library
.
get
(
category
,
[])
combo
.
addItems
(
items
)
# 尝试恢复选中值
index
=
combo
.
findText
(
current_value
)
if
index
>=
0
:
combo
.
setCurrentIndex
(
index
)
else
:
combo
.
setCurrentIndex
(
0
)
def
generate_image
(
self
):
"""调用文生图 API"""
# 获取 prompt
prompt
=
self
.
prompt_preview
.
toPlainText
()
if
not
prompt
.
strip
():
QMessageBox
.
warning
(
self
,
"提示"
,
"Prompt 不能为空"
)
return
# 获取设置
aspect_ratio
=
self
.
aspect_ratio
.
currentText
()
image_size
=
self
.
image_size
.
currentText
()
model
=
"imagen-3.0-generate-002"
# 硬编码使用默认模型
# 获取父窗口的 API key
if
not
hasattr
(
self
.
parent_window
,
'api_key'
)
or
not
self
.
parent_window
.
api_key
:
QMessageBox
.
warning
(
self
,
"错误"
,
"未找到 API 密钥,请在主窗口配置"
)
return
# 非阻塞状态提示
self
.
generate_btn
.
setEnabled
(
False
)
self
.
status_label
.
setText
(
"● 生成中..."
)
QApplication
.
processEvents
()
try
:
# 创建生成线程
self
.
gen_thread
=
ImageGenerationWorker
(
api_key
=
self
.
parent_window
.
api_key
,
prompt
=
prompt
,
images
=
[],
aspect_ratio
=
aspect_ratio
,
image_size
=
image_size
,
model
=
model
)
self
.
gen_thread
.
finished
.
connect
(
self
.
on_generation_success
)
self
.
gen_thread
.
error
.
connect
(
self
.
on_generation_error
)
self
.
gen_thread
.
start
()
except
Exception
as
e
:
self
.
generate_btn
.
setEnabled
(
True
)
self
.
status_label
.
setText
(
"● 就绪"
)
QMessageBox
.
critical
(
self
,
"错误"
,
f
"生成失败: {e}"
)
def
on_generation_success
(
self
,
image_bytes
:
bytes
):
"""生成成功回调"""
# 恢复按钮状态
self
.
generate_btn
.
setEnabled
(
True
)
self
.
status_label
.
setText
(
"● 就绪"
)
self
.
generated_image_bytes
=
image_bytes
# 启用下载按钮
self
.
download_btn
.
setEnabled
(
True
)
# 显示图片
pixmap
=
QPixmap
()
pixmap
.
loadFromData
(
image_bytes
)
# 清除之前的文本
self
.
result_label
.
setText
(
""
)
# 获取标签可用空间
label_size
=
self
.
result_label
.
size
()
if
label_size
.
width
()
<
100
or
label_size
.
height
()
<
100
:
# 如果标签还没有正确的尺寸,使用默认尺寸
label_size
=
QSize
(
400
,
300
)
# 缩放图片以适应显示区域,保持宽高比且完整显示
scaled_pixmap
=
pixmap
.
scaled
(
label_size
,
Qt
.
KeepAspectRatio
,
Qt
.
SmoothTransformation
)
# 设置缩放后的图片,并确保居中对齐
self
.
result_label
.
setPixmap
(
scaled_pixmap
)
self
.
result_label
.
setAlignment
(
Qt
.
AlignCenter
)
# 保存到历史记录
try
:
# 添加到历史记录管理器
if
hasattr
(
self
.
parent_window
,
'history_manager'
):
timestamp
=
self
.
parent_window
.
history_manager
.
save_generation
(
image_bytes
=
image_bytes
,
prompt
=
self
.
prompt_preview
.
toPlainText
(),
reference_images
=
[],
# 款式设计无参考图
aspect_ratio
=
self
.
aspect_ratio
.
currentText
(),
image_size
=
self
.
image_size
.
currentText
(),
model
=
"imagen-3.0-generate-002"
)
self
.
logger
.
info
(
f
"款式设计已添加到历史记录: {timestamp}"
)
# 刷新历史记录列表
if
hasattr
(
self
.
parent_window
,
'refresh_history'
):
self
.
parent_window
.
refresh_history
()
else
:
self
.
logger
.
warning
(
"未找到历史记录管理器"
)
except
Exception
as
e
:
self
.
logger
.
error
(
f
"保存历史记录失败: {e}"
)
# 更新状态提示
self
.
status_label
.
setText
(
"● 图片生成成功"
)
self
.
status_label
.
setStyleSheet
(
"QLabel { color: #34C759; }"
)
def
on_generation_error
(
self
,
error_msg
:
str
):
"""生成失败回调"""
# 恢复按钮状态
self
.
generate_btn
.
setEnabled
(
True
)
self
.
status_label
.
setText
(
"● 就绪"
)
QMessageBox
.
critical
(
self
,
"生成失败"
,
error_msg
)
def
download_image
(
self
):
"""下载图片"""
if
not
self
.
generated_image_bytes
:
QMessageBox
.
warning
(
self
,
"提示"
,
"没有可下载的图片"
)
return
file_path
,
_
=
QFileDialog
.
getSaveFileName
(
self
,
"保存图片"
,
f
"jewelry_design_{datetime.now().strftime('
%
Y
%
m
%
d
%
H
%
M
%
S')}.png"
,
"PNG Files (*.png)"
)
if
file_path
:
try
:
with
open
(
file_path
,
'wb'
)
as
f
:
f
.
write
(
self
.
generated_image_bytes
)
# 使用状态栏提示而不是弹窗
self
.
status_label
.
setText
(
"● 图片已保存"
)
self
.
status_label
.
setStyleSheet
(
"QLabel { color: #34C759; }"
)
except
Exception
as
e
:
QMessageBox
.
critical
(
self
,
"错误"
,
f
"保存失败: {e}"
)
def
preview_image
(
self
):
"""预览大图"""
if
not
self
.
generated_image_bytes
:
QMessageBox
.
warning
(
self
,
"提示"
,
"没有可预览的图片"
)
return
# 创建预览对话框
dialog
=
QDialog
(
self
)
dialog
.
setWindowTitle
(
"预览大图"
)
dialog
.
resize
(
800
,
600
)
layout
=
QVBoxLayout
()
# 图片标签
label
=
QLabel
()
pixmap
=
QPixmap
()
pixmap
.
loadFromData
(
self
.
generated_image_bytes
)
label
.
setPixmap
(
pixmap
)
label
.
setScaledContents
(
True
)
# 滚动区域
scroll
=
QScrollArea
()
scroll
.
setWidget
(
label
)
scroll
.
setWidgetResizable
(
True
)
layout
.
addWidget
(
scroll
)
# 关闭按钮
close_btn
=
QPushButton
(
"关闭"
)
close_btn
.
clicked
.
connect
(
dialog
.
close
)
layout
.
addWidget
(
close_btn
)
dialog
.
setLayout
(
layout
)
dialog
.
exec
()
def
main
():
"""Main application entry point"""
# 初始化日志系统
...
...
jewelry_library.json
0 → 100644
View file @
994a9ea
{
"主石"
:
[
"椭圆形黑发晶"
,
"圆形莫桑石"
,
"祖母绿形红宝石"
,
"梨形蓝宝石"
,
"垫形绿碧玺"
,
"椭圆形黄水晶带天然包裹体"
,
"公主方形钻石"
,
"心形粉红蓝宝石"
],
"金属"
:
[
"14K黄金"
,
"18K玫瑰金"
,
"14K白金"
,
"铂金(PT950)"
,
"双色14K黄金+白金"
,
"950银镀铑"
,
"钛金属"
],
"花头形式"
:
[
"花卉风格光环"
,
"经典圆形光环"
,
"复古风格米粒边光环"
,
"双层光环"
,
"几何六边形光环"
,
"非对称光环"
,
"简约爪镶无光环"
],
"戒臂结构"
:
[
"扭转戒臂"
,
"分裂戒臂"
,
"刀刃戒臂"
,
"大教堂戒臂"
,
"交叉戒臂"
,
"直线平滑戒臂"
,
"三股编织戒臂"
],
"戒臂处理"
:
[
"雕刻镂空花丝工艺"
,
"密钉镶戒臂"
,
"抛光平滑戒臂"
,
"浮雕雕刻"
,
"编织纹理"
,
"极简干净戒臂"
,
"穿孔镂空细节"
,
"锤纹处理"
],
"特殊元素"
:
[
"凯尔特结图案"
,
"装饰艺术几何元素"
,
"自然风格叶子图案"
,
"复古米粒边细节"
,
"哥特式图案元素"
,
"天体图案(星星、月亮)"
,
"藤蔓装饰曲线"
,
"蝴蝶结装饰"
],
"辅石镶嵌"
:
[
"密钉镶"
,
"微密钉镶"
,
"槽镶"
,
"珠粒镶"
,
"共爪镶"
,
"包镶"
,
"轨道镶"
],
"主石形状"
:
[
"椭圆形"
,
"圆形"
,
"祖母绿形"
,
"梨形"
,
"垫形"
,
"公主方形"
,
"心形"
],
"主石材质"
:
[
"黑发晶"
,
"莫桑石"
,
"红宝石"
,
"蓝宝石"
,
"绿碧玺"
,
"黄水晶带天然包裹体"
,
"钻石"
,
"粉红蓝宝石"
]
}
\ No newline at end of file
zb100_icon.png
0 → 100644
View file @
994a9ea
2.2 MB
Please
register
or
sign in
to post a comment