I am trying to export an image from a mapbox component in my app to storage TBD (but let's say S3 or Google Drive for the sake of argument), ideally triggered by a certain action like clicking a button. I've tried to find a good starting point here in the forums, but am having a hard time. Does anyone have ideas on how I might approach this question?
hey @Gregory_Caplan i'm going to be attempting something similar this week. we want to build emails using Retool as the UI, and then send the email using HTML content of the Retool app.
what if you did something like Selenium in a python workflow block in retool?
it may be trickier than desired, since you would need a demo account with username/password, then automate entering those before grabbing the HTML content of the page.
chatGPT gave me something that looked promising, considering I've worked with Selenium before for unit testing and screenshotting. but not sure if Retool will play nice with this. doubt it would work for Oauth like Google.
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from PIL import Image
import time
# Set up Selenium with Chrome WebDriver
chrome_options = Options()
chrome_options.add_argument("--headless") # Run in headless mode (without opening a browser window)
service = Service('/path/to/chromedriver') # Update with your path to ChromeDriver
driver = webdriver.Chrome(service=service, options=chrome_options)
# URL of the Retool app
url = 'https://your-retool-app-url.com'
# Credentials if needed
username = 'your_username'
password = 'your_password'
# Open the Retool app
driver.get(url)
# Log in if necessary
# Replace these steps with the correct selectors and login process
driver.find_element(By.ID, 'username_input_id').send_keys(username)
driver.find_element(By.ID, 'password_input_id').send_keys(password)
driver.find_element(By.ID, 'login_button_id').click()
# Wait for the page to load
time.sleep(5)
# Locate the Mapbox component by its div selector
mapbox_div = driver.find_element(By.CSS_SELECTOR, 'div.mapbox-selector') # Replace with actual CSS selector
# Get the location and size of the Mapbox component
location = mapbox_div.location
size = mapbox_div.size
# Take a screenshot of the entire page
screenshot = driver.get_screenshot_as_png()
# Open the screenshot with PIL
image = Image.open(BytesIO(screenshot))
# Calculate the bounding box of the Mapbox component
left = location['x']
top = location['y']
right = location['x'] + size['width']
bottom = location['y'] + size['height']
# Crop the screenshot to the Mapbox component
mapbox_screenshot = image.crop((left, top, right, bottom))
# Save the screenshot
mapbox_screenshot.save('mapbox_screenshot.png')
# Close the browser
driver.quit()
print("Screenshot saved successfully.")
This is a bit of a tricky problem. I would typically recommend the downloadPage util function but it unfortunately does not work with embedded WebGL content like a mapbox. There are a ton of great tools for this exact use case - like htmltocanvas - but they all rely upon having access to the DOM, which Retool doesn't expose.
I'm really intrigued by @trz-justin-dev's suggestion, but I think the biggest hurdle would be installing the proper WebDriver. It's doable if you're running a self-hosted deployment but probably not if you're on cloud.
All that to say - I don't think there's a good solution right now. I'll talk to our team about getting the downloadPage utility to work properly with embedded WebGL content.
hey @Darren you’re spot on about WebDrivers. Selenium+Chrome was too big for a Retool Workflow VM but I switched to Gecko and had more success. had to switch back to customer features but hope to revisit soon.