如何用Pillow绘制进度条?

Dom*_*nik 7 python python-imaging-library discord.py

我目前正在尝试根据计算的百分比绘制进度条。

但是,我无法以正确的格式显示它。

我将自己定位在这个网站的另一个答案(How do you make a Progress Bar and put it on an image? & 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 StackOverflow ###
    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 StackOverflow ###

    img.save('leveling/infoimg2.png') # Save it and send it out
Run Code Online (Sandbox Code Playgroud)

这看起来像这样:
在此输入图像描述

第二个例子:

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 StackOverflow ###
    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 StackOverflow ###

    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
Run Code Online (Sandbox Code Playgroud)

看起来像这样:
在此输入图像描述

两个结果与答案和问题中显示的图片都不匹配。

有人能告诉我哪里错了吗?我还尝试增加x第二个示例或集合img = Image.open("pic.png").format('RGB'),但似乎没有任何效果。进度条太长或太短。

我试图实现我的进度条被限制在一定的大小,始终匹配 100%,并且我的定义progress会适应它。

Eri*_*Jin 4

您的代码的问题是背景和进度条部分具有相同的颜色,因此您看不到它。这可以通过对它们进行不同的着色来解决。

\n

该线progress = ((already_earned / to_reach ) * 100)还设置progress为百分比[0, 100]。然后你乘以width这个。对于 的输入100,例如 50% 的填充量会使椭圆远离5000屏幕并覆盖已经在屏幕上绘制的所有内容。

\n
@client.command(name=\'rank\')\nasync def rank(ctx, progress: float):\n\n    # Open the image and do stuff\n    # I tested this with a blank 800x400 RGBA\n\n    def new_bar(x, y, width, height, progress, bg=(129, 66, 97), fg=(211,211,211), fg2=(15,15,15)):\n        # Draw the background\n        draw.rectangle((x+(height/2), y, x+width+(height/2), y+height), fill=fg2, width=10)\n        draw.ellipse((x+width, y, x+height+width, y+height), fill=fg2)\n        draw.ellipse((x, y, x+height, y+height), fill=fg2)\n        width = int(width*progress)\n        # Draw the part of the progress bar that is actually filled\n        draw.rectangle((x+(height/2), y, x+width+(height/2), y+height), fill=fg, width=10)\n        draw.ellipse((x+width, y, x+height+width, y+height), fill=fg)\n        draw.ellipse((x, y, x+height, y+height), fill=fg)\n\n    new_bar(10, 10, 100, 25, progress)\n\n\n    # send\n
Run Code Online (Sandbox Code Playgroud)\n
\n

例子:

\n

progress = 0.25
\n在此输入图像描述

\n

progress = 0.9
\n在此输入图像描述

\n
\n

至于第二个代码片段,它的使用不floodfill()正确。thresh是“像素值与 \xe2\x80\x98background\xe2\x80\x99 的最大可容忍差异,以便将其替换。” 这意味着洪水填充实际上什么也不做,您只绘制了椭圆(而不是进度条前面的部分)。

\n
    # this draws a circle with bounding box `x,y` and `x+diam,y+diam`\n    # note that `x` is dependent on the progress value: higher progress means larger x, which means the circle is drawn more to the right\n    draw.ellipse([x,y, x+diam,y+diam], fill=color)\n    # If you look at the post where the code was given, you can see the error.\n    # In that post, the entirety of the progress bar already exists, and is a very different color (allowing the use of the threshold value).\n    # In this code, no progress bar exists yet, meaning everything else is just one solid color, and then the floodfill cannot do anything.\n    # Also, xy is specified as arbitrary coordinates, which you would need to change to fit your bar.\n    # This does nothing.\n    ImageDraw.floodfill(img, xy=(14,24), value=color, thresh=40)\n
Run Code Online (Sandbox Code Playgroud)\n

如果你想解决这个问题,你需要填充进度条左侧的颜色。上面的第一个函数已经做到了这一点。如果需要,您可以删除前 3 行以避免绘制背景,从而产生相同的结果。

\n