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




