Selectors are a very useful idea and can do more that apply styles. We can also find widgets in Python code with selectors, and make updates to widgets in a simple expressive way. Let's look at how!
The query_one method gets a single widget in an app or other widget. If you call it with a selector it will return the first matching widget.
Let's say we have a widget with an ID of
send and we want to get a reference to it in our app. We could do this with the following:
If there is no widget with an ID of
send, Textual will raise a NoMatches exception. Otherwise it will return the matched widget.
You can also add a second parameter for the expected type.
If the matched widget is not a button (i.e. if
isinstance(widget, Button) equals
False), Textual will raise a WrongType exception.
The second parameter allows type-checkers like MyPy to know the exact return type. Without it, MyPy would only know the result of
query_one is a Widget (the base class).
If you call
query with no arguments, you will get back a
DOMQuery containing all widgets. This method is recursive, meaning it will also return child widgets (as many levels as required).
Here's how you might iterate over all the widgets in your app:
Called on the
app, this will retrieve all widgets in the app. If you call the same method on a widget, it will return the children of that widget.
All the query and related methods work on both App and Widget sub-classes.
You can call
query with a CSS selector. Let's look a few examples:
If we want to find all the button widgets, we could do something like the following:
Any selector that works in CSS will work with the
query method. For instance, if we want to find all the disabled buttons in a Dialog widget, we could do this:
Dialog Button.disabled says find all the
Button with a CSS class of
disabled that are a child of a
Query objects have a results method which is an alternative way of iterating over widgets. If you supply a type (i.e. a Widget class) then this method will generate only objects of that type.
The following example queries for widgets with the
disabled CSS class and iterates over just the Button objects.
This method allows type-checkers like MyPy to know the exact type of the object in the loop. Without it, MyPy would only know that
button is a
Widget (the base class).
We've seen that the query method returns a DOMQuery object you can iterate over in a for loop. Query objects behave like Python lists and support all of the same operations (such as
reverse(query) etc). They also have a number of other methods to simplify retrieving and modifying widgets.
First and last¶
Here's how we might find the last button in an app:
If there are no buttons, Textual will raise a NoMatches exception. Otherwise it will return a button widget.
last() accept an
expect_type argument that should be the class of the widget you are expecting. Let's say we want to get the last widget with class
.disabled, and we want to check it really is a button. We could do this:
The query selects all widgets with a
disabled CSS class. The
last method gets the last disabled widget and checks it is a
Button and not any other kind of widget.
If the last widget is not a button, Textual will raise a WrongType exception.
Specifying the expected type allows type-checkers like MyPy to know the exact return type.
Query objects have a filter method which further refines a query. This method will return a new query object with widgets that match both the original query and the new selector
Let's say we have a query which gets all the buttons in an app, and we want a new query object with just the disabled buttons. We could write something like this:
disabled_buttons will give us all the disabled buttons.
Here's how we could get all the buttons which don't have the
disabled class set.
Once you have a query object, you can loop over it to call methods on the matched widgets. Query objects also support a number of methods which make an update to every matched widget without an explicit loop.
For instance, let's say we want to disable all buttons in an app. We could do this by calling add_class() on a query object.
This single line is equivalent to the following:
Here are the other loop-free methods on query objects:
- set_class Sets a CSS class (or classes) on matched widgets.
- add_class Adds a CSS class (or classes) to matched widgets.
- remove_class Removes a CSS class (or classes) from matched widgets.
- toggle_class Sets a CSS class (or classes) if it is not set, or removes the class (or classes) if they are set on the matched widgets.
- remove Removes matched widgets from the DOM.
- refresh Refreshes matched widgets.