app basics and doc structure

This commit is contained in:
Will McGugan
2022-09-04 20:40:12 +01:00
parent ca9492ac56
commit 432db21512
24 changed files with 331 additions and 30 deletions

94
docs/guide/app.md Normal file
View File

@@ -0,0 +1,94 @@
# App Basics
In this chapter we will cover what you will need to know to build a Textual application. Just enough to get you up to speed. We will go in to more detail in the following chapters.
## The App class
The first step in building a Textual app is to import the [App][textual.app.App] class and create a subclass. Let's look at the simplest app class:
```python
--8<-- "docs/examples/app/simple01.py"
```
### The run method
To run an app we create an instance and call [run()][textual.app.App.run].
```python hl_lines="8-10" title="simple02.py"
--8<-- "docs/examples/app/simple02.py"
```
Apps don't get much simpler than this&mdash;don't expect it to do much.
!!! tip
The `__name__ == "__main__":` condition is true only if you run the file with `python` command. This allows us to import `app` without running the app immediately. It also allows the [devtools run](devtools.md#run) command to run the app in development mode. See the [Python docs](https://docs.python.org/3/library/__main__.html#idiomatic-usage) for more information.
If we run this app with `python simple02.py` you will see a blank terminal, something like the following:
```{.textual path="docs/examples/app/simple02.py" title="simple02.py"}
```
When you call [App.run()][textual.app.App.run] Textual puts the terminal in to a special state called *application mode*. When in application mode, the terminal will no longer echo what you type. Textual will take over responding to user input (keyboard and mouse) and will update the visible portion of the terminal (i.e. the *screen*).
If you hit ++ctrl+c++ Textual will exit application mode and return you to the command prompt. Any content you had in the terminal prior to application mode will be restored.
## Events
Textual has an event system you can use to respond to key presses, mouse actions, and also internal state changes. Event handlers are methods which are prefixed with `on_` followed by the name of the event.
One such event is the *mount* event which is sent to an application after it enters application mode. You can respond to this event by defining a method called `on_mount`.
Another such event is `on_key` which is sent when the user presses a key. The following example contains handlers for both those events:
```python title="event01.py"
--8<-- "docs/examples/app/event01.py"
```
The `on_mount` handler sets the `self.styles.background` attribute to `"darkblue"` which (as you can probably guess) turns the background blue. Since the mount event is sent immediately after entering application mode, you will see a blue screen when you run the code:
```{.textual path="docs/examples/app/event01.py" hl_lines="23-25"}
```
The key event handler (`on_key`) specifies an `event` parameter which should be a [events.Key][textual.events.Key] instance. Every event has an associated event object which will be passed to the handler method if it is present in the method's parameter list.
!!! note
It is unusual for a method's parameters to affect how it is called. Textual accomplishes this by inspecting the method prior to calling it.
For some events, such as the key event, there is additional information on the event object. In the case of [events.Key][textual.events.Key] it will contain the key that was pressed.
The `on_key` method above uses the `key` attribute on the Key event to change the background color if any of the keys 0-9 are pressed.
### Async events
Textual is powered by Python's [asyncio](https://docs.python.org/3/library/asyncio.html) framework which uses the `async` and `await` keywords to coordinate events.
Textual knows to *await* your event handlers if they are generators (i.e. prefixed with the `async` keywords).
!!! note
Don't worry if you aren't familiar with the async programming in Python. You can build many apps without using them.
## Widgets
Widgets are self-contained components responsible for generating the output for a portion of the screen and can respond to events in much the same way as the App. Most apps that do anything interesting will contain at least one (and probably many) widgets which together form a User Interface.
Widgets can be as simple as a piece of text, a button, or a fully-fledge component like a text editor or file browser (which may contain widgets of their own).
### Composing
To add widgets to your app implement a [`compose()`][textual.app.App.compose] method which should return a iterable of Widget instances. A list would work, but it is convenient to yield widgets, making the method a *generator*.
The following example imports a builtin Welcome widget and yields it from compose.
```python title="widgets01.py"
--8<-- "docs/examples/app/widgets01.py"
```
When you run this code, Textual will *mount* the Welcome widget which contains a Markdown content area and a button:
```{.textual path="docs/examples/app/widgets01.py"}
```
Notice the `on_button_pressed` method which handles the [Button.Pressed][textual.widgets.Button] event send by the button contained in the Welcome widget. The handlers calls [App.exit()][textual.app.App] to exit the app.

View File

@@ -1,5 +1,13 @@
# Devtools
!!! note inline end
If you don't have the `textual` command on your path, you may have forgotten so install with the `dev` switch.
See [getting started](../getting_started.md#installation) for details.
Textual comes with a command line application of the same name. The `textual` command is a super useful tool that will help you to build apps.
Take a moment to look through the available sub-commands. There will be even more helpful tools here in the future.
@@ -8,6 +16,7 @@ Take a moment to look through the available sub-commands. There will be even mor
textual --help
```
## Run
You can run Textual apps with the `run` subcommand. If you supply a path to a Python file it will load and run the application.
@@ -28,9 +37,9 @@ textual run my_app.py:alternative_app
## Console
When running any terminal application, you can no longer use `print` when debugging (or log to the console). This is because anything you write to standard output would overwrite application content, making it unreadable. Fortunately Textual supplies a debug console of its own which has some super helpful features.
When running a terminal application, you will generally no longer be able to use `print` when debugging (or log to the console). This is because anything you write to standard output would overwrite application content, making it unreadable. Fortunately Textual supplies a debug console of its own which has some super helpful features.
To use the console, open up 2 terminal emulators. Run the following in one of the terminals:
To use the console, open up **two** terminal emulators. Run the following in one of the terminals:
```bash
textual console
@@ -41,22 +50,63 @@ You should see the Textual devtools welcome message:
```{.textual title="textual console" path="docs/examples/getting_started/console.py", press="_,_"}
```
In the other console, run your application using `textual run` and the `--dev` switch:
In the other console, run your application with `textual run` and the `--dev` switch:
```bash
textual run --dev my_app.py
```
Anything you `print` from your application will be displayed in the console window.
Anything you `print` from your application will be displayed in the console window. Textual will also write log messages to this window which may be helpful when debugging your application.
### Textual log
### Verbosity
In addition to printing strings, Textual console supports more advanced formatting in logs. To write advanced logs import `log` from `textual` as follows:
Textual writes log messages to inform you about certain events, such as when the user presses a key or clicks on the terminal. To avoid swamping you with too much information, some events are marked as "verbose" and will be excluded from the logs. If you want to see these log messages, you can add the `-v` switch.
```bash
textual console -v
```
## Textual log
In addition to simple strings, Textual console supports [Rich](https://rich.readthedocs.io/en/latest/) formatting. To write rich logs, import `log` as follows:
```python
from textual import log
```
You can logs strings, other Python data types which will be pretty printed in the console. You can also log [Rich renderables](https://rich.readthedocs.io/en/stable/protocol.html).
This method will pretty print data structures (like lists and dicts) as well as [Rich renderables](https://rich.readthedocs.io/en/stable/protocol.html). Here are some examples:
```python
log("Hello, World") # simple string
log(locals()) # Log local variables
log(children=self.children, pi=3.141592) # key/values
log(self.tree) # Rich renderables
```
Textual log messages may contain [console Markup](https://rich.readthedocs.io/en/stable/markup.html):
```python
log("[bold red]DANGER![/] We're having too much fun")
```
### Log method
There's a convenient shortcut to `log` available on the App and Widget objects you can use in event handlers:
```python
from textual.app import App
class LogApp(App):
def on_load(self):
self.log("In the log handler!", pi=3.141529)
def on_mount(self):
self.log(self.tree)
app = LogApp()
if __name__ == "__main__":
app.run()
```