Textual - An Intro to DOM Queries (Part I)
In this article, you will learn how to query the DOM in Textual. You will discover that the DOM keeps track of all the widgets in your application. By running queries against the DOM, you can find widgets quickly and update them, too.
You will be learning the following topics related to the DOM:
The query one method
Textual queries
You will learn more in the second part of this series next week!
You will soon see the value of working with DOM queries and the power that these queries give you. Let's get started!
The Query One Method
You will find thequery_one() method throughout the Textual documentation and many Textual applications on GitHub. You may use query_one() to retrieve a single widget that matches a CSS selector or a widget type.
You can pass in up to two parameters to query_one():
The CSS selector
The widget type
Or both at the same time
If you pass in both, you should pass in the CSS selector first, with the widget type being the second parameter.
Try some of this out. Open up your Python editor and create a file named query_input.py. Then enter this code in it:
Your code creates an Input and a Button widget. Enter some text in the Input widget and press the button. Your on_button_pressed() method will get called. You call query_one() and pass it an Input widget. Then, you update the returned Input widget’s value with a new string.
Here is what the application might look like:
Now, you will try writing a new piece of code where you use query_one() with a CSS selector. Create a new file called query_one_same_ids.py and use this code:
In this example, you create two widgets with different IDs. Then you use query_one() to select the Label widget and update its text.
If you call query_one() and there are no matches, you will get a NoMatches exception. On the other hand, if there is more than one match, the method will return the first item that does match.
What will the following code do if you put it in your example above?
self.query_one(”#label”, Button)If you guessed that Textual will raise an exception, you should congratulate yourself. You have good intuition! If the widget matches the CSS selector but not the widget type, then you will get a WrongType exception raised.
Textual Queries
Textual has more than one way to query the DOM. You may also use the query() method, which you can use to query or find multiple widgets. When you call query(), it will return a DOMQuery object, which behaves as a list-like container of widgets.
You can see how this works by writing some code. Create a new Python file named query_all.py and add this code to it:
The idea is to get all the widgets in your application and print them out. Of course, you can’t print out anything when your terminal application is blocking stdout, so instead, you create a string of widgets separated by new lines and update the Label widget.
Here is an example of what you might get if you run the code and press the button on your machine:
You might be surprised by that output. Perhaps you thought you would only see a Label and a Button widget in that list? If so, you forgot that a Screen widget is always lurking in the background. But there are also two more: a ToastRack and a Tooltip widget. These come with all your applications. The ToastRack positions Toast widgets, which you use to display a notification message. A Tooltip is a message that appears when you hover your mouse over a widget.
You do not need to know more about those extra widgets now.
Also note that all query methods can be used on both the App and Widget subclasses, which is very handy.
Using Selectors
You can use CSS selectors with query() in much the same way as you can with query_one(). The difference, of course, is that query() always returns an iterable DOMObject.
Let’s pretend you want to get all the Button widgets in your application and iterate over them. Create a new Python script called query_button.py with this code:
Here you are passing in a string, "Button", to query(). If using query_one, you would use the Button type directly. Regardless, when you run this code and press the button, you will see the following:
That worked great! This time, you queried the DOM and returned all the Button widgets in your application.
What if you wanted to find all the disabled buttons in your code? You can disable widgets using the disabled style flag or the CSS attribute. To find those widgets, you would update the query like this: widgets = self.query("Button.disabled").
Results
The query objects in Textual also provide a results() method that you can use as an alternative way of iterating over the widgets. For example, you can use results() to rewrite the query above that would retrieve all the disabled buttons to be something like this:
This code combines the last example query with the last full code example. Although this latter version is more verbose, you might find it easier to read than the original query for disabled widgets.
Another benefit of using results() is that Python type checkers, such as Mypy, can use it to determine the widget type in the loop. When you do not use results(), then Mypy will only know that you are looping over a Widget object, rather than a Button object.
Wrapping Up
You learned the basics of using Textual’s DOM query methods in this article. You can use these query methods to access one or more widgets in your user interface.
Specifically, you learned about the following:
The query one method
Textual queries
Textual is a great way to create a user interface with Python. You should check it out today!
Learn More
Want to learn more about Textual? Check out my book:










