PIL not writing text a second time onto image - python

I'm trying to make a somewhat ambitious hangman game for a discord bot, and for that I need PIL to write in text. After writing some text to an image, and then trying to write text again to that same image, instead of sending the image with the second text added, it only sends the image with the first text. The weird thing is, is that the function goes through, it saves it as a new file with a different name, but no text (the second set, that is). What gives? What am I doing wrong here?
import random, discord
from requests import get as reGet
from io import BytesIO
from PIL import Image, ImageDraw, ImageFont
# Just a random image I found off google, not actually using it
inp_shell = reGet("https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/Circle_-_black_simple.svg/1200px-Circle_-_black_simple.svg.png")
# Yes, I know, probably not the best place to put the font, I'll change it later
fnt = ImageFont.truetype("modules/Roboto-Regular.ttf", size= 40)
# Opens a list of words that it can get from; The file here is just a stub
with open('words_alpha.txt') as words_file:
word_list = words_file.read().splitlines()
class img():
def __init__(self):
self.open_shell = Image.open(BytesIO(inp_shell.content))
# Text in the top left
async def tLeft(self, txt, inp_img):
image = ImageDraw.Draw(inp_img)
image.text((10,10), txt, font=fnt, fill=(0, 0, 0))
self.open_shell.save("tLeft.png")
async def main_text(self, txt, inp_img):
image = ImageDraw.Draw(inp_img)
# Used to position the text in the center but currently is not being used
x, y = inp_img.size
pos = x/2
# I've tried changing the fill and position, and still nothing.
# This is probably the source of the problem
image.text((20,20), txt, font=fnt, fill=(255, 255, 255))
print(txt)
self.open_shell.save("mainText.png")
# Creates a dictionary with the length of the words assigned as they keys,
# I think anyways, I didn't write this
by_length = {}
for word in word_list:
by_length.setdefault(len(word), []).append(word)
# Retrieves a random word with a certain length
async def word_finder(wordlength):
global word
word = random.choice(by_length[wordlength])
print(word)
# Main function
async def hanggMan(message): #double g in hang is intentional
content = message.clean_content[11:]
print(content) # Just used to make sure it's going through
# For now I'm using a word length of 5
if content.lower() == "5":
z = img() # Calls an instance of the img class
# Puts the image in an embed; ignore t his
embed = discord.Embed(title="testtest testtesttesttest")
embed.type = "rich"
embed.colour = discord.Color.gold()
await word_finder(5) # Tells the word_finder function to find a random word with a length of 5
await z.tLeft(txt="tLeft", inp_img= z.open_shell) # Calls the tLeft function and tells it to write "tLeft"
# Calls the main_text function and tells it to write the word on top of
# "tLeft.png". There is a print statement in the function and it actually
# does print the word, so this is not likely to be the source of the problem
await z.main_text(txt=word, inp_img=Image.open("tLeft.png"))
embed.set_image(url="attachment://mainText.png")
# The interesting thing about this is that it actually does save it as "mainText.png"
# and sends the file properly, but the word is nowhere to be found
await message.channel.send(embed=embed, file=discord.File("mainText.png"))

I can't run it but when I start removing not imprtant code then I saw you use image in class img() in wrong way.
You always save image which you have in self.open_shell
In first function you send the same image as argument
z.tLeft(txt="tLeft", inp_img=z.open_shell)
so add text to z.open_shell and you save z.open_shell
But in function you send different image as argument
z.main_text(txt=word, inp_img=Image.open("tLeft.png"))
so you add text to new image but you save again z.open_shell which has older version.
You need
self.open_shell = inp_img
Like this
def main_text(self, txt, inp_img):
self.open_shell = inp_img
image = ImageDraw.Draw(inp_img)
image.text((20,20), txt, font=fnt, fill=(255, 255, 255))
self.open_shell.save("mainText.png")

Related

doesn't do anythinng when I run the ascii art program in pycharm

I have been working on this project for a while. What Ascii does is when we run the program, it asks for a picture, and after we give it the picture it changes it into symbols/letters
if u still didn't understand, read this:
ASCII art is a graphic design technique that uses computers for presentation and consists of pictures pieced together from the 95 printable characters defined by the ASCII Standard from 1963 and ASCII compliant character sets with proprietary extended characters.
so here is the code I wrote in python (Pycharm, a Python IDE):
import PIL.Image
ASCII_CHARS = ["#", "#", "S", "%", "?", "*", "+", ";", ":", ",", "."]
def resize_image(image, new_width=100):
width, height = image.size
ratio = height / width / 1.65
new_height = int(new_width * ratio)
resized_image = image.resize((new_width, new_height))
return(resized_image)
def grayify(image):
grayscale_image = image.convert("L")
return(grayscale_image)
def pixels_to_ascii(image):
pixels = image.getdata()
charecters = "".join([ASCII_CHARS[pixel//25] for pixel in pixels])
return(charecters)
def main(new_width=100):
path = input("Enter a valid pathname to an image :\n")
try:
image = PIL.Image.open(path)
except:
print(path, "is not a valid pathname to an image")
new_image_data = pixels_to_ascii(grayify(resize_image(image)))
pixel_count = len(new_image_data)
ascii_image = '\n'.join(new_image_data[i:(i+new_width)] for i in range(0, pixel_count, new_width))
print(ascii_image)
with open("ascii_image.txt", "w") as f:
f.write(ascii_image)
main()
and every time I run this I have 0 errors, 0 warnings, 0 weak warnings, and 0 typos. But it doesn't work and shows this:
Process finished with exit code 0
and nothing else, how can I fix this and make it work...
You are calling main() function inside the main() function itself, hence there is no output.
You should place main() without any indentation:
def main():
# code
main()
Best Practices for calling main() function:
if __name__ == "__main__":
main()
So that you can reuse this program further without execution of main function in any import.
One more thing, you are not doing anything in main function after opening image with PIL hence no output, and you should not write import PIL.Image if you are using PIL.Image, you should use import PIL, else it is recommended to use from PIL import Image when you want to use Image class from PIL. You are also using image inside the body of except.
Suggestions:
You are taking path to image file as input which is somewhat annoying to the user, you can use tkinter.filedialog to get file path like this:
from tkinter.filedialog import askopenfilename
.
.
def main():
path = askopenfilename(filetypes=(("Images", ["*.jpg", "*.png", "*.jpeg"]), ))
# ..

micro:bit Python send radio Image

micro:bit wireless radio BLE using Python
I want to send over radio a member of the Image collection (Image.HEART). I know how to send strings and a custom image but not a member of the Image collection.
I want receiver's message_in string to be used directly by display.show (or maybe an intermediate variable to modify). I don't want to test the received string for every possible member of the Image collection by using if/else or a dictionary.
I've tried ideas in code below but all fail. I appreciate your help.
# micro:bit radio: Send an image from Image collection
from microbit import *
import radio
radio.config(group=1)
radio.on()
while True:
if button_a.is_pressed():
radio.send(Image.HEART) # ?????
# radio.send(index(Image.HEART)) # ?????
# radio.send(str(Image.HEART)) # ?????
# radio.send('Image.HEART') # ?????
# radio.send('HEART') # ?????
message_in = radio.receive()
if message_in != None:
display.show(message_in) #show heart
# and other tries at syntax for argument
This feels rather "hacky" and brittle, and I am happy to delete it if a better method shows up, but one way that works is like this.
If you run this (link to docs):
repr(Image.HEART)
you'll get this:
"Image('09090:99999:99999:09990:00900:')"
If you look at the documentation for Image class (link to docs), you'll see you can create a new Image from that string. So, my suggestion for the moment is to do this:
# Get a string corresponding to Image.HEART
s = repr(Image.HEART)[7:-3]
... TRANSMIT ...
# Convert received string back into Image
I = Image(received)
I guess this is a slightly less brittle way of picking up digits and colons from the repr output, but it's still ugly:
s = ""
for char in repr(Image.SAD):
if char in '0123456789:': s += char
The way Mark has suggested works well and allows for any image to be sent. I've put it in a function to make it easier for me to experiment with.
from microbit import *
import radio
radio.config(group=1)
radio.on()
def tx_value(image_to_send):
return ''.join([x for x in str(image_to_send) if x in '0123456789:'])
while True:
if button_a.is_pressed() and button_b.is_pressed():
radio.send(tx_value(Image('97531:97531:97531:97531:97531')))
elif button_a.is_pressed():
radio.send(tx_value(Image.DUCK))
elif button_b.is_pressed():
radio.send(tx_value(Image.HEART))
sleep(.25)
message_in = radio.receive()
if message_in != None:
display.show(Image(message_in))
The other approach is to have a dictionary of images and just transmit the dictionary key:
from microbit import *
import radio
radio.config(group=1)
radio.on()
IMAGES = {'duck': Image.DUCK,
'heart': Image.HEART,
'fade': Image('97531:97531:97531:97531:97531')}
while True:
if button_a.is_pressed() and button_b.is_pressed():
radio.send('fade')
elif button_a.is_pressed():
radio.send('duck')
elif button_b.is_pressed():
radio.send('heart')
sleep(.25)
message_in = radio.receive()
if message_in != None:
display.show(IMAGES[message_in])
This requires for the dictionary to be defined the same on both micro:bits

Python: How can I make a new line when printing?

I am using Cmd, PIL, and Image to make a program called Visual Object Creator, aka VOC. I made a command prompt and when you type in a command, let's say, bluesquare, it creates just that. But when I do, it makes the image right in front of my command prompt. It looks terribly ugly. It makes me type the command in the next line, away from the (Cmd) prompt. It's hard to explain, just use trinket.io and see my problem, because that's the only compiler I find that works
I tried using /n and printing simply a blank line, but it never worked. I have seen the /n being used but it simply prints the actual /n and not the blank line. I am sorry I pasted the whole file, but every image creating command that I programmed does it!
from PIL import Image
from cmd import Cmd
class Root(Cmd):
intro = "Visual Object Creator v.04."
prompt = ">"
def do_redsquare(self, inp):
print("VOC created this image for you! rsqr.jpg")
rs = Image.new("RGB", (50, 50), color = "red")
rs.save("rsqr.jpg")
def help_redsquare(self):
print("VOC creates a red square under the filename rsqr.jpg.")
def do_exit(self, inp):
return True
def help_exit(self):
print("Ends the VOC application.")
def do_greensquare(self, inp):
print("VOC created this image for you! gsqr.jpg")
gs = Image.new("RGB", (50, 50), color = "green")
gs.save("gsqr.jpg")
def help_greensquare(self):
print("VOC creates a green sqare under the filename gsqr.jpg.")
def do_bluesquare(self, inp):
print("VOC created this image for you! bsqr.jpg")
bs = Image.new("RGB", (50, 50), color = "blue")
bs.save("bsqr.jpg")
def help_bluesquare(self):
print("VOC creates a blue square under the filename bsqr.jpg")
Root().cmdloop()
There were no errors, but it was making me enter commands on a blank line when I actually want to do it on the line of the (cmd) prompt.
The correct newline character for python is \n.
The \ (backslash) is used to start an "escape sequence" which has special
meaning to Python. Some of the options are:
\n newline
\t tab
\\ backslash
print() should print a blank line. Example:
print('line 1')
print()
print('line 2')
will output:
line 1
line 2

Generate a image which produces gif like effect

I am trying to generate a image which produces gif like effect by flushing the response continuously to the browser, I am relatively new to django/python and tried with following code testing for both text and image. The generator for text works fine but in case of image only first version of image is being generated.
I tried searching but can't find anything on this. I am confused how to proceed with this or if this is at all possible with django or If the idea is conceptually wrong.
Image Generator :
def refreshx():
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
size = (1000,500)
im = Image.new('RGB', size)
draw = ImageDraw.Draw(im)
red = (255,255,255)
text_pos = (10,30)
draw.text(text_pos, str(datetime.datetime.today()), fill=red)
buffer = StringIO.StringIO()
im.save(buffer, 'PNG')
yield buffer.getvalue()
time.sleep(5)
buffers = StringIO.StringIO()
ims = Image.new('RGB', size)
draws = ImageDraw.Draw(ims)
text_poss = (30,80)
draws.text(text_poss, 'dasdasdsa', fill=red)
print 'been there'
ims.save(buffers, 'PNG')
yield buffers.getvalue()
Text Generator :
def testgenerator():
yield str(datetime.datetime.today())
time.sleep(5)
yield str(datetime.datetime.today())
View Function :
def test(request):
#return HttpResponse(testgenerator());
return HttpResponse(refreshx(), mimetype="image/png")
EDIT :
I learned while researching that there's a concept called gifsocket, I'm looking into it..please suggest if anyone has experience with these

Use decodebin with adder

I'm trying to create an audio stream that has a constant audio source (in this case, audiotestsrc) to which I can occasionally add sounds from files (of various formats, that's why I'm using decodebin) through the play_file() method. I use an adder for that purpose. However, for some reason, I cannot add the second sound correctly. Not only does the program play the sound incorrectly, it also completely stops the original audiotestsrc. Here's my code so far:
import gst; import gobject; gobject.threads_init()
pipe = gst.Pipeline()
adder = gst.element_factory_make("adder", "adder")
first_sink = adder.get_request_pad('sink%d')
pipe.add(adder)
test = gst.element_factory_make("audiotestsrc", "test")
test.set_property('freq', 100)
pipe.add(test)
testsrc = test.get_pad("src")
testsrc.link(first_sink)
output = gst.element_factory_make("alsasink", "output")
pipe.add(output)
adder.link(output)
pipe.set_state(gst.STATE_PLAYING)
raw_input('Press key to play sound')
def play_file(filename):
adder_sink = adder.get_request_pad('sink%d')
audiofile = gst.element_factory_make('filesrc', 'audiofile')
audiofile.set_property('location', filename)
decoder = gst.element_factory_make('decodebin', 'decoder')
def on_new_decoded_pad(element, pad, last):
pad.link(adder_sink)
decoder.connect('new-decoded-pad', on_new_decoded_pad)
pipe.add(audiofile)
pipe.add(decoder)
audiofile.link(decoder)
pipe.set_state(gst.STATE_PAUSED)
pipe.set_state(gst.STATE_PLAYING)
play_file('sample.wav')
while True:
pass
Thanks to moch on #gstreamer, I realized that all adder sources should have the same format. I modified the above script so as to have the caps "audio/x-raw-int, endianness=(int)1234, channels=(int)1, width=(int)16, depth=(int)16, signed=(boolean)true, rate=(int)11025" (example) go before every input in the adder.

Categories

Resources