Is there a way to register_shape using turtles of a random image in a file?

38 Views Asked by At

Attempting to make an outfit generator but need to generate a random image for the Turtle. I have files full of pictures of clothes, separated into shirts, shoes, and pants. Need the Shirts/Pants/Shoes.register_shape(random_shirt/pants/shoes) to produce a random image from said files. Not sure if this is possible but if it is please let me know. ##If there is a different easier GUI to do this with I'm open to suggestions, currently looking into TKinter.

import random
import turtle
import os

wn = turtle.Screen()
wn.bgcolor('light gray')
wn.setup(width = 600, height = 800)
wn.title('Outfit Generator')

#Turtle of Shirt
Shirt = turtle.Turtle()
Shirt.hideturtle()
Shirt.speed(0)
Shirt.setposition(300, 400)


#Turtle of Pants
Pants = turtle.Turtle()
Pants.hideturtle()
Pants.speed(0)
Pants.setposition(500, 400)


#Turtle of Shoes
Shoes = turtle.Turtle()
Shoes.hideturtle()
Shoes.speed(0)
Shoes.setposition(100, 400)


# Specify the directory where your photos are located
photo_shirts_dir = r'C:\Users\Rest Of Path\Shirts'
photo_pants_dir = r'C:\Users\Rest Of Path\Pants'
photo_shoes_dir = r'C:\Users\Rest Of Path\Shoes'

#Create Directory of all images in files
image_files_shirts = [file for file in os.listdir(photo_shirts_dir) if file.endswith(('.gif', '.jpg', '.jpeg', '.png'))]
image_files_pants = [file for file in os.listdir(photo_pants_dir) if file.endswith(('.gif', '.jpg', '.jpeg', '.png'))]
image_files_shoes = [file for file in os.listdir(photo_shoes_dir) if file.endswith(('.gif', '.jpg', '.jpeg', '.png'))]

if not image_files_shirts:
    print("No image files found in the directory.")
else:
    random_shirt = (random.choice(image_files_shirts))
    turtle.register_shape(random_shirt)
    Shirt.shape(random_shirt)

if not image_files_pants:
    print("No image files found in the directory.")
else:
    random_pants = random.choice(image_files_pants)
    turtle.register_shape(random_pants)
    Pants.shape(random_pants)

if not image_files_shoes:
    print("No image files found in the directory.")
else:
    random_shoes = random.choice(image_files_shoes)
    turtle.register_shape(random_shoes)
    Shoes.shape(random_shoes)


wn.listen()
wn.mainloop()

Error Being Received

Traceback (most recent call last):
  File "c:\Users\joezi\OneDrive\Desktop\Python\Outfit Creator\OutfitGenerator.py", line 45, in <module>
    turtle.register_shape(random_shirt)
  File "<string>", line 8, in register_shape
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2288.0_x64__qbz5n2kfra8p0\Lib\turtle.py", line 1134, in register_shape    
    shape = Shape("image", self._image(name))
                           ^^^^^^^^^^^^^^^^^
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2288.0_x64__qbz5n2kfra8p0\Lib\turtle.py", line 479, in _image
    return TK.PhotoImage(file=filename, master=self.cv)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2288.0_x64__qbz5n2kfra8p0\Lib\tkinter\__init__.py", line 4145, in __init__
    Image.__init__(self, 'photo', name, cnf, master, **kw)
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2288.0_x64__qbz5n2kfra8p0\Lib\tkinter\__init__.py", line 4092, in __init__
    self.tk.call(('image', 'create', imgtype, name,) + options)
_tkinter.TclError: couldn't open "shirt.gif": no such file or directory
2

There are 2 best solutions below

0
ggorlen On

As discussed in the comments, os.listdir returns a list of file names. The error _tkinter.TclError: couldn't open "shirt.gif": no such file or directory indicates that turtle is trying to open shirt.gif in the current working directory, which isn't right. Try prepending the path back onto the file names:

random_shirt = random.choice(image_files_shirts)
shape = os.path.join(photo_shirts_dir, random_shirt)
turtle.register_shape(shape)
Shirt.shape(shape)

Note I'm using os.path.join() for an operating-system agnostic path join. This requires adding import os to the top of your script. I suggest using os.path.join for your other paths as well.

Note that your endswith isn't valid since turtle only works with .gifs at the present time. os.path.splitext("...")[1] is preferable to endswith in any case. The general rule is not to use plain string manipulation on paths to avoid bugs and gotchas.

I'd also use a relative path rather than presuming C:\Users\... is a thing, which it won't be on most computers.

Another nitpick: the Python convention is that PascalCase is only used for class names. Variables are snake_case.


When attacking problems, it's always good to minimize the problem so the situation is free of any noise, and you can focus fully on the issue.

Directory structure:

├── images
│   └── test.gif
└── test.py

test.py:

import os
from turtle import Screen, Turtle


path = "images"
files = [
    x for x in os.listdir(path) if
    os.path.splitext(x)[1] == ".gif"
]

if files:
    t = Turtle()
    shape = files[0]
    Screen().register_shape(shape)
    t.shape(shape)
    Screen().exitonclick()

Error:

Traceback (most recent call last):
  File "/home/greg/programming/test.py", line 13, in <module>
    Screen().register_shape(files[0])
  File "/usr/lib/python3.10/turtle.py", line 1133, in register_shape
    shape = Shape("image", self._image(name))
  File "/usr/lib/python3.10/turtle.py", line 478, in _image
    return TK.PhotoImage(file=filename, master=self.cv)
  File "/usr/lib/python3.10/tkinter/__init__.py", line 4103, in __init__
    Image.__init__(self, 'photo', name, cnf, master, **kw)
  File "/usr/lib/python3.10/tkinter/__init__.py", line 4048, in __init__
    self.tk.call(('image', 'create', imgtype, name,) + options)
_tkinter.TclError: couldn't open "test.gif": no such file or directory

Now, the fix:

# ...
if files:
    t = Turtle()
    shape = files[0]
    Screen().register_shape(shape)
    t.shape(shape)
    Screen().exitonclick()

Related: Reading a file using a relative path in a Python project

0
cdlane On

In theory, we could use a generic name like 'shirt' to represent a random image instead of a file path:

from turtle import Screen, Turtle, Shape
# ...
shape = Shape('image', full_path_name)
screen.register_shape('shirt', shape)
shirt.shape('shirt')

But unfortunately, this is broken by an apparent bug in turtle's Shape.__init__ method where it invokes _image as a class method, instead of an instance method, effectively leaving off a self argument:

elif type_ == "image":
    if isinstance(data, str):
        if data.lower().endswith(".gif") and isfile(data):
            data = TurtleScreen._image(data)

where the last line probably should be something like:

data = Screen()._image(data)

Ignoring that glitch, my handling of the file path issue would be something like:

from random import choice
from turtle import Screen, Turtle
import os

# Specify the directory where your photos are located
PHOTO_SHIRTS_DIRECTORY = r'C:\Users\Rest Of Path\Shirts'

screen = Screen()
screen.bgcolor('light gray')
screen.setup(width=600, height=800)
screen.title('Outfit Generator')

# Turtle of Shirt
shirt = Turtle()
shirt.penup()
shirt.speed('fastest')
shirt.setposition(300, 400)

image_files_shirts = []

# Create list of matching images in directory
with os.scandir(PHOTO_SHIRTS_DIRECTORY) as directory:
    for entry in directory:
        if entry.is_file() and os.path.splitext(entry.name)[-1].lower() == '.gif':
            image_files_shirts.append(entry.path)

if image_files_shirts:
    random_shirt = choice(image_files_shirts)
    screen.register_shape(random_shirt)
    shirt.shape(random_shirt)
else:
    print("No image files found in the shirt directory.")

screen.mainloop()