Closed joejoethejoey closed 1 year ago
It looks like their code uses PIL to draw all the display stuff, so in theory it should just be a case of replacing WaveShare specific stuff like from waveshare_epd import epd7in5b_HD
and epd = epd7in5b_HD.EPD()
with the Inky equivalents - I don't have an Impression but I'd advise looking through the examples in the Inky library for the equivalent lines and stealing those to replace the Waveshare stuff.
You may be lucky enough to find that is all you need!
Thanks @jerbzz. I think I get what you mean, should I be checking the examples in this directory?
https://github.com/pimoroni/inky/tree/master/library/inky
From the init.py file, I'm thinking this:
from .inky_uc8159 import Inky as Inky7Colour
epd = Inky7Colour()
The syntax looks a bit different.
The examples that got installed in ~/Pimoroni/inky/examples might be easier to digest :)
Ah I see. I'll give this a try:
from inky.inky_uc8159 import Inky
and epd = Inky()
That looks very much like the right direction - good luck :D
I tried a few times, I've passed the but I get AttributeError: 'Inky' object has no attribute 'init'
error on these two lines of code:
epd.init()
epd.Clear()
I'm not sure how to implement the test_init.py code into this. Any suggestion please @jerbzz ?
Try looking at the end of this example file:
https://github.com/pimoroni/inky/blob/master/examples/7color/image.py
Thanks @jerbzz , but I think I'm in way over my head. I wouldn't know what I would need to do with defining the different items.
I think I'll just wait for something much easier to come around. Lol....I can't afford to pull more hair out.
Honestly it sounds like you’re close!
I would just delete both the lines you showed me; I think that’s dealt with for you automatically with the inky.
Thanks for the encouragement @jerbzz, I got a little further, but now get a AttributeError: module 'PIL.Image' has no attribute 'size'
error now. I don't see the size of the display being defined anywhere on the original code from https://www.the-diy-life.com/
Removed a few lines of code regarding the logging and the clearing screen, but now it seems to be the font files causing an issue.
Hmm, so the font selection stuff in the inky
library works thusly:
from font_hanken_grotesk import HankenGroteskBold, HankenGroteskMedium
hanken_bold_font = ImageFont.truetype(HankenGroteskBold, 35)
hanken_medium_font = ImageFont.truetype(HankenGroteskMedium, 16)
draw.text((hello_x, hello_y), "Hello", inky_display.WHITE, font=hanken_bold_font)
35 and 16 are the font size
It's a bit hard to do this without a display of my own, sorry! :)
Oh no no, you have been a massive help! Thanks for your patience! I would've given up long ago if not for your encouragement. It is a good feeling when you get pass each error code. I've sorted the font error, but now the very last line of code:
epd.show(epd.getbuffer(blackImage),epd.getbuffer(redImage))
I get this error:
AttributeError: 'Inky' object has no attribute 'getbuffer'
I've tried taking this line out and the code runs with no errors, but nothing shows on the screen.
I removed the getbuffer
and finally get the screen to change. But it's a complete black screen now. Lol.
This is my code
blackImage = Image.new('1', (epd.width, epd.height), 255)
redImage = Image.new('1', (epd.width, epd.height), 255)
draw_blackImage = ImageDraw.Draw(blackImage)
draw_redImage = ImageDraw.Draw(redImage)
draw_blackImage.text((312, 360), 'OIK', font = hanken_bold_font, fill = 0)
draw_blackImage.text((200, 435), noViews, font = intuitive_font, fill = 0)
draw_blackImage.text((475, 435), noSubs, font = intuitive_font, fill = 0)
draw_blackImage.text((800, 500), today, font = intuitive_font, fill = 0)
bmp = Image.open(os.path.join(picdir, 'youtubeIcon.bmp'))
redImage.paste(bmp, (265,80))
epd.display(epd.blackImage,epd.redImage)```
Can you show the whole code?
Sure:
# -*- coding:utf-8 -*-
import sys
import os
import requests
picdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'pic')
libdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'lib')
if os.path.exists(libdir):
sys.path.append(libdir)
import logging
from inky.inky_uc8159 import Inky
from time import sleep
from PIL import Image, ImageFont, ImageDraw
from font_hanken_grotesk import HankenGroteskBold, HankenGroteskMedium
from font_intuitive import Intuitive
epd = Inky()
saturation = 0.5
scale_size = 1.0
from datetime import date
today = str(date.today())
# Set up the structure of the API request URL
URL = "https://www.googleapis.com/youtube/v3/channels"
type = "statistics"
channelid = "XXXXXXXXXXXX"
apikey = "XXXXXXXXXXXX"
PARAMS = {('part', type), ('id',channelid), ('key',apikey)}
#Get API results
r = requests.get(url = URL, params = PARAMS)
data = r.json()
subscribers = int(data['items'][0]['statistics']['subscriberCount'])
totalviews = int(data['items'][0]['statistics']['viewCount'])
#Convert results to a string and format numbers with commas
noViews = 'Views: ' + f"{totalviews:,d}"
noSubs = 'Subscribers: ' + f"{subscribers:,d}"
logging.info("Updating display")
logging.info("initialising and clearing display")
# Load the fonts
intuitive_font = ImageFont.truetype(Intuitive, int(22))
hanken_bold_font = ImageFont.truetype(HankenGroteskBold, int(35))
hanken_medium_font = ImageFont.truetype(HankenGroteskMedium, int(16))
logging.info("Drawing new image")
blackImage = Image.new('1', (epd.width, epd.height), 255)
redImage = Image.new('1', (epd.width, epd.height), 255)
draw_blackImage = ImageDraw.Draw(blackImage)
draw_redImage = ImageDraw.Draw(redImage)
draw_blackImage.text((312, 360), 'OIK', font = hanken_bold_font, fill = 0)
draw_blackImage.text((200, 435), noViews, font = intuitive_font, fill = 0)
draw_blackImage.text((475, 435), noSubs, font = intuitive_font, fill = 0)
draw_blackImage.text((800, 500), today, font = intuitive_font, fill = 0)
bmp = Image.open(os.path.join(picdir, 'youtubeIcon.bmp'))
redImage.paste(bmp, (265,80))
epd.display(epd.blackImage,epd.redImage)
To drive by and interject with a pointer - Inky has no concept of "red" and "black" images, and just uses a single "P" (paletted) style image with the colours in the right order. So blackImage
and redImage
should just be one thing, and the fill
or color
of text should be adjusted to match the desired result using the epd.BLACK
or epd.RED
constants.
Looks like this is a cool example we should include in this repository somehow!
So, instead of draw_blackimage and draw_redimage etc, you just need one Image, and each draw call should say something like
draw_Image.text((x,y), "Whatever", 1, font)
for black and draw_Image.text((x,y), "Whatever", 5, font)
- 1 means black and 5 means red.
To drive by and interject with a pointer - Inky has no concept of "red" and "black" images, and just uses a single "P" (paletted) style image with the colours in the right order. So
blackImage
andredImage
should just be one thing, and thefill
orcolor
of text should be adjusted to match the desired result using theepd.BLACK
orepd.RED
constants.Looks like this is a cool example we should include in this repository somehow!
Thanks for the input. Yeh, I thought that this would be an awesome DIY project for Social Media counters.
I assumed the blackImage and redImage was just the variables defined by the code to show those in different colours.
blackImage
and redImage
are "instances" of the Image
"class" from the PIL
library - I think I have the right terminology, I'm not a Python expert!! You only need one instance of Image
to make the Impression do a do, not two like the original code has.
Think of "instances" as objects within the code which each know how to be an image - the "class" has a bunch of "methods" like Draw
- so:
myImage = Image.new('1', (epd.width, epd.height), 255)
creates a new instance of the Image
class called myImage
draw_myImage = ImageDraw.Draw(myImage)
is basically a shortcut so you don't have to say ImageDraw.Draw(myImage)
every time
and then draw_myImage.text(....
is using the Draw.text
method of the Image
class...
(like I said - very much not a Python person!)
You'll need to change '1' to 'P' in the call to Image.new
.
I've changed the code to this to try and at least get some text to show on the screen, but I get error AttributeError: 'Image' object has no attribute 'text'
myImage = Image.new('P', (epd.width, epd.height), 255)
draw_myImage = ImageDraw.Draw(myImage)
myImage.text((312, 360), 'OIK', 1, font = hanken_bold_font, fill = 0)
myImage.text((200, 435), noViews, 5, font = intuitive_font, fill = 0)
myImage.text((475, 435), noSubs, 5, font = intuitive_font, fill = 0)
myImage.text((800, 500), today, 1, font = intuitive_font, fill = 0)
bmp = Image.open(os.path.join(picdir, 'youtubeIcon.bmp'))
epd.show(epd.myImage)
Oops, spotted that I missed out the draw_
But now I get TypeError: text() got multiple Values for argument 'fill'
myImage = Image.new('P', (epd.width, epd.height), 255)
draw_myImage = ImageDraw.Draw(myImage)
draw_myImage.text((312, 360), 'OIK', 1, font = hanken_bold_font, fill = 0)
draw_myImage.text((200, 435), noViews, 5, font = intuitive_font, fill = 0)
draw_myImage.text((475, 435), noSubs, 5, font = intuitive_font, fill = 0)
draw_myImage.text((800, 500), today, 1, font = intuitive_font, fill = 0)
bmp = Image.open(os.path.join(picdir, 'youtubeIcon.bmp'))
epd.show(epd.myImage)
myImage.text((312, 360), 'OIK', 1, font = hanken_bold_font, fill = 0)
myImage.text((200, 435), noViews, 5, font = intuitive_font, fill = 0)
myImage.text((475, 435), noSubs, 5, font = intuitive_font, fill = 0)
myImage.text((800, 500), today, 1, font = intuitive_font, fill = 0)
These should be draw_myImage.text(...
Yeh, I added those back in, but get TypeError now.
Well, it's still progress - what exact error? The "parameters" ((312, 360), 'OIK', 1, font = hanken_bold_font, fill = 0
) etc have to be in the right order.
Removed the 1
and the screen refreshes now, but it's just a full black screen now:
myImage = Image.new('P', (epd.width, epd.height), 255)
draw_myImage = ImageDraw.Draw(myImage)
draw_myImage.text((312, 360), 'OIK', font = hanken_bold_font, fill = 0)
draw_myImage.text((200, 435), noViews, font = intuitive_font, fill = 0)
draw_myImage.text((475, 435), noSubs, font = intuitive_font, fill = 0)
draw_myImage.text((800, 500), today, font = intuitive_font, fill = 0)
bmp = Image.open(os.path.join(picdir, 'youtubeIcon.bmp'))
epd.show(myImage)
Well, it's still progress - what exact error? The "parameters" (
(312, 360), 'OIK', 1, font = hanken_bold_font, fill = 0
) etc have to be in the right order.
I got TypeError: text() got multiple Values for argument 'fill'
with the 1
Yeah, you're not telling it a colour. Try this:
draw_myImage.text((312, 360), 'OIK', epd.BLACK, font=hanken_bold_font)
etc - not sure about the fill stuff.
Yeah, you're not telling it a colour. Try this:
draw_myImage.text((312, 360), 'OIK', epd.BLACK, font=hanken_bold_font)
etc - not sure about the fill stuff.
Thanks but still giving me a black screen. I do see the screen flash to white when I run the code, but then it just all goes to black. Is there a way I can just make it display a string of text without using any variables first to check if there is anything else affecting the code?
Hmm, sure... here's the bare minimum that should draw some text... remember I have never even seen an Impression let alone written any code for one 😛 I'm also not sure what the default image colour is - I think black but not sure - so we will draw some red text and hope it shows up
# import essential modules
from PIL import Image, ImageFont, ImageDraw
from font_intuitive import Intuitive
from inky.inky_uc8159 import Inky
#instantiate classes and methods
inky_display = Inky()
img = Image.new("P", inky_display.resolution)
draw = ImageDraw.Draw(img)
intuitive_font = ImageFont.truetype(Intuitive, 50)
#draw some text near the top left corner (I assume)
draw.text((25,25), "Hello, World!", inky_display.RED, font=intuitive_font)
#make it do a do
inky_display.set_image(img, saturation=0.5)
inky_display.show()
Wow @jerbzz , your code worked first time running. It gives a black background and red text. I've edited your code and finally managed to get it to show the text I need. I'm playing around with the X and Y co-ordinates now to align them into the correct places. However, I'm facing two problems now:
1) I can't get the variables from the YouTube subs and views API to show correctly. I don't get any errors, but it just shows as 0.
2) The YouTube logo bitmap doesn't show. I'm not sure whether it's because the background is black and so it's not showing properly. I've looked through the colour-palette.py but can't see where the "P" @Gadgetoid referred to in the code to change it to a different color.
Getting real close now. Thanks @jerbzz !!
The code now:
import sys
import os
import requests
from inky.inky_uc8159 import Inky
from time import sleep
from PIL import Image, ImageFont, ImageDraw
from font_hanken_grotesk import HankenGroteskBold, HankenGroteskMedium
from font_intuitive import Intuitive
inky_display = Inky()
from datetime import date
today = str(date.today())
# Get the current path
PATH = os.path.dirname(__file__)
# Set up the structure of the API request URL
URL = "https://www.googleapis.com/youtube/v3/channels"
type = "statistics"
channelid = "XXXXXX"
apikey = "XXXXXX"
PARAMS = {('part', type), ('id',channelid), ('key',apikey)}
#Get API results
r = requests.get(url = URL, params = PARAMS)
data = r.json()
subscribers = int(data['items'][0]['statistics']['subscriberCount'])
totalviews = int(data['items'][0]['statistics']['viewCount'])
#Convert results to a string and format numbers with commas
noViews = 'Views: ' + f"{totalviews:,d}"
noSubs = 'Subscribers: ' + f"{subscribers:,d}"
# Load the fonts
intuitive_font = ImageFont.truetype(Intuitive, int(22))
hanken_bold_font = ImageFont.truetype(HankenGroteskBold, int(35))
hanken_medium_font = ImageFont.truetype(HankenGroteskMedium, int(16))
img = Image.new("P", inky_display.resolution)
draw = ImageDraw.Draw(img)
intuitive_font = ImageFont.truetype(Intuitive, 50)
draw.text((25, 25), 'Channel Name', inky_display.RED, font=hanken_medium_font)
draw.text((80, 80), noViews, inky_display.RED, font=intuitive_font)
draw.text((120, 120), noSubs, inky_display.RED, font=intuitive_font)
draw.text((250, 250), today, inky_display.WHITE, font=hanken_medium_font)
bmp = Image.open(os.path.join(PATH, "SubCountTemplate.jpg"))
inky_display.set_image(img, saturation=0.5)
inky_display.show()
That's gone well!
For the viewer count variables etc, try adding print(data)
after data = r.json()
- you may well get a big mess of output but it will at least show you if the data is even being retrieved from the API.
(I've also assumed that you have got the real channelid
and apikey
in your code?)
For the logo, I think you need img.paste(bmp, (150,150))
or whatever other coordinates (top left corner) after bmp = Image.open
(I assume you have SubCountTemplate.jpg
in the same folder as this script?)
@jerbzz YOU ARE A LEGEND!! Thanks so much!
Managed to get the bmp to display now and sorted out all the alignment. It's all working great. One last thing I need to get right is:
1) How can I get it to display the bmp (YouTube) icon to show in RED instead of black? 2) The background is still BLACK. Where can I set the color for the bg to WHITE? 3) Set a timer for it to refresh every hour
The code now:
#!/usr/bin/env python
import sys
import os
import argparse
import requests
from inky.auto import auto
from inky.inky_uc8159 import Inky
from time import sleep
from PIL import Image, ImageFont, ImageDraw
from font_hanken_grotesk import HankenGroteskBold, HankenGroteskMedium
from font_intuitive import Intuitive
inky_display = Inky()
inky_display.set_border(inky_display.WHITE)
from datetime import datetime
today = str(datetime.today())
# Get the current path
PATH = os.path.dirname(__file__)
# Set up the structure of the API request URL
URL = "https://www.googleapis.com/youtube/v3/channels"
type = "statistics"
channelid = "XXXXX"
apikey = "XXXXX"
PARAMS = {('part', type), ('id',channelid), ('key',apikey)}
# Get API results
r = requests.get(url = URL, params = PARAMS)
data = r.json()
subscribers = int(data['items'][0]['statistics']['subscriberCount'])
totalviews = int(data['items'][0]['statistics']['viewCount'])
# Convert results to a string and format numbers with commas
noViews = 'Views: ' + f"{totalviews:,d}"
noSubs = 'Subscribers: ' + f"{subscribers:,d}"
# Create a new canvas to draw on
img = Image.new("P", inky_display.resolution)
draw = ImageDraw.Draw(img)
inky_display.WHITE
# Load the fonts
intuitive_font = ImageFont.truetype(Intuitive, int(35))
hanken_bold_font = ImageFont.truetype(HankenGroteskBold, int(35))
hanken_medium_font = ImageFont.truetype(HankenGroteskMedium, int(25))
hanken_small_font = ImageFont.truetype(HankenGroteskMedium, int(12))
draw.text((275, 230), 'OIK', inky_display.RED, font=intuitive_font)
draw.text((100, 300), noViews, inky_display.RED, font=hanken_medium_font)
draw.text((300, 300), noSubs, inky_display.RED, font=hanken_medium_font)
draw.text((476, 428), today, inky_display.WHITE, font=hanken_small_font)
bmp = Image.open(os.path.join(PATH, "youtubeIcon2.bmp"))
bmp2 = Image.open(os.path.join(PATH, "youtubelogo.bmp"))
img.paste(bmp, (200,50))
img.paste(bmp2, (235,355))
inky_display.set_image(img, saturation=1)
inky_display.show()
Hey sorry, not had much time for this today. For the background, try this before draw.text:
for x in range (0, inky_display.width):
for y in range (0, inky_display.height):
inky_display.set_pixel(x, y, inky_display.WHITE)
Can you share a link to the exact files you're using for the icons?
No worries @jerbzz , any help is greatly appreciated!
I have the code as this now, but it still doesn't change the background to white. Could there be an issue with the "P" variable in img = Image.new("P", inky_display.resolution)
?
import sys
import os
import argparse
import requests
from inky.auto import auto
from inky.inky_uc8159 import Inky
from time import sleep
from PIL import Image, ImageFont, ImageDraw
from font_hanken_grotesk import HankenGroteskBold, HankenGroteskMedium
from font_intuitive import Intuitive
inky_display = Inky()
inky_display.set_border(inky_display.WHITE)
from datetime import datetime
today = str(datetime.today())
# Get the current path
PATH = os.path.dirname(__file__)
# Set up the structure of the API request URL
URL = "https://www.googleapis.com/youtube/v3/channels"
type = "statistics"
channelid = "XXXXXX"
apikey = "XXXXXXX"
PARAMS = {('part', type), ('id',channelid), ('key',apikey)}
# Get API results
r = requests.get(url = URL, params = PARAMS)
data = r.json()
subscribers = int(data['items'][0]['statistics']['subscriberCount'])
totalviews = int(data['items'][0]['statistics']['viewCount'])
# Convert results to a string and format numbers with commas
noViews = 'Views: ' + f"{totalviews:,d}"
noSubs = 'Subscribers: ' + f"{subscribers:,d}"
# Create a new canvas to draw on
img = Image.new("P", inky_display.resolution)
draw = ImageDraw.Draw(img)
# Load the fonts
intuitive_font = ImageFont.truetype(Intuitive, int(35))
hanken_bold_font = ImageFont.truetype(HankenGroteskBold, int(35))
hanken_medium_font = ImageFont.truetype(HankenGroteskMedium, int(25))
hanken_small_font = ImageFont.truetype(HankenGroteskMedium, int(12))
for x in range (0, inky_display.width):
for y in range (0, inky_display.height):
inky_display.set_pixel(x, y, inky_display.WHITE)
draw.text((275, 230), 'OIK', inky_display.WHITE, font=intuitive_font)
draw.text((100, 300), noViews, inky_display.WHITE, font=hanken_medium_font)
draw.text((300, 300), noSubs, inky_display.WHITE, font=hanken_medium_font)
draw.text((476, 428), today, inky_display.WHITE, font=hanken_small_font)
bmp = Image.open(os.path.join(PATH, "youtubeIcon2.bmp"))
bmp2 = Image.open(os.path.join(PATH, "youtubelogo.bmp"))
img.paste(bmp, (200,50))
img.paste(bmp2, (235,355))
inky_display.set_image(img, saturation=1)
inky_display.show()
The YouTube bmps are here:
youtubeIcon2.bmp: https://ibb.co/XF7sPvw
youtubelogo.bmp: https://ibb.co/wg5s2Q2
I think we need to draw a white rectangle on the image rather than setting pixels and then drawing an image...
Delete the bit I did before and try this in the same place (before draw.text...)
draw.rectangle([(0,0), (inky_display.width, inky_display.height)], fill=inky_display.WHITE, outline=inky_display.WHITE)
Spot on @jerbzz !! Showing white background at last!! Thank you!!!!
I've managed to get the YouTube logo to show pinkish now. For some reason it won't show the full RED even though that's one of the 7 colors. I'm not sure whether it's the problem with the BMP file since the original tutorial with the Waveshare display required the bmp to be in full black. I've used RED in the logo now, but it shows pink instead.
Is the correct way to set a variable with the display command or does the bmp itself have to be changed in some way to show RED as it should? Do I need to play around with dithering the bmp?
Using the example image.py for the impression, I can display red bmp perfectly using the image.py command, do I need to call to the same command via my code?
python3 image.py sampleREDscreen.jpg
what happens if you display your exact image file (which are PNGs, not BMPs) using the command above?
It still shows as pink. I notice that it's actually made up of red and white pixels, giving a pinkish color. I've tried with JPG, BMP and PNG.
Where are you finding "sampleREDscreen.jpg"?
What happens if you try to display this?
Also can I see a picture of what you're getting?
I've just tried using a BLUE version of the logo, and for some reason it shows up reddish on the display, it looks more red than the actual RED version. Am I using the wrong RGB values?
BLUE version: https://ibb.co/4JT3LXf RED version: https://ibb.co/sKM5qC8
Yeah probably - it looks like to paste images like that they have to be correctly palletised... I can't see where that's documented. I'll have a quick look at the code and a guess, bear with.
I just tried taking a photo of it, you can see the text for CHANNEL NAME is more red than the YouTube logo:
Hi all,
I recently saw this tutorial for a YouTube subscriber count using a Waveshare eink display:
https://www.the-diy-life.com/make-a-youtube-subscriber-counter-using-an-e-ink-display-and-a-raspberry-pi-zero-w/
I didn't realize it was such a handful with the code, but I purchased the Inky Impression display thinking it would work just as easily.
Can anyone point me in the right direction to get the code to work with the Inky Impression? I'm at a complete loss. The code gets stuck when it tries to detect the Waveshare, I have no idea how to make it look for the Inky Impression instead. Or if it could be simpler, how can I get it to show a dynamic webpage using html to refresh every hour so that it will update automatically?
Thanks in advance.