mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Worker API (#2182)
* worker class * worker API tests * tidy * Decorator and more tests * type fix * error order * more tests * remove active message * move worker manager to app * cancel nodes * typing fix * revert change * typing fixes and cleanup * revert typing * test fix * cancel group * Added test for worker * comment * workers docs * Added exit_on_error * changelog * svg * refactor test * remove debug tweaks * docstrings * worker test * fix typing in run * fix 3.7 tests * blog post * fix deadlock test * words * words * words * workers docs * blog post * Apply suggestions from code review Co-authored-by: Dave Pearson <davep@davep.org> * docstring * fix and docstring * Apply suggestions from code review Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com> * Update src/textual/widgets/_markdown.py Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com> * Update src/textual/worker.py Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com> * Fix black * docstring * merge * changelog --------- Co-authored-by: Dave Pearson <davep@davep.org> Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>
This commit is contained in:
16
docs/examples/guide/workers/weather.css
Normal file
16
docs/examples/guide/workers/weather.css
Normal file
@@ -0,0 +1,16 @@
|
||||
Input {
|
||||
dock: top;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#weather-container {
|
||||
width: 100%;
|
||||
height: 1fr;
|
||||
align: center middle;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#weather {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
40
docs/examples/guide/workers/weather01.py
Normal file
40
docs/examples/guide/workers/weather01.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import httpx
|
||||
from rich.text import Text
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import VerticalScroll
|
||||
from textual.widgets import Input, Static
|
||||
|
||||
|
||||
class WeatherApp(App):
|
||||
"""App to display the current weather."""
|
||||
|
||||
CSS_PATH = "weather.css"
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Input(placeholder="Enter a City")
|
||||
with VerticalScroll(id="weather-container"):
|
||||
yield Static(id="weather")
|
||||
|
||||
async def on_input_changed(self, message: Input.Changed) -> None:
|
||||
"""Called when the input changes"""
|
||||
await self.update_weather(message.value)
|
||||
|
||||
async def update_weather(self, city: str) -> None:
|
||||
"""Update the weather for the given city."""
|
||||
weather_widget = self.query_one("#weather", Static)
|
||||
if city:
|
||||
# Query the network API
|
||||
url = f"https://wttr.in/{city}"
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(url)
|
||||
weather = Text.from_ansi(response.text)
|
||||
weather_widget.update(weather)
|
||||
else:
|
||||
# No city, so just blank out the weather
|
||||
weather_widget.update("")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = WeatherApp()
|
||||
app.run()
|
||||
40
docs/examples/guide/workers/weather02.py
Normal file
40
docs/examples/guide/workers/weather02.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import httpx
|
||||
from rich.text import Text
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import VerticalScroll
|
||||
from textual.widgets import Input, Static
|
||||
|
||||
|
||||
class WeatherApp(App):
|
||||
"""App to display the current weather."""
|
||||
|
||||
CSS_PATH = "weather.css"
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Input(placeholder="Enter a City")
|
||||
with VerticalScroll(id="weather-container"):
|
||||
yield Static(id="weather")
|
||||
|
||||
async def on_input_changed(self, message: Input.Changed) -> None:
|
||||
"""Called when the input changes"""
|
||||
self.run_worker(self.update_weather(message.value), exclusive=True)
|
||||
|
||||
async def update_weather(self, city: str) -> None:
|
||||
"""Update the weather for the given city."""
|
||||
weather_widget = self.query_one("#weather", Static)
|
||||
if city:
|
||||
# Query the network API
|
||||
url = f"https://wttr.in/{city}"
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(url)
|
||||
weather = Text.from_ansi(response.text)
|
||||
weather_widget.update(weather)
|
||||
else:
|
||||
# No city, so just blank out the weather
|
||||
weather_widget.update("")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = WeatherApp()
|
||||
app.run()
|
||||
42
docs/examples/guide/workers/weather03.py
Normal file
42
docs/examples/guide/workers/weather03.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import httpx
|
||||
from rich.text import Text
|
||||
|
||||
from textual import work
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import VerticalScroll
|
||||
from textual.widgets import Input, Static
|
||||
|
||||
|
||||
class WeatherApp(App):
|
||||
"""App to display the current weather."""
|
||||
|
||||
CSS_PATH = "weather.css"
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Input(placeholder="Enter a City")
|
||||
with VerticalScroll(id="weather-container"):
|
||||
yield Static(id="weather")
|
||||
|
||||
async def on_input_changed(self, message: Input.Changed) -> None:
|
||||
"""Called when the input changes"""
|
||||
self.update_weather(message.value)
|
||||
|
||||
@work(exclusive=True)
|
||||
async def update_weather(self, city: str) -> None:
|
||||
"""Update the weather for the given city."""
|
||||
weather_widget = self.query_one("#weather", Static)
|
||||
if city:
|
||||
# Query the network API
|
||||
url = f"https://wttr.in/{city}"
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(url)
|
||||
weather = Text.from_ansi(response.text)
|
||||
weather_widget.update(weather)
|
||||
else:
|
||||
# No city, so just blank out the weather
|
||||
weather_widget.update("")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = WeatherApp()
|
||||
app.run()
|
||||
47
docs/examples/guide/workers/weather04.py
Normal file
47
docs/examples/guide/workers/weather04.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import httpx
|
||||
from rich.text import Text
|
||||
|
||||
from textual import work
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import VerticalScroll
|
||||
from textual.widgets import Input, Static
|
||||
from textual.worker import Worker
|
||||
|
||||
|
||||
class WeatherApp(App):
|
||||
"""App to display the current weather."""
|
||||
|
||||
CSS_PATH = "weather.css"
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Input(placeholder="Enter a City")
|
||||
with VerticalScroll(id="weather-container"):
|
||||
yield Static(id="weather")
|
||||
|
||||
async def on_input_changed(self, message: Input.Changed) -> None:
|
||||
"""Called when the input changes"""
|
||||
self.update_weather(message.value)
|
||||
|
||||
@work(exclusive=True)
|
||||
async def update_weather(self, city: str) -> None:
|
||||
"""Update the weather for the given city."""
|
||||
weather_widget = self.query_one("#weather", Static)
|
||||
if city:
|
||||
# Query the network API
|
||||
url = f"https://wttr.in/{city}"
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(url)
|
||||
weather = Text.from_ansi(response.text)
|
||||
weather_widget.update(weather)
|
||||
else:
|
||||
# No city, so just blank out the weather
|
||||
weather_widget.update("")
|
||||
|
||||
def on_worker_state_changed(self, event: Worker.StateChanged) -> None:
|
||||
"""Called when the worker state changes."""
|
||||
self.log(event)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = WeatherApp()
|
||||
app.run()
|
||||
52
docs/examples/guide/workers/weather05.py
Normal file
52
docs/examples/guide/workers/weather05.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
from rich.text import Text
|
||||
|
||||
from textual import work
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import VerticalScroll
|
||||
from textual.widgets import Input, Static
|
||||
from textual.worker import Worker, get_current_worker
|
||||
|
||||
|
||||
class WeatherApp(App):
|
||||
"""App to display the current weather."""
|
||||
|
||||
CSS_PATH = "weather.css"
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Input(placeholder="Enter a City")
|
||||
with VerticalScroll(id="weather-container"):
|
||||
yield Static(id="weather")
|
||||
|
||||
async def on_input_changed(self, message: Input.Changed) -> None:
|
||||
"""Called when the input changes"""
|
||||
self.update_weather(message.value)
|
||||
|
||||
@work(exclusive=True)
|
||||
def update_weather(self, city: str) -> None:
|
||||
"""Update the weather for the given city."""
|
||||
weather_widget = self.query_one("#weather", Static)
|
||||
worker = get_current_worker()
|
||||
if city:
|
||||
# Query the network API
|
||||
url = f"https://wttr.in/{city}"
|
||||
request = Request(url)
|
||||
request.add_header("User-agent", "CURL")
|
||||
response_text = urlopen(request).read().decode("utf-8")
|
||||
weather = Text.from_ansi(response_text)
|
||||
if not worker.is_cancelled:
|
||||
self.call_from_thread(weather_widget.update, weather)
|
||||
else:
|
||||
# No city, so just blank out the weather
|
||||
if not worker.is_cancelled:
|
||||
self.call_from_thread(weather_widget.update, "")
|
||||
|
||||
def on_worker_state_changed(self, event: Worker.StateChanged) -> None:
|
||||
"""Called when the worker state changes."""
|
||||
self.log(event)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = WeatherApp()
|
||||
app.run()
|
||||
Reference in New Issue
Block a user