Menus and paginators
ballsdex.core.utils.menus.menus
Menu
Menu(bot: 'BallsDexBot', view: LayoutView, source: Source[P], *formatters: Formatter[P, Any])
A helper to have a pagination system inside of a LayoutView. It is possible to have
multiple menus per view.
A menu needs an instance of Source for the pagination, and one or more
Formatters which define how to display the current page. The source and
formatters are not directly linked, but must follow the same type
constraints, use your type checker to ensure you are using compatible classes.
If there are multiple pages, then this class will add a row of buttons to the position you choose via
init.
Example
Simple pagination for a select list, divided in sections of 25
from ballsdex.core.utils.menus import *
from discord.ui import *
my_options = [...]
view = discord.ui.LayoutView()
select = discord.ui.Select()
view.add_item(select)
# max number of options for a select is 25
source = ChunkedListSource(my_options, per_page=25)
# the formatter is only linked to its UI element, not the source itself
formatter = SelectFormatter(select)
menu = Menu(self.bot, view, source, formatter)
# by default, this will add the control buttons at the end
await menu.init()
await interaction.response.send_message(view=view)
Another example with a list of TextDisplay items, dynamically sized to respect the view's limits
from ballsdex.core.utils.menus import *
from discord.ui import *
async def generate_options():
async for item in queryset:
yield TextDisplay("## Item title\nItem description...")
view = discord.ui.LayoutView()
container = discord.ui.Container()
container.add_item(Section(
TextDisplay("# Message title"),
TextDisplay("Message subtitle"),
accessory=Thumbnail(user.display_avatar_url),
)
container.add_item(Separator())
source = ListSource(await dynamic_chunks(view, generate_options()))
formatter = ItemFormatter(container, position=2) # insert after separator
menu = Menu(self.bot, view, source, formatter)
await menu.init()
await interaction.response.send_message(view=view)
Tip
Using a type checker can reveal incompatible types
from ballsdex.core.utils.menus import *
source = ModelSource(BallInstance.objects.filter(player=player))
formatter = TextSource(item)
menu = Menu(self.bot, view, source, formatter)
# Argument of type "TextSource" cannot be assigned to parameter "formatters" of type "Formatter[P@Menu, Any]" in function "__init__"
# "TextSource" is not assignable to "Formatter[QuerySet[BallInstance], Any]"
Parameters:
-
bot('BallsDexBot') –The bot instance. Unused by itself, but some formatters may find it useful to have it available.
-
view(LayoutView) –The view you are attaching to. This is incompatible with V1 views.
-
source(Source[P]) –The source instance providing the elements to paginate
-
*formatters(Formatter[P, Any], default:()) –One or more formatters which will display the data from the source. They are attached to an item that belongs to the view.
Source code in ballsdex/core/utils/menus/menus.py
init
Prepare the menu before sending.
Parameters:
-
position(int | None, default:None) –The position at which to insert the control buttons. If
None, this will be at the end. -
container(Container | None, default:None) –If provided, the control buttons will be inserted inside the container instead of the outer view. The
positionparameter is respected within the container.
Source code in ballsdex/core/utils/menus/menus.py
ballsdex.core.utils.menus.source
Source
A source of long items to paginate and display over a LayoutView.
ListSource
ListSource(items: list[P])
Bases: Source[P]
Source code in ballsdex/core/utils/menus/source.py
ChunkedListSource
Source code in ballsdex/core/utils/menus/source.py
TextSource
TextSource(text: str, delims: Sequence[str] = ['\n#', '\n##', '\n###', '\n\n', '\n'], *, priority: bool = True, escape_mass_mentions: bool = True, shorten_by: int = 8, page_length: int = 3900, prefix: str = '', suffix: str = '')
Bases: ListSource[str]
Source code in ballsdex/core/utils/menus/source.py
ModelSource
ModelSource(queryset: QuerySet[M], per_page: int = 25)
ballsdex.core.utils.menus.formatter
Formatter
A class that edits one of the layout's components from a page of the menu.
Parameters:
-
item(I) –An item to edit from a page. Must be part of the view attached to the menu.
Source code in ballsdex/core/utils/menus/formatter.py
format_page
Edits the item attached with the given page.
Parameters:
-
page(P) –The current page of the menu
ItemFormatter
Bases: Formatter[Iterable[Item], Container]
This formatter takes as source a list of UI items, and dynamically add them to the given container.
Useful for iterations where a list of Section or TextDisplay
need to be given.
You are responsible of passing a list of items that respect the container limits. Use
dynamic_chunks to pagify your items while respecting limits.
Parameters:
-
item(Container) –Must be of container type.
-
position(int) –The position at which items must be inserted.
-
footer(bool, default:True) –Whether to include a "Page 1/max" footer at the end.
Source code in ballsdex/core/utils/menus/formatter.py
ballsdex.core.utils.menus.utils
dynamic_chunks
dynamic_chunks[I: Item](view: LayoutView, source: AsyncIterable[I]) -> list[list[I]]
Transform an iterable of Items into a list of lists. Each sublist is guaranteed to fit the
limits of the view given as argument.
This is useful combined with ItemFormatter to display a
dynamically-sized list of items.
Warning
This will ensure the limits of the view are respected at the time this function is called. Do not append new items to your view after calling this function as the results will be inaccurate.
Parameters:
-
view(LayoutView) –The finished view that will have the items appended to. The position does not matter as it only checks for global limits.
-
source(AsyncIterable[I]) –The generator providing the items. This is asynchronous in case you are doing this along an asynchronous iterator, like a database query or a Discord paginator.
Returns:
Source code in ballsdex/core/utils/menus/utils.py
iter_to_async
iter_to_async[T](source: Iterable[T]) -> AsyncIterable[T]
A helper to transform a synchronous iterable into an asynchronous one, useful for dynamic_chunks.