From 91a13406e66a5f4e715118be3aa9f0b9484d0d96 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 13:41:10 +0100 Subject: [PATCH 01/28] Remove trailing whitespace Including one instance that I wouldn't have, but the pre-commit hooks have, and it seems fine. --- docs/tutorial.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index c9e03c89e..b41a7365b 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -1,18 +1,18 @@ --- hide: - - navigation + - navigation --- # Tutorial Welcome to the Textual Tutorial! -By the end of this page you should have a solid understanding of app development with Textual. +By the end of this page you should have a solid understanding of app development with Textual. !!! quote If you want people to build things, make it fun. - + — **Will McGugan** (creator of Rich and Textual) @@ -83,7 +83,7 @@ Return types follow `->`. So `-> str:` indicates this method returns a string. The first step in building a Textual app is to import and extend the `App` class. Here's a basic app class we will use as a starting point for the stopwatch app. -```python title="stopwatch01.py" +```python title="stopwatch01.py" --8<-- "docs/examples/tutorial/stopwatch01.py" ``` @@ -134,7 +134,7 @@ The final three lines create an instance of the app and calls the [run()][textua ## Designing a UI with widgets -Textual comes with a number of builtin widgets, like Header and Footer, which are versatile and re-usable. We will need to build some custom widgets for the stopwatch. Before we dive in to that, let's first sketch a design for the app — so we know what we're aiming for. +Textual comes with a number of builtin widgets, like Header and Footer, which are versatile and re-usable. We will need to build some custom widgets for the stopwatch. Before we dive in to that, let's first sketch a design for the app — so we know what we're aiming for.
--8<-- "docs/images/stopwatch.excalidraw.svg" @@ -157,9 +157,9 @@ Let's add those to the app. Just a skeleton for now, we will add the rest of the --8<-- "docs/examples/tutorial/stopwatch02.py" ``` -We've imported two new widgets in this code: `Button`, which creates a clickable button, and `Static` which is a base class for a simple control. We've also imported `Container` from `textual.containers` which (as the name suggests) is a Widget which contains other widgets. +We've imported two new widgets in this code: `Button`, which creates a clickable button, and `Static` which is a base class for a simple control. We've also imported `Container` from `textual.containers` which (as the name suggests) is a Widget which contains other widgets. -We've defined an empty `TimeDisplay` widget by extending `Static`. We will flesh this out later. +We've defined an empty `TimeDisplay` widget by extending `Static`. We will flesh this out later. The Stopwatch widget class also extends `Static`. This class has a `compose()` method which yields child widgets, consisting of three `Button` objects and a single `TimeDisplay` object. These widgets will form the stopwatch in our sketch. @@ -168,7 +168,7 @@ The Stopwatch widget class also extends `Static`. This class has a `compose()` m The Button constructor takes a label to be displayed in the button (`"Start"`, `"Stop"`, or `"Reset"`). Additionally some of the buttons set the following parameters: - `id` is an identifier we can use to tell the buttons apart in code and apply styles. More on that later. -- `variant` is a string which selects a default style. The "success" variant makes the button green, and the "error" variant makes it red. +- `variant` is a string which selects a default style. The "success" variant makes the button green, and the "error" variant makes it red. ### Composing the widgets @@ -194,8 +194,8 @@ Every widget has a `styles` object with a number of attributes that impact how t self.styles.background = "blue" self.styles.color = "white" ``` - -While it's possible to set all styles for an app this way, it is rarely necessary. Textual has support for CSS (Cascading Style Sheets), a technology used by web browsers. CSS files are data files loaded by your app which contain information about styles to apply to your widgets. + +While it's possible to set all styles for an app this way, it is rarely necessary. Textual has support for CSS (Cascading Style Sheets), a technology used by web browsers. CSS files are data files loaded by your app which contain information about styles to apply to your widgets. !!! info @@ -213,7 +213,7 @@ Let's add a CSS file to our application. Adding the `CSS_PATH` class variable tells Textual to load the following file when the app starts: -```sass title="stopwatch03.css" +```sass title="stopwatch03.css" --8<-- "docs/examples/tutorial/stopwatch03.css" ``` @@ -228,7 +228,7 @@ This app looks much more like our sketch. let's look at how the Textual uses `st CSS files contain a number of _declaration blocks_. Here's the first such block from `stopwatch03.css` again: -```sass +```sass Stopwatch { layout: horizontal; background: $boost; @@ -263,7 +263,7 @@ TimeDisplay { } Button { - width: 16; + width: 16; } #start { @@ -354,7 +354,7 @@ We have added two reactive attributes: `start_time` will contain the time in sec Both attributes will be available on `self` as if you had assigned them in `__init__`. If you write to either of these attributes the widget will update automatically. -!!! info +!!! info The `monotonic` function in this example is imported from the standard library `time` module. It is similar to `time.time` but won't go backwards if the system clock is changed. From a90a2fd72879f98090ea17217b1981a2905fc03a Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 13:45:34 +0100 Subject: [PATCH 02/28] Correct the name of mypy It seems that mypy's preference for its name is mypy (or even my[py]), but not Mypy. --- docs/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index b41a7365b..e73088655 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -64,7 +64,7 @@ python stopwatch.py Type hints are entirely optional in Textual. We've included them in the example code but it's up to you whether you add them to your own projects. -We're a big fan of Python type hints at Textualize. If you haven't encountered type hinting, it's a way to express the types of your data, parameters, and return values. Type hinting allows tools like [Mypy](https://mypy.readthedocs.io/en/stable/) to catch bugs before your code runs. +We're a big fan of Python type hints at Textualize. If you haven't encountered type hinting, it's a way to express the types of your data, parameters, and return values. Type hinting allows tools like [mypy](https://mypy.readthedocs.io/en/stable/) to catch bugs before your code runs. The following function contains type hints: From 93e403c8dc7d5738aaf94e3f7231dfb937a19391 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 14:11:12 +0100 Subject: [PATCH 03/28] Mark mention of Widget as a class as code --- docs/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index e73088655..0324a4ed0 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -157,7 +157,7 @@ Let's add those to the app. Just a skeleton for now, we will add the rest of the --8<-- "docs/examples/tutorial/stopwatch02.py" ``` -We've imported two new widgets in this code: `Button`, which creates a clickable button, and `Static` which is a base class for a simple control. We've also imported `Container` from `textual.containers` which (as the name suggests) is a Widget which contains other widgets. +We've imported two new widgets in this code: `Button`, which creates a clickable button, and `Static` which is a base class for a simple control. We've also imported `Container` from `textual.containers` which (as the name suggests) is a `Widget` which contains other widgets. We've defined an empty `TimeDisplay` widget by extending `Static`. We will flesh this out later. From 82499c31e789c856568de97e72e6653415e4529f Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 14:16:24 +0100 Subject: [PATCH 04/28] Capital at the start of a sentence --- docs/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index 0324a4ed0..5a4eeceb7 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -222,7 +222,7 @@ If we run the app now, it will look *very* different. ```{.textual path="docs/examples/tutorial/stopwatch03.py" title="stopwatch03.py"} ``` -This app looks much more like our sketch. let's look at how the Textual uses `stopwatch03.css` to apply styles. +This app looks much more like our sketch. Let's look at how the Textual uses `stopwatch03.css` to apply styles. ### CSS basics From 2e25c894e3d97c4b9a65ae3341b936e91b3d65d6 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 14:18:23 +0100 Subject: [PATCH 05/28] Add code markup to the mention of a class name --- docs/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index 5a4eeceb7..ea9cb749e 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -250,7 +250,7 @@ Here's how this CSS code changes how the `Stopwatch` widget is displayed. - `background: $boost` sets the background color to `$boost`. The `$` prefix picks a pre-defined color from the builtin theme. There are other ways to specify colors such as `"blue"` or `rgb(20,46,210)`. - `height: 5` sets the height of our widget to 5 lines of text. - `padding: 1` sets a padding of 1 cell around the child widgets. -- `margin: 1` sets a margin of 1 cell around the Stopwatch widget to create a little space between widgets in the list. +- `margin: 1` sets a margin of 1 cell around the `Stopwatch` widget to create a little space between widgets in the list. Here's the rest of `stopwatch03.css` which contains further declaration blocks: From 50fb72396599edf9df9e59abe52fa1a57e5e08f8 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 14:19:50 +0100 Subject: [PATCH 06/28] Add code markup to the mention of a class name --- docs/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index ea9cb749e..0dab08caa 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -284,7 +284,7 @@ The `TimeDisplay` block aligns text to the center (`content-align`), fades it sl The `Button` block sets the width (`width`) of buttons to 16 cells (character widths). -The last 3 blocks have a slightly different format. When the declaration begins with a `#` then the styles will be applied to widgets with a matching "id" attribute. We've set an ID on the Button widgets we yielded in compose. For instance the first button has `id="start"` which matches `#start` in the CSS. +The last 3 blocks have a slightly different format. When the declaration begins with a `#` then the styles will be applied to widgets with a matching "id" attribute. We've set an ID on the `Button` widgets we yielded in compose. For instance the first button has `id="start"` which matches `#start` in the CSS. The buttons have a `dock` style which aligns the widget to a given edge. The start and stop buttons are docked to the left edge, while the reset button is docked to the right edge. From 758ddfa86f9a93f63a4eea73e41624f65ff04ffb Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 14:20:41 +0100 Subject: [PATCH 07/28] Add code markup to the mention of a method name --- docs/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index 0dab08caa..b67ff3ac3 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -284,7 +284,7 @@ The `TimeDisplay` block aligns text to the center (`content-align`), fades it sl The `Button` block sets the width (`width`) of buttons to 16 cells (character widths). -The last 3 blocks have a slightly different format. When the declaration begins with a `#` then the styles will be applied to widgets with a matching "id" attribute. We've set an ID on the `Button` widgets we yielded in compose. For instance the first button has `id="start"` which matches `#start` in the CSS. +The last 3 blocks have a slightly different format. When the declaration begins with a `#` then the styles will be applied to widgets with a matching "id" attribute. We've set an ID on the `Button` widgets we yielded in `compose`. For instance the first button has `id="start"` which matches `#start` in the CSS. The buttons have a `dock` style which aligns the widget to a given edge. The start and stop buttons are docked to the left edge, while the reset button is docked to the right edge. From e109599a97391f5b14b35de24e6c5109f1b22ca2 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 14:21:39 +0100 Subject: [PATCH 08/28] Add code markup to the mention of a class name --- docs/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index b67ff3ac3..edd46d18f 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -292,7 +292,7 @@ You may have noticed that the stop button (`#stop` in the CSS) has `display: non ### Dynamic CSS -We want our Stopwatch widget to have two states: a default state with a Start and Reset button; and a _started_ state with a Stop button. When a stopwatch is started it should also have a green background and bold text. +We want our `Stopwatch` widget to have two states: a default state with a Start and Reset button; and a _started_ state with a Stop button. When a stopwatch is started it should also have a green background and bold text.
--8<-- "docs/images/css_stopwatch.excalidraw.svg" From ed20f4a8e68c90864b9860d18ea4b8e284942dca Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 14:23:26 +0100 Subject: [PATCH 09/28] Add code markup a string that is a widget ID Elsewhere in the docs we see `"something"` for a class, so I feel it follows that "#something" should really be `"#something"` too. --- docs/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index edd46d18f..97704a9d1 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -317,7 +317,7 @@ Some of the new styles have more than one selector separated by a space. The spa } ``` -The `.started` selector matches any widget with a `"started"` CSS class. While `#start` matches a child widget with an ID of "start". So it matches the Start button only for Stopwatches in a started state. +The `.started` selector matches any widget with a `"started"` CSS class. While `#start` matches a child widget with an ID of `"start"`. So it matches the Start button only for Stopwatches in a started state. The rule is `"display: none"` which tells Textual to hide the button. From be36c55d37b9fa6b29f56e2fef6c443acaf67551 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 14:26:44 +0100 Subject: [PATCH 10/28] Add code markup to the mention of a class name --- docs/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index 97704a9d1..221765557 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -350,7 +350,7 @@ You can declare a reactive attribute with [reactive][textual.reactive.reactive]. --8<-- "docs/examples/tutorial/stopwatch05.py" ``` -We have added two reactive attributes: `start_time` will contain the time in seconds when the stopwatch was started, and `time` will contain the time to be displayed on the Stopwatch. +We have added two reactive attributes: `start_time` will contain the time in seconds when the stopwatch was started, and `time` will contain the time to be displayed on the `Stopwatch`. Both attributes will be available on `self` as if you had assigned them in `__init__`. If you write to either of these attributes the widget will update automatically. From 86b5612c9f590ef1708cd5ec62219bc0173b9bd2 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 14:28:04 +0100 Subject: [PATCH 11/28] Lower-case mention of stopwatches This looks more like it's been used as a normal word, rather than as a plural for the class. --- docs/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index 221765557..f60c0576b 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -374,7 +374,7 @@ The end result is that the `Stopwatch` widgets show the time elapsed since the w ```{.textual path="docs/examples/tutorial/stopwatch05.py" title="stopwatch05.py"} ``` -We've seen how we can update widgets with a timer, but we still need to wire up the buttons so we can operate Stopwatches independently. +We've seen how we can update widgets with a timer, but we still need to wire up the buttons so we can operate stopwatches independently. ### Wiring buttons From 1dbde34b40eae4a66377e3ea885d24a45c9a6a14 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 14:31:32 +0100 Subject: [PATCH 12/28] Add code markup a string that is a class Elsewhere a string that is a class is marked up this way, so let's follow that earlier standard. --- docs/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index f60c0576b..62894d36b 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -414,7 +414,7 @@ This code supplies missing features and makes our app useful. We've made the fol - The first line retrieves `id` attribute of the button that was pressed. We can use this to decide what to do in response. - The second line calls `query_one` to get a reference to the `TimeDisplay` widget. - We call the method on `TimeDisplay` that matches the pressed button. -- We add the "started" class when the Stopwatch is started (`self.add_class("started)`), and remove it (`self.remove_class("started")`) when it is stopped. This will update the Stopwatch visuals via CSS. +- We add the `"started"` class when the Stopwatch is started (`self.add_class("started)`), and remove it (`self.remove_class("started")`) when it is stopped. This will update the Stopwatch visuals via CSS. If you run stopwatch06.py you will be able to use the stopwatches independently. From 45db071bb5d7100993f5eca3555329b6c05cc989 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 14:32:20 +0100 Subject: [PATCH 13/28] Add missing end quote to a string --- docs/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index 62894d36b..35e85053b 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -414,7 +414,7 @@ This code supplies missing features and makes our app useful. We've made the fol - The first line retrieves `id` attribute of the button that was pressed. We can use this to decide what to do in response. - The second line calls `query_one` to get a reference to the `TimeDisplay` widget. - We call the method on `TimeDisplay` that matches the pressed button. -- We add the `"started"` class when the Stopwatch is started (`self.add_class("started)`), and remove it (`self.remove_class("started")`) when it is stopped. This will update the Stopwatch visuals via CSS. +- We add the `"started"` class when the Stopwatch is started (`self.add_class("started")`), and remove it (`self.remove_class("started")`) when it is stopped. This will update the Stopwatch visuals via CSS. If you run stopwatch06.py you will be able to use the stopwatches independently. From d6a2c88713fb7fa47ca7e5ff8f35d2e7a8fb26e7 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 14:35:39 +0100 Subject: [PATCH 14/28] Add code markup to a number of code-oriented things in a line --- docs/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index 35e85053b..9376b787e 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -435,7 +435,7 @@ Let's use these methods to implement adding and removing stopwatches to our app. Here's a summary of the changes: -- The Container object in StopWatchApp grew a "timers" ID. +- The `Container` object in `StopWatchApp` grew a `"timers"` ID. - Added `action_add_stopwatch` to add a new stopwatch. - Added `action_remove_stopwatch` to remove a stopwatch. - Added keybindings for the actions. From a342a2cb530e9cf6910bd70e5c795cd6ca7dcdf1 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 14:37:03 +0100 Subject: [PATCH 15/28] Add code markup to the mention of a class name --- docs/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index 9376b787e..866738a15 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -440,7 +440,7 @@ Here's a summary of the changes: - Added `action_remove_stopwatch` to remove a stopwatch. - Added keybindings for the actions. -The `action_add_stopwatch` method creates and mounts a new stopwatch. Note the call to [query_one()][textual.dom.DOMNode.query_one] with a CSS selector of `"#timers"` which gets the timer's container via its ID. Once mounted, the new Stopwatch will appear in the terminal. That last line in `action_add_stopwatch` calls [scroll_visible()][textual.widget.Widget.scroll_visible] which will scroll the container to make the new Stopwatch visible (if required). +The `action_add_stopwatch` method creates and mounts a new stopwatch. Note the call to [query_one()][textual.dom.DOMNode.query_one] with a CSS selector of `"#timers"` which gets the timer's container via its ID. Once mounted, the new Stopwatch will appear in the terminal. That last line in `action_add_stopwatch` calls [scroll_visible()][textual.widget.Widget.scroll_visible] which will scroll the container to make the new `Stopwatch` visible (if required). The `action_remove_stopwatch` function calls [query()][textual.dom.DOMNode.query] with a CSS selector of `"Stopwatch"` which gets all the `Stopwatch` widgets. If there are stopwatches then the action calls [last()][textual.css.query.DOMQuery.last] to get the last stopwatch, and [remove()][textual.css.query.DOMQuery.remove] to remove it. From bd43f0466ab28b7c34315804a9d21e35fddc7a28 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 14:41:10 +0100 Subject: [PATCH 16/28] Harmonise the tutorial's markup of filenames In some cases filenames where `marked-up.py`, in other cases they were "marked-up.py", and in some cases they were just mentioned with no-markup.py. This commit seeks to make all filename mentions, in text, the same. --- docs/tutorial.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index 866738a15..52d12df70 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -102,7 +102,7 @@ Hit ++ctrl+c++ to exit the app and return to the command prompt. ### A closer look at the App class -Let's examine stopwatch01.py in more detail. +Let's examine `stopwatch01.py` in more detail. ```python title="stopwatch01.py" hl_lines="1 2" --8<-- "docs/examples/tutorial/stopwatch01.py" @@ -179,7 +179,7 @@ The new line in `Stopwatch.compose()` yields a single `Container` object which w ### The unstyled app -Let's see what happens when we run "stopwatch02.py". +Let's see what happens when we run `stopwatch02.py`. ```{.textual path="docs/examples/tutorial/stopwatch02.py" title="stopwatch02.py"} ``` @@ -335,7 +335,7 @@ The following code will start or stop the stopwatches in response to clicking a The `on_button_pressed` method is an *event handler*. Event handlers are methods called by Textual in response to an *event* such as a key press, mouse click, etc. Event handlers begin with `on_` followed by the name of the event they will handler. Hence `on_button_pressed` will handle the button pressed event. -If you run "stopwatch04.py" now you will be able to toggle between the two states by clicking the first button: +If you run `stopwatch04.py` now you will be able to toggle between the two states by clicking the first button: ```{.textual path="docs/examples/tutorial/stopwatch04.py" title="stopwatch04.py" press="tab,tab,tab,_,enter,_,_,_"} ``` @@ -416,7 +416,7 @@ This code supplies missing features and makes our app useful. We've made the fol - We call the method on `TimeDisplay` that matches the pressed button. - We add the `"started"` class when the Stopwatch is started (`self.add_class("started")`), and remove it (`self.remove_class("started")`) when it is stopped. This will update the Stopwatch visuals via CSS. -If you run stopwatch06.py you will be able to use the stopwatches independently. +If you run `stopwatch06.py` you will be able to use the stopwatches independently. ```{.textual path="docs/examples/tutorial/stopwatch06.py" title="stopwatch06.py" press="tab,enter,_,_,tab,enter,_,tab"} ``` @@ -451,6 +451,6 @@ If you run `stopwatch.py` now you can add a new stopwatch with the ++a++ key and ## What next? -Congratulations on building your first Textual application! This tutorial has covered a lot of ground. If you are the type that prefers to learn a framework by coding, feel free. You could tweak stopwatch.py or look through the examples. +Congratulations on building your first Textual application! This tutorial has covered a lot of ground. If you are the type that prefers to learn a framework by coding, feel free. You could tweak `stopwatch.py` or look through the examples. Read the guide for the full details on how to build sophisticated TUI applications with Textual. From 7676a6cf4c8fade5237e96758482245946fd52bc Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 14:45:54 +0100 Subject: [PATCH 17/28] Trailing whitespace cleanup --- docs/guide/devtools.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/guide/devtools.md b/docs/guide/devtools.md index fa835bba3..9b60dbd4c 100644 --- a/docs/guide/devtools.md +++ b/docs/guide/devtools.md @@ -3,7 +3,7 @@ !!! 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. @@ -44,7 +44,7 @@ textual run --dev my_app.py One of the the features of *dev* mode is live editing of CSS files: any changes to your CSS will be reflected in the terminal a few milliseconds later. -This is a great feature for iterating on your app's look and feel. Open the CSS in your editor and have your app running in a terminal. Edits to your CSS will appear almost immediately after you save. +This is a great feature for iterating on your app's look and feel. Open the CSS in your editor and have your app running in a terminal. Edits to your CSS will appear almost immediately after you save. ## Console @@ -120,4 +120,3 @@ if __name__ == "__main__": LogApp.run() ``` - From ceb531e5354b3f5c2d4bf1b0083ab0717e79d361 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 14:46:39 +0100 Subject: [PATCH 18/28] Add code markup to the mention of a class name --- docs/guide/devtools.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/devtools.md b/docs/guide/devtools.md index 9b60dbd4c..970b8f768 100644 --- a/docs/guide/devtools.md +++ b/docs/guide/devtools.md @@ -23,7 +23,7 @@ You can run Textual apps with the `run` subcommand. If you supply a path to a Py textual run my_app.py ``` -The `run` sub-command assumes you have an App instance called `app` in the global scope of your Python file. If the application is called something different, you can specify it with a colon following the filename: +The `run` sub-command assumes you have an `App` instance called `app` in the global scope of your Python file. If the application is called something different, you can specify it with a colon following the filename: ```bash textual run my_app.py:alternative_app From 4a6e4ff02589ddb85f93b24c07d5ba67d913fb5a Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 15:26:54 +0100 Subject: [PATCH 19/28] Reword the details of how `textual run` finds an app It seems the docs are lagging behind the code here, so this commit attempts to update them so they're more in line with how `textual run` works. --- docs/guide/devtools.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/devtools.md b/docs/guide/devtools.md index 970b8f768..316471eba 100644 --- a/docs/guide/devtools.md +++ b/docs/guide/devtools.md @@ -23,7 +23,7 @@ You can run Textual apps with the `run` subcommand. If you supply a path to a Py textual run my_app.py ``` -The `run` sub-command assumes you have an `App` instance called `app` in the global scope of your Python file. If the application is called something different, you can specify it with a colon following the filename: +The `run` sub-command will look to see if you have an `App` instance called `app` in the global scope of your Python file and will use that if it does. If not it will then look to see if there is a class that inherits from `App` and it will create and `run` an instance of that. Finally, you can specify the application instance to run with a colon following the filename: ```bash textual run my_app.py:alternative_app From 25fcf26a30b79a1920094eeecf2ead3f8ff551f8 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 15:31:34 +0100 Subject: [PATCH 20/28] Add code markup to the mention of a class names --- docs/guide/devtools.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/devtools.md b/docs/guide/devtools.md index 316471eba..2809446a7 100644 --- a/docs/guide/devtools.md +++ b/docs/guide/devtools.md @@ -103,7 +103,7 @@ 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. This is useful in event handlers. Here's an example: +There's a convenient shortcut to `log` available on the `App` and `Widget` objects. This is useful in event handlers. Here's an example: ```python from textual.app import App From e736611b55f5deedfe1f77ebddf95c846cbc28fd Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 15:34:10 +0100 Subject: [PATCH 21/28] Remove trailing whitespace --- docs/guide/app.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide/app.md b/docs/guide/app.md index 1689895ef..4cdc653e6 100644 --- a/docs/guide/app.md +++ b/docs/guide/app.md @@ -76,7 +76,7 @@ Textual knows to *await* your event handlers if they are coroutines (i.e. prefix !!! tip For a friendly introduction to async programming in Python, see FastAPI's [concurrent burgers](https://fastapi.tiangolo.com/async/) article. - + ## Widgets @@ -172,7 +172,7 @@ When `"question02.py"` runs it will load `"question02.css"` and update the app a ### Classvar CSS -While external CSS files are recommended for most applications, and enable some cool features like *live editing*, you can also specify the CSS directly within the Python code. +While external CSS files are recommended for most applications, and enable some cool features like *live editing*, you can also specify the CSS directly within the Python code. To do this set a `CSS` class variable on the app to a string containing your CSS. From da49d19cb70cc9596254aa94eeaa7db6987d6acb Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 15:40:03 +0100 Subject: [PATCH 22/28] s/fledge/fledged/ --- docs/guide/app.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/app.md b/docs/guide/app.md index 4cdc653e6..f1585a5a3 100644 --- a/docs/guide/app.md +++ b/docs/guide/app.md @@ -82,7 +82,7 @@ Textual knows to *await* your event handlers if they are coroutines (i.e. prefix Widgets are self-contained components responsible for generating the output for a portion of the screen. Widgets 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). +Widgets can be as simple as a piece of text, a button, or a fully-fledged component like a text editor or file browser (which may contain widgets of their own). ### Composing From 4569a77d54ee5de50871528a786b9ea2140b1086 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 15:40:55 +0100 Subject: [PATCH 23/28] Add code markup to the mention of a class name --- docs/guide/app.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/app.md b/docs/guide/app.md index f1585a5a3..1e8b5bff7 100644 --- a/docs/guide/app.md +++ b/docs/guide/app.md @@ -86,7 +86,7 @@ Widgets can be as simple as a piece of text, a button, or a fully-fledged compon ### Composing -To add widgets to your app implement a [`compose()`][textual.app.App.compose] method which should return an iterable of Widget instances. A list would work, but it is convenient to yield widgets, making the method a *generator*. +To add widgets to your app implement a [`compose()`][textual.app.App.compose] method which should return an 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 `App.compose()`. From ffbccd5d5d107f123b8910d087388a721668e1a1 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 15:42:01 +0100 Subject: [PATCH 24/28] Add code markup to the mention of class names --- docs/guide/app.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/guide/app.md b/docs/guide/app.md index 1e8b5bff7..3ed44fae2 100644 --- a/docs/guide/app.md +++ b/docs/guide/app.md @@ -88,18 +88,18 @@ Widgets can be as simple as a piece of text, a button, or a fully-fledged compon To add widgets to your app implement a [`compose()`][textual.app.App.compose] method which should return an 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 `App.compose()`. +The following example imports a builtin `Welcome` widget and yields it from `App.compose()`. ```python title="widgets01.py" --8<-- "docs/examples/app/widgets01.py" ``` -When you run this code, Textual will *mount* the Welcome widget which contains Markdown content and a button: +When you run this code, Textual will *mount* the `Welcome` widget which contains Markdown content 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 sent by a button contained in the Welcome widget. The handler calls [App.exit()][textual.app.App.exit] to exit the app. +Notice the `on_button_pressed` method which handles the [Button.Pressed][textual.widgets.Button] event sent by a button contained in the `Welcome` widget. The handler calls [App.exit()][textual.app.App.exit] to exit the app. ### Mounting From c4a43c3f527a61e78c4cff2c32e6e171ff610fd3 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 15:44:46 +0100 Subject: [PATCH 25/28] s/Mypy/mypy/ --- docs/guide/app.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/app.md b/docs/guide/app.md index 3ed44fae2..9cea67855 100644 --- a/docs/guide/app.md +++ b/docs/guide/app.md @@ -141,7 +141,7 @@ You may have noticed that we subclassed `App[str]` rather than the usual `App`. --8<-- "docs/examples/app/question01.py" ``` -The addition of `[str]` tells Mypy that `run()` is expected to return a string. It may also return `None` if [App.exit()][textual.app.App.exit] is called without a return value, so the return type of `run` will be `str | None`. Replace the `str` in `[str]` with the type of the value you intend to call the exit method with. +The addition of `[str]` tells mypy that `run()` is expected to return a string. It may also return `None` if [App.exit()][textual.app.App.exit] is called without a return value, so the return type of `run` will be `str | None`. Replace the `str` in `[str]` with the type of the value you intend to call the exit method with. !!! note From 7182c7981419bbf0712440dbef9b298722f218c1 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 15:46:17 +0100 Subject: [PATCH 26/28] Lets to let's --- docs/guide/app.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/app.md b/docs/guide/app.md index 9cea67855..14e5c79cb 100644 --- a/docs/guide/app.md +++ b/docs/guide/app.md @@ -151,7 +151,7 @@ The addition of `[str]` tells mypy that `run()` is expected to return a string. Textual apps can reference [CSS](CSS.md) files which define how your app and widgets will look, while keeping your Python code free of display related code (which tends to be messy). -The chapter on [Textual CSS](CSS.md) describes how to use CSS in detail. For now lets look at how your app references external CSS files. +The chapter on [Textual CSS](CSS.md) describes how to use CSS in detail. For now let's look at how your app references external CSS files. The following example enables loading of CSS by adding a `CSS_PATH` class variable: From 31a1ebbe8fd1b17d32b10ea2c2f124d6d3981430 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Thu, 13 Oct 2022 16:36:30 +0100 Subject: [PATCH 27/28] s/you/your/ --- docs/guide/app.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/app.md b/docs/guide/app.md index 14e5c79cb..3f8b3dd46 100644 --- a/docs/guide/app.md +++ b/docs/guide/app.md @@ -184,4 +184,4 @@ Here's the question app with classvar CSS: ## What's next -In the following chapter we will learn more about how to apply styles to you widgets and app. +In the following chapter we will learn more about how to apply styles to your widgets and app. From 248b12c23458387cd81d9c9296458e1ac820b1d1 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Fri, 14 Oct 2022 18:55:13 +0100 Subject: [PATCH 28/28] Update docs/guide/devtools.md Co-authored-by: Will McGugan --- docs/guide/devtools.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/guide/devtools.md b/docs/guide/devtools.md index 2809446a7..887315ebc 100644 --- a/docs/guide/devtools.md +++ b/docs/guide/devtools.md @@ -23,7 +23,9 @@ You can run Textual apps with the `run` subcommand. If you supply a path to a Py textual run my_app.py ``` -The `run` sub-command will look to see if you have an `App` instance called `app` in the global scope of your Python file and will use that if it does. If not it will then look to see if there is a class that inherits from `App` and it will create and `run` an instance of that. Finally, you can specify the application instance to run with a colon following the filename: +The `run` sub-command will first look for a `App` instance called `app` in the global scope of your Python file. If there is no `app`, it will create an instance of the first `App` class it finds and run that. + +Alternatively, you can add the name of an `App` instance or class after a colon to run a specific app in the Python file. Here's an example: ```bash textual run my_app.py:alternative_app