8ac7635f by 柴进

nanobanana初版

1 parent 9b6295c7
1 {
2 "api_key": "AIzaSyBRIH8iItqTZXg7d4zAWVNhyit2CZbXWIU",
3 "saved_prompts": [
4 "生成一张白人夫妻正常的买家秀展现我的戒指",
5 "主石换成闪耀的鸽血红红宝石,火彩非常好",
6 "生成一张白人夫妻在自家后院里的椅子坐着,亲密姿态,展现笑容,妻子手背捂嘴笑来展现我的戒指,两个人年龄大概40岁上下,男性微胖,女性短发,男士和女士脸上有一些痕迹,手要略显粗糙,普通人长相",
7 "一副美丽的嵖岈山风景,冬日清晨,初雪"
8 ],
9 "db_config": {
10 "host": "rm-uf6hx9ka75jdm2474xo.mysql.rds.aliyuncs.com",
11 "port": 3306,
12 "user": "root",
13 "password": "Np2KfoleLon3nqDpDbdjPa3Rqe",
14 "database": "saas_user",
15 "table": "nano_banana_users"
16 },
17 "last_user": "testuser",
18 "saved_password_hash": ""
19 }
...\ No newline at end of file ...\ No newline at end of file
...@@ -21,6 +21,7 @@ from google.genai import types ...@@ -21,6 +21,7 @@ from google.genai import types
21 import threading 21 import threading
22 import hashlib 22 import hashlib
23 import pymysql 23 import pymysql
24 from datetime import datetime
24 25
25 26
26 def hash_password(password: str) -> str: 27 def hash_password(password: str) -> str:
...@@ -384,13 +385,14 @@ class ImageGeneratorApp: ...@@ -384,13 +385,14 @@ class ImageGeneratorApp:
384 style.configure('Accent.TButton', 385 style.configure('Accent.TButton',
385 background=accent_color, 386 background=accent_color,
386 foreground='white', 387 foreground='white',
387 borderwidth=0, 388 borderwidth=1,
389 relief='solid',
388 focuscolor='none', 390 focuscolor='none',
389 font=('Segoe UI', 10), 391 font=('Segoe UI', 10),
390 padding=(18, 8)) 392 padding=(18, 8))
391 393
392 style.map('Accent.TButton', 394 style.map('Accent.TButton',
393 background=[('active', hover_color), ('pressed', hover_color)], 395 background=[('active', hover_color), ('pressed', '#003D99')],
394 foreground=[('disabled', '#999999')]) 396 foreground=[('disabled', '#999999')])
395 397
396 # Secondary button style 398 # Secondary button style
...@@ -398,23 +400,24 @@ class ImageGeneratorApp: ...@@ -398,23 +400,24 @@ class ImageGeneratorApp:
398 background=secondary_bg, 400 background=secondary_bg,
399 foreground=text_color, 401 foreground=text_color,
400 borderwidth=1, 402 borderwidth=1,
401 relief='flat', 403 relief='solid',
402 font=('Segoe UI', 9), 404 font=('Segoe UI', 9),
403 padding=(12, 6)) 405 padding=(12, 6))
404 406
405 style.map('Secondary.TButton', 407 style.map('Secondary.TButton',
406 background=[('active', '#e8e8e8'), ('pressed', '#d8d8d8')]) 408 background=[('active', '#e8e8e8'), ('pressed', '#c8c8c8')])
407 409
408 # Icon button style (small, subtle) 410 # Icon button style (small, subtle)
409 style.configure('Icon.TButton', 411 style.configure('Icon.TButton',
410 background=bg_color, 412 background=bg_color,
411 foreground='#666666', 413 foreground='#666666',
412 borderwidth=0, 414 borderwidth=1,
415 relief='solid',
413 font=('Segoe UI', 9), 416 font=('Segoe UI', 9),
414 padding=(4, 4)) 417 padding=(4, 4))
415 418
416 style.map('Icon.TButton', 419 style.map('Icon.TButton',
417 background=[('active', secondary_bg)], 420 background=[('active', secondary_bg), ('pressed', '#d8d8d8')],
418 foreground=[('active', accent_color)]) 421 foreground=[('active', accent_color)])
419 422
420 # Delete button style (visible with red hover) 423 # Delete button style (visible with red hover)
...@@ -470,6 +473,7 @@ class ImageGeneratorApp: ...@@ -470,6 +473,7 @@ class ImageGeneratorApp:
470 # 初始化默认值 473 # 初始化默认值
471 self.db_config = None 474 self.db_config = None
472 self.last_user = "" 475 self.last_user = ""
476 self.saved_password_hash = ""
473 477
474 # Try to load from user config first 478 # Try to load from user config first
475 if config_path.exists(): 479 if config_path.exists():
...@@ -480,6 +484,7 @@ class ImageGeneratorApp: ...@@ -480,6 +484,7 @@ class ImageGeneratorApp:
480 self.saved_prompts = config.get("saved_prompts", []) 484 self.saved_prompts = config.get("saved_prompts", [])
481 self.db_config = config.get("db_config") 485 self.db_config = config.get("db_config")
482 self.last_user = config.get("last_user", "") 486 self.last_user = config.get("last_user", "")
487 self.saved_password_hash = config.get("saved_password_hash", "")
483 except Exception as e: 488 except Exception as e:
484 print(f"Failed to load config from {config_path}: {e}") 489 print(f"Failed to load config from {config_path}: {e}")
485 490
...@@ -525,6 +530,12 @@ class ImageGeneratorApp: ...@@ -525,6 +530,12 @@ class ImageGeneratorApp:
525 else: 530 else:
526 config["last_user"] = "" 531 config["last_user"] = ""
527 532
533 # 添加保存的密码哈希
534 if hasattr(self, 'saved_password_hash'):
535 config["saved_password_hash"] = self.saved_password_hash
536 else:
537 config["saved_password_hash"] = ""
538
528 # Ensure directory exists 539 # Ensure directory exists
529 config_path.parent.mkdir(parents=True, exist_ok=True) 540 config_path.parent.mkdir(parents=True, exist_ok=True)
530 541
...@@ -590,7 +601,7 @@ class ImageGeneratorApp: ...@@ -590,7 +601,7 @@ class ImageGeneratorApp:
590 prompt_toolbar.pack(fill="x", pady=(0, 8)) 601 prompt_toolbar.pack(fill="x", pady=(0, 8))
591 602
592 self.save_prompt_btn = ttk.Button(prompt_toolbar, text="⭐ 收藏", 603 self.save_prompt_btn = ttk.Button(prompt_toolbar, text="⭐ 收藏",
593 command=self.save_prompt, 604 command=self.toggle_favorite,
594 style='Icon.TButton') 605 style='Icon.TButton')
595 self.save_prompt_btn.pack(side="left", padx=(0, 5)) 606 self.save_prompt_btn.pack(side="left", padx=(0, 5))
596 self.bind_hover_effect(self.save_prompt_btn) 607 self.bind_hover_effect(self.save_prompt_btn)
...@@ -617,6 +628,9 @@ class ImageGeneratorApp: ...@@ -617,6 +628,9 @@ class ImageGeneratorApp:
617 self.prompt_text.pack(fill="both", expand=True) 628 self.prompt_text.pack(fill="both", expand=True)
618 self.prompt_text.insert("1.0", "一幅美丽的风景画,有山有湖,日落时分") 629 self.prompt_text.insert("1.0", "一幅美丽的风景画,有山有湖,日落时分")
619 630
631 # Bind text change event to check favorite status
632 self.prompt_text.bind('<<Modified>>', self.on_prompt_change)
633
620 # Right: Settings Section 634 # Right: Settings Section
621 settings_container = ttk.LabelFrame(content_row, text="生成设置", padding=12) 635 settings_container = ttk.LabelFrame(content_row, text="生成设置", padding=12)
622 settings_container.pack(side="right", fill="y") 636 settings_container.pack(side="right", fill="y")
...@@ -633,14 +647,7 @@ class ImageGeneratorApp: ...@@ -633,14 +647,7 @@ class ImageGeneratorApp:
633 self.image_size = ttk.Combobox(settings_container, width=18, state="readonly") 647 self.image_size = ttk.Combobox(settings_container, width=18, state="readonly")
634 self.image_size['values'] = ("1K", "2K", "4K") 648 self.image_size['values'] = ("1K", "2K", "4K")
635 self.image_size.current(1) 649 self.image_size.current(1)
636 self.image_size.pack(fill="x", pady=(0, 12)) 650 self.image_size.pack(fill="x", pady=(0, 0))
637
638 # Model
639 ttk.Label(settings_container, text="AI 模型", foreground='#666666').pack(anchor="w", pady=(0, 4))
640 self.model = ttk.Combobox(settings_container, width=18, state="readonly")
641 self.model['values'] = ("gemini-3-pro-image-preview")
642 self.model.current(0)
643 self.model.pack(fill="x", pady=(0, 0))
644 651
645 # Action buttons 652 # Action buttons
646 action_frame = ttk.Frame(main_container) 653 action_frame = ttk.Frame(main_container)
...@@ -719,27 +726,42 @@ class ImageGeneratorApp: ...@@ -719,27 +726,42 @@ class ImageGeneratorApp:
719 else: 726 else:
720 self.saved_prompts_combo['values'] = [] 727 self.saved_prompts_combo['values'] = []
721 728
722 def save_prompt(self): 729 def check_favorite_status(self):
723 """Save current prompt to favorites""" 730 """Check if current prompt is favorited and update button state"""
731 prompt = self.prompt_text.get("1.0", tk.END).strip()
732 if prompt in self.saved_prompts:
733 self.save_prompt_btn.config(text="✓ 已收藏")
734 else:
735 self.save_prompt_btn.config(text="⭐ 收藏")
736
737 def on_prompt_change(self, event=None):
738 """Callback when prompt text changes"""
739 # Clear the modified flag to avoid repeated triggers
740 self.prompt_text.edit_modified(False)
741 self.check_favorite_status()
742
743 def toggle_favorite(self):
744 """Toggle favorite status of current prompt"""
724 prompt = self.prompt_text.get("1.0", tk.END).strip() 745 prompt = self.prompt_text.get("1.0", tk.END).strip()
725 if not prompt: 746 if not prompt:
726 self.status_label.config(text="● 提示词不能为空", foreground='#FF3B30') 747 self.status_label.config(text="● 提示词不能为空", foreground='#FF3B30')
727 return 748 return
728 749
729 if prompt in self.saved_prompts: 750 if prompt in self.saved_prompts:
730 self.status_label.config(text="● 该提示词已收藏", foreground='#FF9500') 751 # Remove from favorites
731 self.save_prompt_btn.config(text="✓ 已收藏") 752 self.saved_prompts.remove(prompt)
732 # 2秒后恢复按钮文本 753 self.save_config()
733 self.root.after(2000, lambda: self.save_prompt_btn.config(text="⭐ 收藏")) 754 self.update_saved_prompts_list()
734 return 755 self.status_label.config(text="● 该提示词已取消收藏", foreground='#34C759')
735 756 else:
757 # Add to favorites
736 self.saved_prompts.append(prompt) 758 self.saved_prompts.append(prompt)
737 self.save_config() 759 self.save_config()
738 self.update_saved_prompts_list() 760 self.update_saved_prompts_list()
739 self.status_label.config(text="● 已收藏提示词", foreground='#34C759') 761 self.status_label.config(text="● 该提示词已收藏", foreground='#34C759')
740 self.save_prompt_btn.config(text="✓ 已收藏") 762
741 # 2秒后恢复按钮文本 763 # Update button state
742 self.root.after(2000, lambda: self.save_prompt_btn.config(text="⭐ 收藏")) 764 self.check_favorite_status()
743 765
744 def load_saved_prompt(self, event): 766 def load_saved_prompt(self, event):
745 """Load a saved prompt""" 767 """Load a saved prompt"""
...@@ -748,6 +770,7 @@ class ImageGeneratorApp: ...@@ -748,6 +770,7 @@ class ImageGeneratorApp:
748 self.prompt_text.delete("1.0", tk.END) 770 self.prompt_text.delete("1.0", tk.END)
749 self.prompt_text.insert("1.0", self.saved_prompts[index]) 771 self.prompt_text.insert("1.0", self.saved_prompts[index])
750 self.status_label.config(text="● 已加载提示词", foreground='#007AFF') 772 self.status_label.config(text="● 已加载提示词", foreground='#007AFF')
773 self.check_favorite_status()
751 774
752 def delete_saved_prompt(self): 775 def delete_saved_prompt(self):
753 """Delete the currently selected saved prompt""" 776 """Delete the currently selected saved prompt"""
...@@ -929,7 +952,7 @@ class ImageGeneratorApp: ...@@ -929,7 +952,7 @@ class ImageGeneratorApp:
929 952
930 # Generate 953 # Generate
931 response = client.models.generate_content( 954 response = client.models.generate_content(
932 model=self.model.get(), 955 model="gemini-3-pro-image-preview",
933 contents=content_parts, 956 contents=content_parts,
934 config=config 957 config=config
935 ) 958 )
...@@ -1028,8 +1051,12 @@ class ImageGeneratorApp: ...@@ -1028,8 +1051,12 @@ class ImageGeneratorApp:
1028 messagebox.showerror("错误", "没有可下载的图片!") 1051 messagebox.showerror("错误", "没有可下载的图片!")
1029 return 1052 return
1030 1053
1054 # 生成默认文件名: 时间戳格式 YYYYMMDDHHMMSS.png
1055 default_filename = datetime.now().strftime("%Y%m%d%H%M%S.png")
1056
1031 file_path = filedialog.asksaveasfilename( 1057 file_path = filedialog.asksaveasfilename(
1032 defaultextension=".png", 1058 defaultextension=".png",
1059 initialfile=default_filename,
1033 filetypes=[("PNG 文件", "*.png"), ("JPEG 文件", "*.jpg"), ("所有文件", "*.*")], 1060 filetypes=[("PNG 文件", "*.png"), ("JPEG 文件", "*.jpg"), ("所有文件", "*.*")],
1034 title="保存图片" 1061 title="保存图片"
1035 ) 1062 )
......