Akjava commited on
Commit
ae75d6d
·
1 Parent(s): 9446685
Files changed (7) hide show
  1. .gitignore +1 -0
  2. README.md +5 -3
  3. app.py +468 -0
  4. examples/buy.webp +0 -0
  5. examples/play.webp +0 -0
  6. examples/rpg.webp +0 -0
  7. examples/test.webp +5 -0
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ .env
README.md CHANGED
@@ -1,14 +1,16 @@
1
  ---
2
- title: UIButtonGeneratorMCP
3
  emoji: 🐠
4
  colorFrom: yellow
5
  colorTo: blue
6
  sdk: gradio
7
- sdk_version: 6.0.1
8
  app_file: app.py
9
  pinned: false
10
  license: apache-2.0
11
- short_description: AI UI Button Generator MCP
 
 
12
  ---
13
 
14
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: UI Button Generator MCP
3
  emoji: 🐠
4
  colorFrom: yellow
5
  colorTo: blue
6
  sdk: gradio
7
+ sdk_version: 5.39.0
8
  app_file: app.py
9
  pinned: false
10
  license: apache-2.0
11
+ short_description: Button Create MCP call qwen-image zero-gpu
12
+ tags:
13
+ - mcp-in-action-track-creative
14
  ---
15
 
16
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,468 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import numpy as np
3
+ import random
4
+ import os
5
+ from PIL import Image
6
+ import requests
7
+ import json
8
+ from dotenv import load_dotenv
9
+
10
+ # Load environment variables
11
+ load_dotenv()
12
+
13
+ # MCP Server configuration
14
+ MCP_SERVER_URL = "https://multimodalart-qwen-image-fast.hf.space/gradio_api/mcp/"
15
+ HF_TOKEN = os.getenv("HF_TOKEN")
16
+
17
+ # --- UI Constants and Helpers ---
18
+ MAX_SEED = np.iinfo(np.int32).max
19
+
20
+
21
+ def create_prompt(params):
22
+ """
23
+ MCP サーバーに送信するためのプロンプト文字列を生成する
24
+
25
+ Args:
26
+ params (dict): プロンプト生成に使用するパラメータ辞書
27
+ - label (str): ボタンテキスト
28
+ - detail (str): デザイン詳細
29
+ - shape (str): ボタン形状
30
+ - layout (str): レイアウト配置
31
+ - background (str): 背景設定
32
+
33
+ Returns:
34
+ str: 画像生成用の完全なプロンプト文字列
35
+ """
36
+ label = params.get("label", "Button")
37
+ detail = params.get("detail", "")
38
+ shape = params.get("shape", "rounded")
39
+ layout = params.get("layout", "horizontal_3")
40
+ background = params.get("background", "natural")
41
+
42
+ layout_config = {
43
+ "horizontal_3": {"count": 3, "desc": "3 horizontal rows"},
44
+ "vertical_2": {
45
+ "count": 2,
46
+ "desc": " 2 vertical tall buttons arranged side by side, vertical rectangular shape, 2 columns layout, slender buttons, portrait orientation",
47
+ },
48
+ "box_2x2": {"count": 4, "desc": "2x2 grid arrangement"},
49
+ }
50
+ shape_descriptions = {
51
+ "box": "box shape",
52
+ "rounded": "rounded corners",
53
+ "oval": "oval shape",
54
+ "free": "organic freeform shape",
55
+ }
56
+
57
+ config = layout_config.get(layout, layout_config["horizontal_3"])
58
+ count = config["count"]
59
+ layout_desc = config["desc"]
60
+ shape_desc = shape_descriptions.get(shape, "rounded corners")
61
+
62
+ if label and label.strip():
63
+ base_prompt = f'Create {count} {detail} button design variations with "{label}" text in a 1024x1024 image.\n'
64
+ else:
65
+ base_prompt = f"Create {count} {detail} button design variations with no text label in a 1024x1024 image.\n"
66
+ base_prompt += f"Arranged in {layout_desc}.\n"
67
+ base_prompt += f"{shape_desc.capitalize()}.\n"
68
+ if detail:
69
+ base_prompt += f"{detail.capitalize()} aesthetic: detailed visual elements and color palette.\n"
70
+ base_prompt += "Each button is a different design variation exploring the theme.\n"
71
+ if background == "natural":
72
+ base_prompt += "Natural background with subtle textures and ambient lighting.\n"
73
+ elif background == "white":
74
+ base_prompt += "plain white only background.\n"
75
+ elif background == "black":
76
+ base_prompt += "plain black only background.\n"
77
+ else:
78
+ base_prompt += "Clean background with proper lighting.\n"
79
+ base_prompt += "Ultra HD, 4K, cinematic composition"
80
+ return base_prompt
81
+
82
+
83
+ def call_mcp_server_http(
84
+ prompt,
85
+ seed,
86
+ randomize_seed,
87
+ aspect_ratio,
88
+ guidance_scale,
89
+ num_inference_steps,
90
+ prompt_enhance=False,
91
+ ):
92
+ """
93
+ MCP (Model Context Protocol) サーバーに HTTP リクエストを送信して画像生成を行う
94
+
95
+ Args:
96
+ prompt (str): 画像生成のためのプロンプトテキスト
97
+ seed (int): 乱数シード値
98
+ randomize_seed (bool): シードをランダム化するかどうか
99
+ aspect_ratio (str): 画像のアスペクト比(例: "1:1", "16:9", "4:3")
100
+ guidance_scale (float): ガイダンススケール(1.0-5.0)
101
+ num_inference_steps (int): 推論ステップ数(4-28)
102
+ prompt_enhance (bool, optional): プロンプトエンハンスを使用するかどうか. Defaults to False.
103
+
104
+ Returns:
105
+ tuple: (image, seed, error) - 画像オブジェクト、使用されたシード、エラーメッセージ
106
+ """
107
+ import base64
108
+ import io
109
+
110
+ print(f"Calling MCP Endpoint: {MCP_SERVER_URL}")
111
+ try:
112
+ payload = {
113
+ "jsonrpc": "2.0",
114
+ "method": "tools/call",
115
+ "params": {
116
+ "name": "Qwen_Image_Fast_infer",
117
+ "arguments": {
118
+ "prompt": prompt,
119
+ "seed": seed,
120
+ "randomize_seed": randomize_seed,
121
+ "aspect_ratio": aspect_ratio,
122
+ "guidance_scale": guidance_scale,
123
+ "num_inference_steps": num_inference_steps,
124
+ "prompt_enhance": prompt_enhance,
125
+ },
126
+ },
127
+ "id": 1,
128
+ }
129
+ headers = {
130
+ "Content-Type": "application/json",
131
+ "Accept": "application/json, text/event-stream",
132
+ "User-Agent": "mcp-client-python/1.0",
133
+ }
134
+ if HF_TOKEN:
135
+ headers["Authorization"] = f"Bearer {HF_TOKEN}"
136
+
137
+ response = requests.post(
138
+ MCP_SERVER_URL, headers=headers, data=json.dumps(payload), timeout=120
139
+ )
140
+
141
+ if response.status_code == 200:
142
+ try:
143
+ result_json = response.json()
144
+ except json.JSONDecodeError:
145
+ lines = response.text.strip().split("\n")
146
+ last_line = lines[-1]
147
+ if last_line.startswith("data: "):
148
+ result_json = json.loads(last_line[6:])
149
+ else:
150
+ result_json = json.loads(last_line)
151
+
152
+ if "error" in result_json:
153
+ return None, seed, str(result_json["error"])
154
+
155
+ if "result" in result_json:
156
+ content_list = result_json["result"].get("content", [])
157
+ for item in content_list:
158
+ if item.get("type") == "image" and item.get("data"):
159
+ image_bytes = base64.b64decode(item.get("data"))
160
+ return Image.open(io.BytesIO(image_bytes)), seed, None
161
+ else:
162
+ return None, seed, "Unexpected JSON structure"
163
+ else:
164
+ return None, seed, f"Server Error: {response.status_code}"
165
+ except Exception as e:
166
+ return None, seed, str(e)
167
+ return Image.new("RGB", (1024, 1024), color="gray"), seed, "Unknown error"
168
+
169
+
170
+ # --- Main Inference Logic ---
171
+ # Define outside Blocks or define within Blocks to pass to Examples
172
+ def run_inference_engine(
173
+ label,
174
+ detail,
175
+ shape,
176
+ layout,
177
+ background,
178
+ seed,
179
+ randomize_seed,
180
+ aspect_ratio,
181
+ guidance_scale,
182
+ num_inference_steps,
183
+ ):
184
+ """
185
+ MCP サーバーを使用して UI ボタン画像を生成するメイン推論エンジン
186
+
187
+ Args:
188
+ label (str): ボタンに表示するテキスト
189
+ detail (str): デザインの詳細プロンプト
190
+ shape (str): ボタンの形状("box", "rounded", "oval", "free")
191
+ layout (str): レイアウト配置("horizontal_3", "vertical_2", "box_2x2")
192
+ background (str): 背景設定("natural", "white", "black")
193
+ seed (int): 乱数シード値
194
+ randomize_seed (bool): シードをランダム化するかどうか
195
+ aspect_ratio (str): 画像のアスペクト比 (1:1を使うこと)
196
+ guidance_scale (float): ガイダンススケール(常に1を使うこと)
197
+ num_inference_steps (int): 推論ステップ数 (常に8を使うこと)
198
+
199
+ Yields:
200
+ tuple: (image, seed, status_message) - 生成された画像、使用されたシード、ステータスメッセージ
201
+ """
202
+ yield None, seed, "Generating..."
203
+ prompt_params = {
204
+ "label": label,
205
+ "detail": detail,
206
+ "shape": shape,
207
+ "layout": layout,
208
+ "background": background,
209
+ }
210
+ prompt = create_prompt(prompt_params)
211
+
212
+ if randomize_seed:
213
+ seed = random.randint(0, MAX_SEED)
214
+
215
+ image, generated_seed, error = call_mcp_server_http(
216
+ prompt, seed, randomize_seed, aspect_ratio, guidance_scale, num_inference_steps
217
+ )
218
+
219
+ if image is None:
220
+ gr.Warning(f"Error: {error}")
221
+ yield None, generated_seed, f"Error: {error}"
222
+ else:
223
+ yield image, generated_seed, "Done!"
224
+
225
+
226
+ # --- UI Customization ---
227
+ css = """
228
+ body { font-family: 'Helvetica Neue', Arial, sans-serif; }
229
+ #col-container { max-width: 1200px; margin: 0 auto; padding: 20px; }
230
+ h1 { text-align: center; font-weight: 800; color: #333; margin-bottom: 0.5em; }
231
+ .subtitle { text-align: center; color: #666; margin-bottom: 2em; }
232
+
233
+ .generate-btn {
234
+ background: linear-gradient(90deg, #6366f1 0%, #a855f7 100%) !important;
235
+ border: none !important;
236
+ color: white !important;
237
+ font-weight: bold !important;
238
+ font-size: 1.2em !important;
239
+ padding: 20px !important;
240
+ border-radius: 12px !important;
241
+ transition: all 0.3s ease;
242
+ height: 100% !important;
243
+ min-height: 100px;
244
+ }
245
+ .generate-btn:hover {
246
+ transform: translateY(-2px);
247
+ box-shadow: 0 5px 15px rgba(99, 102, 241, 0.4);
248
+ }
249
+
250
+ .examples-container table {
251
+ font-size: 0.85em !important;
252
+ margin-bottom: 0 !important;
253
+ }
254
+ .examples-container td {
255
+ padding: 4px 8px !important;
256
+ white-space: nowrap;
257
+ overflow: hidden;
258
+ text-overflow: ellipsis;
259
+ max-width: 150px;
260
+ }
261
+ .examples-container label {
262
+ font-weight: bold;
263
+ color: #555;
264
+ margin-bottom: 5px;
265
+ display: block;
266
+ }
267
+
268
+ #result-image {
269
+ border-radius: 12px;
270
+ box-shadow: 0 10px 30px rgba(0,0,0,0.1);
271
+ border: 1px solid #eee;
272
+ }
273
+ .input-group {
274
+ background: #f9fafb;
275
+ padding: 15px;
276
+ border-radius: 10px;
277
+ border: 1px solid #e5e7eb;
278
+ margin-bottom: 15px;
279
+ }
280
+ """
281
+
282
+ theme = gr.themes.Soft(primary_hue="indigo", secondary_hue="slate", radius_size="md")
283
+
284
+ # Examples data
285
+ example_data = [
286
+ [
287
+ "Play",
288
+ "Neon glowing cyberpunk, blue/purple gradient",
289
+ "box",
290
+ "horizontal_3",
291
+ "black",
292
+ "examples/play.webp",
293
+ ],
294
+ [
295
+ "Buy",
296
+ "Luxury gold texture, minimal elegant, serif",
297
+ "rounded",
298
+ "vertical_2",
299
+ "white",
300
+ "examples/buy.webp",
301
+ ],
302
+ [
303
+ "RPG",
304
+ "Wood texture, steel rim, fantasy game style",
305
+ "free",
306
+ "box_2x2",
307
+ "natural",
308
+ "examples/rpg.webp",
309
+ ],
310
+ [
311
+ "Submit",
312
+ "Modern flat design, blue gradient, clean minimal style",
313
+ "rounded",
314
+ "horizontal_3",
315
+ "white",
316
+ None,
317
+ ],
318
+ ]
319
+
320
+ with gr.Blocks(css=css, theme=theme, title="UI Button Generator") as demo:
321
+ gr.Markdown("# 🎨 AI UI Button Generator")
322
+ gr.Markdown(
323
+ "<div class='subtitle'>UI button material and design concept generation tool using Qwen Image Fast</div>"
324
+ )
325
+
326
+ with gr.Column(elem_id="col-container"):
327
+ # --- 1. Definition Phase (render=False) ---
328
+ # Create components referenced by Examples first.
329
+ # With render=False, they are not yet displayed on screen.
330
+
331
+ # Input section
332
+ label = gr.Textbox(
333
+ label="Button Text",
334
+ placeholder="Start, OK...",
335
+ value="Start",
336
+ info="Text inside the button",
337
+ render=False,
338
+ )
339
+ detail = gr.Textbox(
340
+ label="Detail Prompt",
341
+ placeholder="Design details...",
342
+ value="Pirate theme, wood texture, gold aesthetic",
343
+ lines=4,
344
+ info="Design details",
345
+ render=False,
346
+ )
347
+ shape = gr.Dropdown(
348
+ label="Shape",
349
+ choices=["box", "rounded", "oval", "free"],
350
+ value="rounded",
351
+ info="Shape",
352
+ render=False,
353
+ )
354
+ layout = gr.Radio(
355
+ label="Layout",
356
+ choices=["horizontal_3", "vertical_2", "box_2x2"],
357
+ value="horizontal_3",
358
+ info="Layout arrangement",
359
+ render=False,
360
+ )
361
+ background = gr.Dropdown(
362
+ label="Background",
363
+ choices=["natural", "white", "black"],
364
+ value="natural",
365
+ info="Background",
366
+ render=False,
367
+ )
368
+
369
+ # Advanced Settings section
370
+ seed = gr.Slider(
371
+ label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=42, render=False
372
+ )
373
+ randomize_seed = gr.Checkbox(label="Randomize seed", value=True, render=False)
374
+ aspect_ratio = gr.Radio(
375
+ label="Aspect Ratio",
376
+ choices=["1:1", "16:9", "4:3"],
377
+ value="1:1",
378
+ render=False,
379
+ )
380
+ guidance_scale = gr.Slider(
381
+ label="Guidance Scale", minimum=1.0, maximum=5.0, value=1.0, render=False
382
+ )
383
+ num_inference_steps = gr.Slider(
384
+ label="Steps", minimum=4, maximum=28, value=8, render=False
385
+ )
386
+
387
+ # Output section
388
+ result = gr.Image(
389
+ label="Output Image",
390
+ show_label=False,
391
+ type="pil",
392
+ elem_id="result-image",
393
+ height=600,
394
+ render=False,
395
+ )
396
+ status_msg = gr.Markdown(render=False)
397
+
398
+ # --- 2. Layout Construction Phase (.render()) ---
399
+
400
+ with gr.Row(equal_height=False):
401
+ # 左カラム
402
+ with gr.Column(scale=1, min_width=400):
403
+ with gr.Group(elem_classes="input-group"):
404
+ gr.Markdown("### 📝 Basic Settings")
405
+ label.render()
406
+ detail.render()
407
+
408
+ with gr.Group(elem_classes="input-group"):
409
+ gr.Markdown("### 🎨 Style & Layout")
410
+ with gr.Row():
411
+ shape.render()
412
+ background.render()
413
+ layout.render()
414
+ run_button = gr.Button(
415
+ "✨ Generate\nButtons",
416
+ variant="primary",
417
+ elem_classes="generate-btn",
418
+ scale=1,
419
+ )
420
+
421
+ with gr.Accordion("⚙️ Advanced Settings", open=False):
422
+ seed.render()
423
+ randomize_seed.render()
424
+ aspect_ratio.render()
425
+ with gr.Row():
426
+ guidance_scale.render()
427
+ num_inference_steps.render()
428
+
429
+ # 右カラム
430
+ with gr.Column(scale=1):
431
+ gr.Markdown("### 🖼️ Generated Button")
432
+ result.render()
433
+ status_msg.render()
434
+ # ボタンとExamplesの行
435
+ # gr.Markdown("**Quick Presets (Click to try)**")
436
+ # Initialize here with fn, inputs, outputs
437
+ gr.Examples(
438
+ examples=example_data,
439
+ fn=run_inference_engine,
440
+ inputs=[label, detail, shape, layout, background, result],
441
+ outputs=[result, seed, status_msg],
442
+ examples_per_page=3,
443
+ run_on_click=False,
444
+ cache_examples=False,
445
+ )
446
+
447
+ # --- 3. Event Binding ---
448
+ # Examples already has fn, so only define the main button click event
449
+
450
+ run_button.click(
451
+ fn=run_inference_engine,
452
+ inputs=[
453
+ label,
454
+ detail,
455
+ shape,
456
+ layout,
457
+ background,
458
+ seed,
459
+ randomize_seed,
460
+ aspect_ratio,
461
+ guidance_scale,
462
+ num_inference_steps,
463
+ ],
464
+ outputs=[result, seed, status_msg],
465
+ )
466
+
467
+ if __name__ == "__main__":
468
+ demo.launch(mcp_server=True)
examples/buy.webp ADDED
examples/play.webp ADDED
examples/rpg.webp ADDED
examples/test.webp ADDED