Files
textual/docs/examples/guide/compound/byte03.py
Will McGugan 864931e94b Compound docs (#1952)
* compound example

* update bit switch

* prevent

* no css

* compound widget example

* more diagrams

* more diagrams

* diagrams

* words

* words

* remove sender

* removed priority post

* timer fix

* test fixes

* drop async version of post_message

* extended docs

* fix no app

* Added control properties

* changelog

* changelog

* changelog

* fix for stopping timers

* changelog

* docs update

* last byte example

* new section

* update of byte03

* updae to docs

* Added compound examples

* Rewording

* Use set sender

* don't need this

* hyphens

* Update docs/guide/widgets.md

Co-authored-by: Dave Pearson <davep@davep.org>

* Update docs/guide/widgets.md

Co-authored-by: Dave Pearson <davep@davep.org>

* Update docs/guide/widgets.md

Co-authored-by: Dave Pearson <davep@davep.org>

* Update docs/guide/widgets.md

Co-authored-by: Dave Pearson <davep@davep.org>

* Update docs/guide/widgets.md

Co-authored-by: Dave Pearson <davep@davep.org>

* Update docs/guide/widgets.md

Co-authored-by: Dave Pearson <davep@davep.org>

* Update docs/guide/widgets.md

Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>

* Update docs/guide/widgets.md

Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>

* Update docs/guide/widgets.md

Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>

* Update docs/guide/widgets.md

Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>

* parenthesis

* stack diagram

---------

Co-authored-by: Dave Pearson <davep@davep.org>
Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>
2023-03-06 16:56:24 +00:00

131 lines
3.4 KiB
Python

from __future__ import annotations
from textual.app import App, ComposeResult
from textual.containers import Container
from textual.geometry import clamp
from textual.messages import Message
from textual.reactive import reactive
from textual.widget import Widget
from textual.widgets import Input, Label, Switch
class BitSwitch(Widget):
"""A Switch with a numeric label above it."""
DEFAULT_CSS = """
BitSwitch {
layout: vertical;
width: auto;
height: auto;
}
BitSwitch > Label {
text-align: center;
width: 1fr;
}
"""
class BitChanged(Message):
"""Sent when the 'bit' changes."""
def __init__(self, bit: int, value: bool) -> None:
super().__init__()
self.bit = bit
self.value = value
value = reactive(0)
def __init__(self, bit: int) -> None:
self.bit = bit
super().__init__()
def compose(self) -> ComposeResult:
yield Label(str(self.bit))
yield Switch()
def watch_value(self, value: bool) -> None: # (1)!
"""When the value changes we want to set the switch accordingly."""
self.query_one(Switch).value = value
def on_switch_changed(self, event: Switch.Changed) -> None:
"""When the switch changes, notify the parent via a message."""
event.stop()
self.value = event.value
self.post_message(self.BitChanged(self.bit, event.value))
class ByteInput(Widget):
"""A compound widget with 8 switches."""
DEFAULT_CSS = """
ByteInput {
width: auto;
height: auto;
border: blank;
layout: horizontal;
}
ByteInput:focus-within {
border: heavy $secondary;
}
"""
def compose(self) -> ComposeResult:
for bit in reversed(range(8)):
yield BitSwitch(bit)
class ByteEditor(Widget):
DEFAULT_CSS = """
ByteEditor > Container {
height: 1fr;
align: center middle;
}
ByteEditor > Container.top {
background: $boost;
}
ByteEditor Input {
width: 16;
}
"""
value = reactive(0)
def validate_value(self, value: int) -> int: # (2)!
"""Ensure value is between 0 and 255."""
return clamp(value, 0, 255)
def compose(self) -> ComposeResult:
with Container(classes="top"):
yield Input(placeholder="byte")
with Container():
yield ByteInput()
def on_bit_switch_bit_changed(self, event: BitSwitch.BitChanged) -> None:
"""When a switch changes, update the value."""
value = 0
for switch in self.query(BitSwitch):
value |= switch.value << switch.bit
self.query_one(Input).value = str(value)
def on_input_changed(self, event: Input.Changed) -> None: # (3)!
"""When the text changes, set the value of the byte."""
try:
self.value = int(event.value or "0")
except ValueError:
pass
def watch_value(self, value: int) -> None: # (4)!
"""When self.value changes, update switches."""
for switch in self.query(BitSwitch):
with switch.prevent(BitSwitch.BitChanged): # (5)!
switch.value = bool(value & (1 << switch.bit)) # (6)!
class ByteInputApp(App):
def compose(self) -> ComposeResult:
yield ByteEditor()
if __name__ == "__main__":
app = ByteInputApp()
app.run()