如何用Pillow绘制进度条?
How to draw a progress bar with Pillow?
我目前正在尝试根据计算出的百分比绘制进度条。
但是,我无法以正确的格式显示它。
我将自己定位于此站点的另一个答案 ( & Is it possible to add a blue bar using PIL or Pillow?)
但要么是进度条太长,width
限制不起作用,要么进度条没有显示进度。
示例 1:
async def rank(self, ctx, member: discord.Member):
member = ctx.author
data = await database.find_user(collection_user, ctx.guild.id, ctx.author.id)
already_earned = data["exp"]
to_reach= ((50 * (data['lvl'] ** 2)) + (50 * (data['lvl'] - 1)))
percentage = ((data["exp"] / next_level_xp ) * 100) # Get the percentage
## Rank card
img = Image.open("leveling/rank.png")
draw = ImageDraw.Draw(img)
font = ImageFont.truetype("settings/myfont.otf", 35)
font1 = ImageFont.truetype("settings/myfont.otf", 24)
async with aiohttp.ClientSession() as session:
async with session.get(str(ctx.author.avatar)) as response:
image = await response.read()
icon = Image.open(BytesIO(image)).convert("RGBA")
img.paste(icon.resize((156, 156)), (50, 60))
# Some text drawn, this works
### From Whosebug ###
def drawProgressBar(d, x, y, w, h, progress, bg=(129, 66, 97), fg=(211,211,211)):
# draw background
draw.ellipse((x+w, y, x+h+w, y+h), fill=bg)
draw.ellipse((x, y, x+h, y+h), fill=bg)
draw.rectangle((x+(h/2), y, x+w+(h/2), y+h), fill=bg, width=10)
# draw progress bar
progress = ((already_earned / to_reach ) * 100)
w *= progress
draw.ellipse((x+w, y, x+h+w, y+h),fill=fg)
draw.ellipse((x, y, x+h, y+h),fill=fg)
draw.rectangle((x+(h/2), y, x+w+(h/2), y+h),fill=fg, width=10)
return d
drawProgressBar(img, 10, 10, 100, 25, 0.5)
### From Whosebug ###
img.save('leveling/infoimg2.png') # Save it and send it out
看起来像这样:
第二个例子:
async def rank(self, ctx, member: discord.Member):
member = ctx.author
data = await database.find_user(collection_user, ctx.guild.id, ctx.author.id)
already_earned = data["exp"]
to_reach = ((50 * (data['lvl'] ** 2)) + (50 * (data['lvl'] - 1)))
percentage = ((already_earned / to_reach) * 100) # Get the percentage
img = Image.open("leveling/rank.png")
draw = ImageDraw.Draw(img)
### From Whosebug ###
color=(129, 66, 97)
x, y, diam = percentage, 8, 34
draw.ellipse([x,y,x+diam,y+diam], fill=color)
ImageDraw.floodfill(img, xy=(14,24), value=color, thresh=40)
### From Whosebug ###
font = ImageFont.truetype("settings/myfont.otf", 35)
font1 = ImageFont.truetype("settings/myfont.otf", 24)
async with aiohttp.ClientSession() as session:
async with session.get(str(ctx.author.avatar)) as response:
image = await response.read()
icon = Image.open(BytesIO(image)).convert("RGBA")
img.paste(icon.resize((156, 156)), (50, 60))
# Draw some other text here, this works though
img.save('leveling/infoimg2.png') # Save the file and output it
看起来像这样:
两个结果都不符合答案和问题中显示的图片。
有人能告诉我哪里做错了吗?
我还尝试在第二个示例中增加 x
或设置 img = Image.open("pic.png").format('RGB')
但似乎没有任何效果。进度条太长或太短。
我试图实现我的进度条被限制在一定的大小,总是匹配 100% 并且我定义的 progress
会适应它。
您的代码存在的问题是背景和进度条部分的颜色相同,因此您看不到它。这可以通过不同的颜色来解决。
行 progress = ((already_earned / to_reach ) * 100)
还将 progress
设置为百分比 [0, 100]
。然后将 width
乘以这个。对于 100
的输入,例如 50% 的填充量会使椭圆变为 5000
像素 - 远离屏幕并覆盖已经在那里绘制的所有内容。
@client.command(name='rank')
async def rank(ctx, progress: float):
# Open the image and do stuff
# I tested this with a blank 800x400 RGBA
def new_bar(x, y, width, height, progress, bg=(129, 66, 97), fg=(211,211,211), fg2=(15,15,15)):
# Draw the background
draw.rectangle((x+(height/2), y, x+width+(height/2), y+height), fill=fg2, width=10)
draw.ellipse((x+width, y, x+height+width, y+height), fill=fg2)
draw.ellipse((x, y, x+height, y+height), fill=fg2)
width = int(width*progress)
# Draw the part of the progress bar that is actually filled
draw.rectangle((x+(height/2), y, x+width+(height/2), y+height), fill=fg, width=10)
draw.ellipse((x+width, y, x+height+width, y+height), fill=fg)
draw.ellipse((x, y, x+height, y+height), fill=fg)
new_bar(10, 10, 100, 25, progress)
# send
示例:
progress = 0.25
progress = 0.9
至于第二个代码片段,它使用 floodfill()
不正确。 thresh
是“像素值与‘背景’的最大容忍差异,以便替换它。”这意味着 floodfill 实际上什么都不做,你只画了椭圆(而不是进度条前面的部分)。
# this draws a circle with bounding box `x,y` and `x+diam,y+diam`
# note that `x` is dependent on the progress value: higher progress means larger x, which means the circle is drawn more to the right
draw.ellipse([x,y, x+diam,y+diam], fill=color)
# If you look at the post where the code was given, you can see the error.
# In that post, the entirety of the progress bar already exists, and is a very different color (allowing the use of the threshold value).
# In this code, no progress bar exists yet, meaning everything else is just one solid color, and then the floodfill cannot do anything.
# Also, xy is specified as arbitrary coordinates, which you would need to change to fit your bar.
# This does nothing.
ImageDraw.floodfill(img, xy=(14,24), value=color, thresh=40)
如果你想解决这个问题,你需要在进度条的左边填充颜色。上面的第一个函数已经这样做了。如果需要,您可以删除前 3 行以避免绘制背景,从而产生相同的结果。
我目前正在尝试根据计算出的百分比绘制进度条。
但是,我无法以正确的格式显示它。
我将自己定位于此站点的另一个答案 (
但要么是进度条太长,width
限制不起作用,要么进度条没有显示进度。
示例 1:
async def rank(self, ctx, member: discord.Member):
member = ctx.author
data = await database.find_user(collection_user, ctx.guild.id, ctx.author.id)
already_earned = data["exp"]
to_reach= ((50 * (data['lvl'] ** 2)) + (50 * (data['lvl'] - 1)))
percentage = ((data["exp"] / next_level_xp ) * 100) # Get the percentage
## Rank card
img = Image.open("leveling/rank.png")
draw = ImageDraw.Draw(img)
font = ImageFont.truetype("settings/myfont.otf", 35)
font1 = ImageFont.truetype("settings/myfont.otf", 24)
async with aiohttp.ClientSession() as session:
async with session.get(str(ctx.author.avatar)) as response:
image = await response.read()
icon = Image.open(BytesIO(image)).convert("RGBA")
img.paste(icon.resize((156, 156)), (50, 60))
# Some text drawn, this works
### From Whosebug ###
def drawProgressBar(d, x, y, w, h, progress, bg=(129, 66, 97), fg=(211,211,211)):
# draw background
draw.ellipse((x+w, y, x+h+w, y+h), fill=bg)
draw.ellipse((x, y, x+h, y+h), fill=bg)
draw.rectangle((x+(h/2), y, x+w+(h/2), y+h), fill=bg, width=10)
# draw progress bar
progress = ((already_earned / to_reach ) * 100)
w *= progress
draw.ellipse((x+w, y, x+h+w, y+h),fill=fg)
draw.ellipse((x, y, x+h, y+h),fill=fg)
draw.rectangle((x+(h/2), y, x+w+(h/2), y+h),fill=fg, width=10)
return d
drawProgressBar(img, 10, 10, 100, 25, 0.5)
### From Whosebug ###
img.save('leveling/infoimg2.png') # Save it and send it out
看起来像这样:
第二个例子:
async def rank(self, ctx, member: discord.Member):
member = ctx.author
data = await database.find_user(collection_user, ctx.guild.id, ctx.author.id)
already_earned = data["exp"]
to_reach = ((50 * (data['lvl'] ** 2)) + (50 * (data['lvl'] - 1)))
percentage = ((already_earned / to_reach) * 100) # Get the percentage
img = Image.open("leveling/rank.png")
draw = ImageDraw.Draw(img)
### From Whosebug ###
color=(129, 66, 97)
x, y, diam = percentage, 8, 34
draw.ellipse([x,y,x+diam,y+diam], fill=color)
ImageDraw.floodfill(img, xy=(14,24), value=color, thresh=40)
### From Whosebug ###
font = ImageFont.truetype("settings/myfont.otf", 35)
font1 = ImageFont.truetype("settings/myfont.otf", 24)
async with aiohttp.ClientSession() as session:
async with session.get(str(ctx.author.avatar)) as response:
image = await response.read()
icon = Image.open(BytesIO(image)).convert("RGBA")
img.paste(icon.resize((156, 156)), (50, 60))
# Draw some other text here, this works though
img.save('leveling/infoimg2.png') # Save the file and output it
看起来像这样:
两个结果都不符合答案和问题中显示的图片。
有人能告诉我哪里做错了吗?
我还尝试在第二个示例中增加 x
或设置 img = Image.open("pic.png").format('RGB')
但似乎没有任何效果。进度条太长或太短。
我试图实现我的进度条被限制在一定的大小,总是匹配 100% 并且我定义的 progress
会适应它。
您的代码存在的问题是背景和进度条部分的颜色相同,因此您看不到它。这可以通过不同的颜色来解决。
行 progress = ((already_earned / to_reach ) * 100)
还将 progress
设置为百分比 [0, 100]
。然后将 width
乘以这个。对于 100
的输入,例如 50% 的填充量会使椭圆变为 5000
像素 - 远离屏幕并覆盖已经在那里绘制的所有内容。
@client.command(name='rank')
async def rank(ctx, progress: float):
# Open the image and do stuff
# I tested this with a blank 800x400 RGBA
def new_bar(x, y, width, height, progress, bg=(129, 66, 97), fg=(211,211,211), fg2=(15,15,15)):
# Draw the background
draw.rectangle((x+(height/2), y, x+width+(height/2), y+height), fill=fg2, width=10)
draw.ellipse((x+width, y, x+height+width, y+height), fill=fg2)
draw.ellipse((x, y, x+height, y+height), fill=fg2)
width = int(width*progress)
# Draw the part of the progress bar that is actually filled
draw.rectangle((x+(height/2), y, x+width+(height/2), y+height), fill=fg, width=10)
draw.ellipse((x+width, y, x+height+width, y+height), fill=fg)
draw.ellipse((x, y, x+height, y+height), fill=fg)
new_bar(10, 10, 100, 25, progress)
# send
示例:
progress = 0.25
progress = 0.9
至于第二个代码片段,它使用 floodfill()
不正确。 thresh
是“像素值与‘背景’的最大容忍差异,以便替换它。”这意味着 floodfill 实际上什么都不做,你只画了椭圆(而不是进度条前面的部分)。
# this draws a circle with bounding box `x,y` and `x+diam,y+diam`
# note that `x` is dependent on the progress value: higher progress means larger x, which means the circle is drawn more to the right
draw.ellipse([x,y, x+diam,y+diam], fill=color)
# If you look at the post where the code was given, you can see the error.
# In that post, the entirety of the progress bar already exists, and is a very different color (allowing the use of the threshold value).
# In this code, no progress bar exists yet, meaning everything else is just one solid color, and then the floodfill cannot do anything.
# Also, xy is specified as arbitrary coordinates, which you would need to change to fit your bar.
# This does nothing.
ImageDraw.floodfill(img, xy=(14,24), value=color, thresh=40)
如果你想解决这个问题,你需要在进度条的左边填充颜色。上面的第一个函数已经这样做了。如果需要,您可以删除前 3 行以避免绘制背景,从而产生相同的结果。