Using images in Anvil Assets in a python-pptx slide

What I’m trying to do:
I like using Anvil Assets to store images for icons, etc. I’m trying to insert images in python-pptx slides but, I don’t understand how to use/prepare images using anvil.media sufficiently to get the correct type of object the python-pptx method is expecting

What I’ve tried and what’s not working:
See the code sample below… and when you’re done laughing… I know that the python-pptx function is expecting an image location so I suspect what I need to be doing is getting a url for the image stored in my assets

Code Sample:

@anvil.server.callable
def gen_pptx():   
  # ***** This is the file_id fetch and incrementation
  coll = app_tables.pptx_files.search(tables.order_by("file_id", ascending=False))
  if len(coll) == 0:
    file_id = 100
  else:
    file_id = int(coll[0]['file_id']) + 1

  # ***** Creating presentation object *****
  prs = Presentation()
    
  # ***** Creating slide layout *****
  #slide_layout_zero = prs.slide_layouts[0] # seems to be a title slide
  #slide_layout_one = prs.slide_layouts[1] # seems to be a title with bulleted list
  #slide_layout_two = prs.slide_layouts[2] # weird layout
  #slide_layout_three = prs.slide_layouts[3] # title with two side-by-side bulleted lists
  #slide_layout_four = prs.slide_layouts[4] # same as above but each list has a title
  #slide_layout_five = prs.slide_layouts[5] # just a title
  blank_slide_layout = prs.slide_layouts[6]  
  
  '''
  slide_zero = prs.slides.add_slide(slide_layout_zero)
  slide_one = prs.slides.add_slide(slide_layout_one)
  slide_two = prs.slides.add_slide(slide_layout_two)
  slide_three = prs.slides.add_slide(slide_layout_three)
  slide_four = prs.slides.add_slide(slide_layout_four)
  slide_five = prs.slides.add_slide(slide_layout_five)
  '''
  first_slide = prs.slides.add_slide(blank_slide_layout)
      
  left = Inches(.5)
  top = Inches(.3)
  width = height = Inches(1)
  
  txBox = first_slide.shapes.add_textbox(left, top, width, height)
  tf = txBox.text_frame
  p = tf.paragraphs[0]
  run = p.add_run()
  run.text = "Do Now Recommendations"
  font = run.font
  font.name = "Calibri"
  font.size = Pt(24)
  font.bold = True
  font.color.rgb = RGBColor(21, 44, 84)
 
  bites = anvil.BlobMedia(content_type="bytes",content="_/theme/warning_yellow.jpg", name=None)
  file = bites.url
  warning_img = first_slide.shapes.add_picture(file, Inches(0), Inches(.3), height=Inches(1))


  # ***** Saving The File *****
  fname = 'test.pptx'
  with anvil.media.TempFile() as file:
    prs.save(file)          
    media_obj = anvil.media.from_file(file,"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",fname)   
    rec = app_tables.pptx_files.add_row(file_id=file_id, file=media_obj)       
      
   
  return file_id

Clone link:
share a copy of your app

You’re creating a BlobMedia. According to the Anvil docs, when you create a BlobMedia, the content parameter must be a binary string containing the actual file contents. In other words, a BlobMedia isn’t what you want.

You probably want a URLMedia, where you pass in the URL. You can pass the location of the asset to it if you prepend it with the url of the environment.

After that, though, you then get the URL of the media object? Do you even need to use a media object then, if you already have the file stored in Assets (and hence already have a URL to it)?

Things I would try, in order:

  1. Get rid of the media object and try: ....add_picture(anvil.server.get_app_origin()+"/_/theme/warning_yellow.jpg", Inches(0)... May not work, but worth a try

  2. Use URLMedia instead of BlobMedia, e.g. URLMedia(anvil.server.get_app_origin()+"/_/theme/warning_yellow.jpg") Then proceed as you are already.

Appreciate the help… so I tried two things, one of them is your second suggestion. I think I am close to a solution, both of these are generating a well-documented error that alas, I don’t understand. In the cloned app you’ll see that I’m grabbing a .jpg via a file_loader in the client side, then passing that to the server module. In my python-pptx code I have tried passing that or the theme-grabbed jpg. They both generate this error:

<class 'anvil.URLMedia'>
<class 'anvil._serialise.StreamingMedia'>
AttributeError: 'URLMedia' object has no attribute 'seek'
at /home/anvil/.env/lib/python3.10/site-packages/pptx/parts/image.py, line 168
  called from /home/anvil/.env/lib/python3.10/site-packages/pptx/package.py, line 153
  called from /home/anvil/.env/lib/python3.10/site-packages/pptx/package.py, line 36
  called from /home/anvil/.env/lib/python3.10/site-packages/pptx/parts/slide.py, line 39
  called from /home/anvil/.env/lib/python3.10/site-packages/pptx/shapes/shapetree.py, line 351
  called from sm_main, line 64
  called from Form2, line 17

App Clone:Anvil | Login

Looks like add_picture wants a file, not a URL. You can go from a media object to a file easily enough, see the docs: Anvil Docs | Files, Media Objects and Binary Data the part where it says “If you’re using a Python library that wants you to pass it a filename”

This worked!!! I don’t know what I would do without the support I get from both Anvil, but also the community…

  file = app_tables.images.search(name='warning_yellow')[0]['file']
  print(file.name)
  print(file)
  with anvil.media.TempFile(file) as img_file:
    warning_img = first_slide.shapes.add_picture(img_file, Inches(0), Inches(.3), height=Inches(1))
2 Likes