Skip to content

Commit fa97faf

Browse files
author
Bryan Howard
committed
meme gen graph (doesn't work yet)
1 parent 7b8262b commit fa97faf

File tree

1 file changed

+173
-0
lines changed

1 file changed

+173
-0
lines changed

examples/meme_generator_graph.json

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
{
2+
"nodes": [
3+
{
4+
"uuid": "meme-list-scraper",
5+
"title": "Scrape Meme List",
6+
"pos": [
7+
-95.17264343750003,
8+
274.51478562499994
9+
],
10+
"code": "import requests\nfrom bs4 import BeautifulSoup\nimport json\n\n@node_entry\ndef scrape_meme_list() -> str:\n try:\n headers = {'User-Agent': 'Mozilla/5.0'}\n resp = requests.get('https://imgflip.com/memetemplates', headers=headers)\n resp.raise_for_status()\n soup = BeautifulSoup(resp.text, 'html.parser')\n memes = []\n for link in soup.select('.mt-box a.mt-title'):\n name = link.get_text(strip=True)\n href = link.get('href')\n if href and name:\n url = 'https://imgflip.com' + href\n memes.append({'name': name, 'url': url})\n # Return the list as a JSON string\n return json.dumps(memes)\n except Exception as e:\n return json.dumps([{'name': f'ERROR: {e}', 'url': ''}])",
11+
"gui_code": "",
12+
"gui_get_values_code": "",
13+
"gui_state": {},
14+
"colors": {
15+
"title": "#2a2a2a",
16+
"body": "#141414"
17+
}
18+
},
19+
{
20+
"uuid": "meme-selector",
21+
"title": "Meme Selector",
22+
"pos": [
23+
208.49250000000006,
24+
253.40999999999994
25+
],
26+
"code": "@node_entry\ndef meme_selector(selected_url: str) -> str:\n # This node simply passes the selected URL through.\n if not selected_url:\n return 'ERROR: No meme selected from the list.'\n return selected_url",
27+
"gui_code": "from PySide6.QtWidgets import QLabel, QListWidget\n\nlayout.addWidget(QLabel('Select Meme Template:', parent))\nwidgets['meme_list'] = QListWidget(parent)\nwidgets['meme_list'].setMinimumHeight(200)\nlayout.addWidget(widgets['meme_list'])",
28+
"gui_get_values_code": "import json\n\ndef get_values(widgets):\n # Get the selected item and extract its URL\n item = widgets['meme_list'].currentItem()\n if item:\n try:\n # The URL is stored in the item's data\n return {'selected_url': item.data(32)}\n except Exception:\n return {'selected_url': ''}\n return {'selected_url': ''}\n\ndef set_values(widgets, outputs):\n # This function is called when the node receives its input data\n meme_data_str = outputs.get('meme_data', '[]')\n try:\n memes = json.loads(meme_data_str)\n widgets['meme_list'].clear()\n for meme in memes:\n from PySide6.QtWidgets import QListWidgetItem\n item = QListWidgetItem(meme['name'])\n item.setData(32, meme['url']) # Store URL in the item's data role\n widgets['meme_list'].addItem(item)\n except json.JSONDecodeError:\n widgets['meme_list'].clear()\n widgets['meme_list'].addItem('Error: Invalid meme list data')",
29+
"gui_state": {
30+
"selected_url": ""
31+
},
32+
"colors": {
33+
"title": "#2a2a2a",
34+
"body": "#141414"
35+
}
36+
},
37+
{
38+
"uuid": "fetch-meme-template",
39+
"title": "Fetch Meme Template",
40+
"pos": [
41+
525.685,
42+
270.60249999999996
43+
],
44+
"code": "import requests\nfrom bs4 import BeautifulSoup\nfrom urllib.parse import urljoin\n\n@node_entry\ndef fetch_template_image(template_url: str) -> str:\n if not template_url or template_url.startswith('ERROR'):\n return template_url\n try:\n headers = {'User-Agent': 'Mozilla/5.0'}\n page_response = requests.get(template_url, headers=headers)\n page_response.raise_for_status()\n soup = BeautifulSoup(page_response.text, 'html.parser')\n img_tag = soup.find('img', {'id': 'im'})\n if not img_tag or not img_tag.get('src'):\n return 'ERROR: Could not find template image on page.'\n \n img_src = img_tag.get('src')\n img_url = urljoin(template_url, img_src)\n\n img_response = requests.get(img_url, headers=headers)\n img_response.raise_for_status()\n\n output_filename = 'downloaded_template.jpg'\n with open(output_filename, 'wb') as f:\n f.write(img_response.content)\n return output_filename\n except Exception as e:\n return f'ERROR: {e}'",
45+
"gui_code": "",
46+
"gui_get_values_code": "",
47+
"gui_state": {},
48+
"colors": {
49+
"title": "#2a2a2a",
50+
"body": "#141414"
51+
}
52+
},
53+
{
54+
"uuid": "meme-text-input",
55+
"title": "Meme Text",
56+
"pos": [
57+
530.7143678124997,
58+
452.01135718750004
59+
],
60+
"code": "from typing import Tuple\n\n@node_entry\ndef get_meme_text(top_text: str, bottom_text: str) -> Tuple[str, str]:\n return top_text, bottom_text",
61+
"gui_code": "from PySide6.QtWidgets import QLabel, QLineEdit\n\nlayout.addWidget(QLabel('Top Text:', parent))\nwidgets['top_text'] = QLineEdit('WHEN YOU REFACTOR THE GRAPH', parent)\nlayout.addWidget(widgets['top_text'])\n\nlayout.addWidget(QLabel('Bottom Text:', parent))\nwidgets['bottom_text'] = QLineEdit('SO IT ACTUALLY WORKS', parent)\nlayout.addWidget(widgets['bottom_text'])",
62+
"gui_get_values_code": "def get_values(widgets):\n return {\n 'top_text': widgets['top_text'].text(),\n 'bottom_text': widgets['bottom_text'].text()\n }\n\ndef set_initial_state(widgets, state):\n if 'top_text' in state: widgets['top_text'].setText(state['top_text'])\n if 'bottom_text' in state: widgets['bottom_text'].setText(state['bottom_text'])",
63+
"gui_state": {
64+
"top_text": "WHEN YOU REFACTOR THE GRAPH",
65+
"bottom_text": "SO IT ACTUALLY WORKS"
66+
},
67+
"colors": {
68+
"title": "#2a2a2a",
69+
"body": "#141414"
70+
}
71+
},
72+
{
73+
"uuid": "font-settings",
74+
"title": "Font Settings",
75+
"pos": [
76+
559.1811474999998,
77+
735.6127206249998
78+
],
79+
"code": "@node_entry\ndef get_font_settings(font_size: int) -> int:\n return font_size",
80+
"gui_code": "from PySide6.QtWidgets import QLabel, QSpinBox\n\nlayout.addWidget(QLabel('Font Size:', parent))\nwidgets['font_size'] = QSpinBox(parent)\nwidgets['font_size'].setRange(10, 100)\nwidgets['font_size'].setValue(40)\nlayout.addWidget(widgets['font_size'])",
81+
"gui_get_values_code": "def get_values(widgets):\n return {'font_size': widgets['font_size'].value()}\n\ndef set_initial_state(widgets, state):\n if 'font_size' in state: widgets['font_size'].setValue(state['font_size'])",
82+
"gui_state": {
83+
"font_size": 40
84+
},
85+
"colors": {
86+
"title": "#2a2a2a",
87+
"body": "#141414"
88+
}
89+
},
90+
{
91+
"uuid": "render-meme",
92+
"title": "Render Meme",
93+
"pos": [
94+
905.3871484374997,
95+
348.1161428124999
96+
],
97+
"code": "from PIL import Image, ImageDraw, ImageFont\n\n@node_entry\ndef render_meme(base_image_path: str, top_text: str, bottom_text: str, font_size: int) -> str:\n if not base_image_path or base_image_path.startswith('ERROR'):\n return base_image_path\n try:\n img = Image.open(base_image_path)\n draw = ImageDraw.Draw(img)\n try:\n font = ImageFont.truetype(\"arial.ttf\", font_size)\n except IOError:\n font = ImageFont.load_default()\n\n def draw_text_with_outline(pos, text, font, draw_context):\n x, y = pos\n draw_context.text((x-2, y-2), text, font=font, fill='black')\n draw_context.text((x+2, y-2), text, font=font, fill='black')\n draw_context.text((x-2, y+2), text, font=font, fill='black')\n draw_context.text((x+2, y+2), text, font=font, fill='black')\n draw_context.text(pos, text, font=font, fill='white')\n\n top_text_bbox = draw.textbbox((0,0), top_text.upper(), font=font)\n top_text_width = top_text_bbox[2] - top_text_bbox[0]\n top_pos = ((img.width - top_text_width) / 2, 10)\n\n bottom_text_bbox = draw.textbbox((0,0), bottom_text.upper(), font=font)\n bottom_text_width = bottom_text_bbox[2] - bottom_text_bbox[0]\n bottom_text_height = bottom_text_bbox[3] - bottom_text_bbox[1]\n bottom_pos = ((img.width - bottom_text_width) / 2, img.height - bottom_text_height - 10)\n\n draw_text_with_outline(top_pos, top_text.upper(), font, draw)\n draw_text_with_outline(bottom_pos, bottom_text.upper(), font, draw)\n\n output_filename = 'final_meme.png'\n img.save(output_filename)\n return output_filename\n except Exception as e:\n return f'ERROR: {e}'",
98+
"gui_code": "",
99+
"gui_get_values_code": "",
100+
"gui_state": {},
101+
"colors": {
102+
"title": "#5c2a9d",
103+
"body": "#3c1d63"
104+
}
105+
},
106+
{
107+
"uuid": "image-preview",
108+
"title": "Image Preview",
109+
"pos": [
110+
1220.074006875,
111+
347.09599937499996
112+
],
113+
"code": "@node_entry\ndef show_image(image_path: str) -> str:\n return image_path",
114+
"gui_code": "from PySide6.QtWidgets import QLabel\nfrom PySide6.QtGui import QPixmap\nfrom PySide6.QtCore import Qt\n\nwidgets['image_label'] = QLabel('Execute graph to see preview...', parent)\nwidgets['image_label'].setMinimumSize(250, 200)\nwidgets['image_label'].setAlignment(Qt.AlignCenter)\nwidgets['image_label'].setStyleSheet('border: 1px solid #555;')\nlayout.addWidget(widgets['image_label'])",
115+
"gui_get_values_code": "def get_values(widgets):\n return {}\n\ndef set_values(widgets, outputs):\n from PySide6.QtGui import QPixmap\n from PySide6.QtCore import Qt\n image_path = outputs.get('output_1')\n if image_path and not image_path.startswith('ERROR'):\n pixmap = QPixmap(image_path)\n if not pixmap.isNull():\n scaled_pixmap = pixmap.scaled(widgets['image_label'].size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)\n widgets['image_label'].setPixmap(scaled_pixmap)\n else:\n widgets['image_label'].setText(f'Error: Could not load\\n{image_path}')\n else:\n widgets['image_label'].setText(f'Execution failed:\\n{image_path}')",
116+
"gui_state": {},
117+
"colors": {
118+
"title": "#2a2a2a",
119+
"body": "#141414"
120+
}
121+
}
122+
],
123+
"connections": [
124+
{
125+
"start_node_uuid": "meme-selector",
126+
"start_pin_name": "output_1",
127+
"end_node_uuid": "fetch-meme-template",
128+
"end_pin_name": "template_url"
129+
},
130+
{
131+
"start_node_uuid": "fetch-meme-template",
132+
"start_pin_name": "output_1",
133+
"end_node_uuid": "render-meme",
134+
"end_pin_name": "base_image_path"
135+
},
136+
{
137+
"start_node_uuid": "meme-text-input",
138+
"start_pin_name": "output_1",
139+
"end_node_uuid": "render-meme",
140+
"end_pin_name": "top_text"
141+
},
142+
{
143+
"start_node_uuid": "meme-text-input",
144+
"start_pin_name": "output_2",
145+
"end_node_uuid": "render-meme",
146+
"end_pin_name": "bottom_text"
147+
},
148+
{
149+
"start_node_uuid": "font-settings",
150+
"start_pin_name": "output_1",
151+
"end_node_uuid": "render-meme",
152+
"end_pin_name": "font_size"
153+
},
154+
{
155+
"start_node_uuid": "render-meme",
156+
"start_pin_name": "output_1",
157+
"end_node_uuid": "image-preview",
158+
"end_pin_name": "image_path"
159+
},
160+
{
161+
"start_node_uuid": "meme-list-scraper",
162+
"start_pin_name": "output_1",
163+
"end_node_uuid": "meme-selector",
164+
"end_pin_name": "selected_url"
165+
}
166+
],
167+
"requirements": [
168+
"Pillow",
169+
"requests",
170+
"beautifulsoup4"
171+
],
172+
"venv_path": "E:\\HOME\\PyFlowCanvas\\.venv_graph"
173+
}

0 commit comments

Comments
 (0)