From e5c54a36832b69e30da01f66fcfe629ec3c3b8c9 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 3 May 2023 13:22:22 +0100 Subject: [PATCH] blog post (#2465) * blog posts * fix typo * word * version bump * changelog * update words --- CHANGELOG.md | 3 +- docs/blog/images/frogmouth.svg | 268 +++++++++++++++++++++++++ docs/blog/posts/release0-23-0.md | 88 ++++++++ docs/examples/events/on_decorator01.py | 2 +- docs/guide/events.md | 2 + pyproject.toml | 2 +- 6 files changed, 362 insertions(+), 3 deletions(-) create mode 100644 docs/blog/images/frogmouth.svg create mode 100644 docs/blog/posts/release0-23-0.md diff --git a/CHANGELOG.md b/CHANGELOG.md index af9aa228ca..c4acf4a763 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## Unreleased +## [0.23.0] - 2023-05-03 ### Fixed @@ -873,6 +873,7 @@ https://textual.textualize.io/blog/2022/11/08/version-040/#version-040 - New handler system for messages that doesn't require inheritance - Improved traceback handling +[0.23.0]: https://github.com/Textualize/textual/compare/v0.22.3...v0.23.0 [0.22.3]: https://github.com/Textualize/textual/compare/v0.22.2...v0.22.3 [0.22.2]: https://github.com/Textualize/textual/compare/v0.22.1...v0.22.2 [0.22.1]: https://github.com/Textualize/textual/compare/v0.22.0...v0.22.1 diff --git a/docs/blog/images/frogmouth.svg b/docs/blog/images/frogmouth.svg new file mode 100644 index 0000000000..685064d260 --- /dev/null +++ b/docs/blog/images/frogmouth.svg @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Frogmouth + + + + + + + + + + +/Users/willmcgugan/projects/textual/FAQ.md + + + +ContentsLocalBookmarksHistory▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +How do I pass arguments to an app? +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +▼  Frequently Asked Questions▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +├──  Does Textual support images?When creating your App class, override __init__ as you would wheninheriting normally. For example: +├──  How can I fix ImportError cannot i +├──  How can I select and copy text in  +├──  How can I set a translucent app bafromtextual.appimportApp,ComposeResult +├──  How do I center a widget in a screfromtextual.widgetsimportStatic +├──  How do I pass arguments to an app? +├──  Why do some key combinations neverclassGreetings(App[None]): +├──  Why doesn't Textual look good on m│    +└──  Why doesn't Textual support ANSI t│   def__init__(self,greeting:str="Hello",to_greet:str="World")->None: +│   │   self.greeting=greeting +│   │   self.to_greet=to_greet +│   │   super().__init__() +│    +│   defcompose(self)->ComposeResult: +│   │   yieldStatic(f"{self.greeting}{self.to_greet}") + + +Then the app can be run, passing in various arguments; for example: +▅▅ + +# Running with default arguments. +Greetings().run() + +# Running with a keyword arguyment. +Greetings(to_greet="davep").run()▅▅ + +# Running with both positional arguments. +Greetings("Well hello","there").run() + + + + +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + F1  Help  F2  About  CTRL+N  Navigation  CTRL+Q  Quit  + + + diff --git a/docs/blog/posts/release0-23-0.md b/docs/blog/posts/release0-23-0.md new file mode 100644 index 0000000000..8fbe14573a --- /dev/null +++ b/docs/blog/posts/release0-23-0.md @@ -0,0 +1,88 @@ +--- +draft: false +date: 2023-05-03 +categories: + - Release +title: "Textual 0.23.0 improves message handling" +authors: + - willmcgugan +--- + +# Textual 0.23.0 improves message handling + +It's been a busy couple of weeks at Textualize. +We've been building apps with [Textual](https://github.com/Textualize/textual), as part of our *dog-fooding* week. +The first app, [Frogmouth](https://github.com/Textualize/frogmouth), was released at the weekend and already has 1K GitHub stars! +Expect two more such apps this month. + + + +
+--8<-- "docs/blog/images/frogmouth.svg" +
+ +!!! tip + + Join our [mailing list](http://eepurl.com/hL0BF1) if you would like to be the first to hear about our apps. + +We haven't stopped developing Textual in that time. +Today we released version 0.23.0 which has a really interesting API update I'd like to introduce. + +Textual *widgets* can send messages to each other. +To respond to those messages, you implement a message handler with a naming convention. +For instance, the [Button](/widget_gallery/#button) widget sends a `Pressed` event. +To handle that event, you implement a method called `on_button_pressed`. + +Simple enough, but handler methods are called to handle pressed events from *all* Buttons. +To manage multiple buttons you typically had to write a large `if` statement to wire up each button to the code it should run. +It didn't take many Buttons before the handler became hard to follow. + +## On decorator + +Version 0.23.0 introduces the [`@on`](/guide/events/#on-decorator) decorator which allows you to dispatch events based on the widget that initiated them. + +This is probably best explained in code. +The following two listings respond to buttons being pressed. +The first uses a single message handler, the second uses the decorator approach: + +=== "on_decorator01.py" + + ```python title="on_decorator01.py" + --8<-- "docs/examples/events/on_decorator01.py" + ``` + + 1. The message handler is called when any button is pressed + +=== "on_decorator02.py" + + ```python title="on_decorator02.py" + --8<-- "docs/examples/events/on_decorator02.py" + ``` + + 1. Matches the button with an id of "bell" (note the `#` to match the id) + 2. Matches the button with class names "toggle" *and* "dark" + 3. Matches the button with an id of "quit" + +=== "Output" + + ```{.textual path="docs/examples/events/on_decorator01.py"} + ``` + +The decorator dispatches events based on a CSS selector. +This means that you could have a handler per button, or a handler for buttons with a shared class, or parent. + +We think this is a very flexible mechanism that will help keep code readable and maintainable. + +## Why didn't we do this earlier? + +It's a reasonable question to ask: why didn't we implement this in an earlier version? +We were certainly aware there was a deficiency in the API. + +The truth is simply that we didn't have an elegant solution in mind until recently. +The `@on` decorator is, I believe, an elegant and powerful mechanism for dispatching handlers. +It might seem obvious in hindsight, but it took many iterations and brainstorming in the office to come up with it! + + +## Join us + +If you want to talk about this update or anything else Textual related, join us on our [Discord server](https://discord.gg/Enf6Z3qhVr). diff --git a/docs/examples/events/on_decorator01.py b/docs/examples/events/on_decorator01.py index 3b97f7f0d7..7b9c0276e2 100644 --- a/docs/examples/events/on_decorator01.py +++ b/docs/examples/events/on_decorator01.py @@ -12,7 +12,7 @@ def compose(self) -> ComposeResult: yield Button("Toggle dark", classes="toggle dark") yield Button("Quit", id="quit") - def on_button_pressed(self, event: Button.Pressed) -> None: + def on_button_pressed(self, event: Button.Pressed) -> None: # (1)! """Handle all button pressed events.""" if event.button.id == "bell": self.bell() diff --git a/docs/guide/events.md b/docs/guide/events.md index 1e6136c8f2..0629e17e5e 100644 --- a/docs/guide/events.md +++ b/docs/guide/events.md @@ -181,6 +181,8 @@ In the following example we have three buttons, each of which does something dif --8<-- "docs/examples/events/on_decorator01.py" ``` + 1. The message handler is called when any button is pressed + === "Output" ```{.textual path="docs/examples/events/on_decorator01.py"} diff --git a/pyproject.toml b/pyproject.toml index e1b4cbee6d..ba155e9930 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "textual" -version = "0.22.3" +version = "0.23.0" homepage = "https://github.com/Textualize/textual" description = "Modern Text User Interface framework" authors = ["Will McGugan "]