You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Discord.py中按阵营/属性分组创建不同按钮数量的按钮行实现方案

Discord.py中按阵营/属性分组创建不同按钮数量的按钮行实现方案

看起来你想给Discord存档机器人的角色按钮按阵营分组,每个阵营单独占一行,对吧?我来帮你解决这个问题,同时也解释一下你遇到的ActionRow错误~

首先先处理你碰到的AttributeError: module 'discord.ui' has no attribute 'ActionRow'问题:这个错误基本是版本兼容导致的。如果你的Discord.py是v2.0及以上版本ActionRow确实在discord.ui模块下;但如果是v1.x旧版本,它直接属于discord模块(即discord.ActionRow)。更推荐你升级到最新的v2.x版本,因为v1.x已经停止维护,很多新功能都不支持啦。

接下来是核心的分组实现,需要分几步调整你的代码:

步骤1:给角色数据添加上阵营标记

首先得让每个角色的信息里带上所属阵营,这样我们才能按阵营分组:

characters = {
    "哈利波特": {"dropdown": NameDropdown, "image": "", "emoji": "", "house": "格兰芬多"},
    "赫敏格兰杰": {"dropdown": HermDropdown, "image": "", "emoji": "", "house": "格兰芬多"},
    "德拉科马尔福": {"dropdown": DracoDropdown, "image": "", "emoji": "", "house": "斯莱特林"},
    # 其他角色同理,都加上"house"字段
}

步骤2:按阵营重新整理角色数据

collections.defaultdict把平级的角色数据按阵营分组,方便后续按阵营生成按钮行:

from collections import defaultdict

# 按阵营分组:key是阵营名,value是该阵营的角色字典
characters_by_house = defaultdict(dict)
for char_name, char_data in characters.items():
    house = char_data["house"]
    characters_by_house[house][char_name] = char_data

步骤3:为每个阵营创建独立的按钮行

现在我们可以遍历每个阵营,给每个阵营单独生成一个按钮行,把该阵营的所有按钮都塞进这个行里,再添加到View中:

async def archive(ctx):
    # 上面的characters定义和分组代码放这里...

    embed = discord.Embed(
        title="角色档案查询",
        description="选择你要查看的角色:",
        color=COLOR
    )

    view = discord.ui.View(timeout=None)

    # 遍历每个阵营,生成专属按钮行
    for house, house_chars in characters_by_house.items():
        # 创建当前阵营的按钮行
        # 注意:v2.x用discord.ui.ActionRow,v1.x替换为discord.ActionRow
        row = discord.ui.ActionRow()
        
        # 给当前阵营的每个角色生成按钮
        for char_name, char_data in house_chars.items():
            button = discord.ui.Button(
                label=char_name,
                style=discord.ButtonStyle.secondary,
                emoji=char_data["emoji"],
                custom_id=f"character_{char_name}"
            )

            # 按钮回调函数,用默认参数捕获当前角色的信息,避免闭包陷阱
            async def button_callback(interaction: discord.Interaction, char_name=char_name, char_data=char_data):
                char_dropdown = char_data["dropdown"]()
                embed = discord.Embed(
                    title=f"{char_name}的档案",
                    description="",
                    color=COLOR
                )
                embed.set_image(url=char_data["image"]) 
                await interaction.response.send_message(embed=embed, view=char_dropdown, ephemeral=True)

            button.callback = button_callback
            # 把按钮添加到当前阵营的行里
            row.add_item(button)
        
        # 把当前阵营的按钮行添加到View中
        view.add_item(row)

    await ctx.send(embed=embed, view=view)
    await ctx.message.delete()

小提醒:关于闭包陷阱

你原来的代码里已经用了默认参数(char_name=char_name, char_data=char_data)来避免闭包陷阱,这点做得非常好!如果不这么处理,所有按钮的回调都会引用循环最后一次的角色数据,导致不管点哪个按钮都触发最后一个角色的详情。

现在这样修改后,每个阵营的按钮就会单独占一行,不管该阵营有2-4个角色,都会整齐地显示在同一行里啦~

备注:内容来源于stack exchange,提问作者Phi

火山引擎 最新活动