merge
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
Screen {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#calculator {
|
||||
layout: grid;
|
||||
grid-size: 4;
|
||||
grid-gutter: 1 2;
|
||||
grid-columns: 1fr;
|
||||
grid-rows: 2fr 1fr 1fr 1fr 1fr 1fr;
|
||||
margin: 1 2;
|
||||
min-height: 25;
|
||||
min-width: 26;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
Button {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#numbers {
|
||||
column-span: 4;
|
||||
content-align: right middle;
|
||||
padding: 0 1;
|
||||
height: 100%;
|
||||
background: $primary-lighten-2;
|
||||
color: $text;
|
||||
}
|
||||
|
||||
#number-0 {
|
||||
column-span: 2;
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
from contextlib import suppress
|
||||
from decimal import Decimal
|
||||
from typing import ClassVar
|
||||
|
||||
from textual import events
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import Container
|
||||
from textual.css.query import NoMatches
|
||||
from textual.reactive import var
|
||||
from textual.widgets import Button, Static
|
||||
|
||||
|
||||
class CalculatorApp(App):
|
||||
"""A working 'desktop' calculator."""
|
||||
|
||||
CSS_PATH = "calculator.css"
|
||||
|
||||
numbers = var("0")
|
||||
show_ac = var(True)
|
||||
left = var(Decimal("0"))
|
||||
right = var(Decimal("0"))
|
||||
value = var("")
|
||||
operator = var("plus")
|
||||
|
||||
NAME_MAP: ClassVar = {
|
||||
"asterisk": "multiply",
|
||||
"slash": "divide",
|
||||
"underscore": "plus-minus",
|
||||
"full_stop": "point",
|
||||
"plus_minus_sign": "plus-minus",
|
||||
"percent_sign": "percent",
|
||||
"equals_sign": "equals",
|
||||
"minus": "minus",
|
||||
"plus": "plus",
|
||||
}
|
||||
|
||||
def watch_numbers(self, value: str) -> None:
|
||||
"""Called when numbers is updated."""
|
||||
# Update the Numbers widget
|
||||
self.query_one("#numbers", Static).update(value)
|
||||
|
||||
def compute_show_ac(self) -> bool:
|
||||
"""Compute switch to show AC or C button"""
|
||||
return self.value in ("", "0") and self.numbers == "0"
|
||||
|
||||
def watch_show_ac(self, show_ac: bool) -> None:
|
||||
"""Called when show_ac changes."""
|
||||
self.query_one("#c").display = not show_ac
|
||||
self.query_one("#ac").display = show_ac
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
"""Add our buttons."""
|
||||
with Container(id="calculator"):
|
||||
yield Static(id="numbers")
|
||||
yield Button("AC", id="ac", variant="primary")
|
||||
yield Button("C", id="c", variant="primary")
|
||||
yield Button("+/-", id="plus-minus", variant="primary")
|
||||
yield Button("%", id="percent", variant="primary")
|
||||
yield Button("÷", id="divide", variant="warning")
|
||||
yield Button("7", id="number-7")
|
||||
yield Button("8", id="number-8")
|
||||
yield Button("9", id="number-9")
|
||||
yield Button("x", id="multiply", variant="warning")
|
||||
yield Button("4", id="number-4")
|
||||
yield Button("5", id="number-5")
|
||||
yield Button("6", id="number-6")
|
||||
yield Button("-", id="minus", variant="warning")
|
||||
yield Button("1", id="number-1")
|
||||
yield Button("2", id="number-2")
|
||||
yield Button("3", id="number-3")
|
||||
yield Button("+", id="plus", variant="warning")
|
||||
yield Button("0", id="number-0")
|
||||
yield Button(".", id="point")
|
||||
yield Button("=", id="equals", variant="warning")
|
||||
|
||||
def on_key(self, event: events.Key) -> None:
|
||||
"""Called when the user presses a key."""
|
||||
|
||||
def press(button_id: str) -> None:
|
||||
with suppress(NoMatches):
|
||||
self.query_one(f"#{button_id}", Button).press()
|
||||
|
||||
key = event.key
|
||||
if key.isdecimal():
|
||||
press(f"number-{key}")
|
||||
elif key == "c":
|
||||
press("c")
|
||||
press("ac")
|
||||
else:
|
||||
button_id = self.NAME_MAP.get(key)
|
||||
if button_id is not None:
|
||||
press(self.NAME_MAP.get(key, key))
|
||||
|
||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||
"""Called when a button is pressed."""
|
||||
|
||||
button_id = event.button.id
|
||||
assert button_id is not None
|
||||
|
||||
def do_math() -> None:
|
||||
"""Does the math: LEFT OPERATOR RIGHT"""
|
||||
try:
|
||||
if self.operator == "plus":
|
||||
self.left += self.right
|
||||
elif self.operator == "minus":
|
||||
self.left -= self.right
|
||||
elif self.operator == "divide":
|
||||
self.left /= self.right
|
||||
elif self.operator == "multiply":
|
||||
self.left *= self.right
|
||||
self.numbers = str(self.left)
|
||||
self.value = ""
|
||||
except Exception:
|
||||
self.numbers = "Error"
|
||||
|
||||
if button_id.startswith("number-"):
|
||||
number = button_id.partition("-")[-1]
|
||||
self.numbers = self.value = self.value.lstrip("0") + number
|
||||
elif button_id == "plus-minus":
|
||||
self.numbers = self.value = str(Decimal(self.value or "0") * -1)
|
||||
elif button_id == "percent":
|
||||
self.numbers = self.value = str(Decimal(self.value or "0") / Decimal(100))
|
||||
elif button_id == "point":
|
||||
if "." not in self.value:
|
||||
self.numbers = self.value = (self.value or "0") + "."
|
||||
elif button_id == "ac":
|
||||
self.value = ""
|
||||
self.left = self.right = Decimal(0)
|
||||
self.operator = "plus"
|
||||
self.numbers = "0"
|
||||
elif button_id == "c":
|
||||
self.value = ""
|
||||
self.numbers = "0"
|
||||
elif button_id in ("plus", "minus", "divide", "multiply"):
|
||||
self.right = Decimal(self.value or "0")
|
||||
do_math()
|
||||
self.operator = button_id
|
||||
elif button_id == "equals":
|
||||
if self.value:
|
||||
self.right = Decimal(self.value)
|
||||
do_math()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
CalculatorApp().run()
|
||||
@@ -0,0 +1,74 @@
|
||||
import io
|
||||
from pathlib import Path
|
||||
|
||||
from textual import on
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.events import DeliveryComplete
|
||||
from textual.widgets import Button, Input, Label
|
||||
|
||||
|
||||
class ScreenshotApp(App[None]):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Button("screenshot: no filename or mime", id="button-1")
|
||||
yield Button("screenshot: screenshot.svg / open in browser", id="button-2")
|
||||
yield Button("screenshot: screenshot.svg / download", id="button-3")
|
||||
yield Button(
|
||||
"screenshot: screenshot.svg / open in browser / plaintext mime",
|
||||
id="button-4",
|
||||
)
|
||||
yield Label("Deliver custom file:")
|
||||
yield Input(id="custom-path-input", placeholder="Path to file...")
|
||||
|
||||
@on(Button.Pressed, selector="#button-1")
|
||||
def on_button_pressed(self) -> None:
|
||||
screenshot_string = self.export_screenshot()
|
||||
string_io = io.StringIO(screenshot_string)
|
||||
self.deliver_text(string_io)
|
||||
|
||||
@on(Button.Pressed, selector="#button-2")
|
||||
def on_button_pressed_2(self) -> None:
|
||||
screenshot_string = self.export_screenshot()
|
||||
string_io = io.StringIO(screenshot_string)
|
||||
self.deliver_text(
|
||||
string_io, save_filename="screenshot.svg", open_method="browser"
|
||||
)
|
||||
|
||||
@on(Button.Pressed, selector="#button-3")
|
||||
def on_button_pressed_3(self) -> None:
|
||||
screenshot_string = self.export_screenshot()
|
||||
string_io = io.StringIO(screenshot_string)
|
||||
self.deliver_text(
|
||||
string_io, save_filename="screenshot.svg", open_method="download"
|
||||
)
|
||||
|
||||
@on(Button.Pressed, selector="#button-4")
|
||||
def on_button_pressed_4(self) -> None:
|
||||
screenshot_string = self.export_screenshot()
|
||||
string_io = io.StringIO(screenshot_string)
|
||||
self.deliver_text(
|
||||
string_io,
|
||||
save_filename="screenshot.svg",
|
||||
open_method="browser",
|
||||
mime_type="text/plain",
|
||||
)
|
||||
|
||||
@on(DeliveryComplete)
|
||||
def on_delivery_complete(self, event: DeliveryComplete) -> None:
|
||||
self.notify(title="Download complete", message=event.key)
|
||||
|
||||
@on(Input.Submitted)
|
||||
def on_input_submitted(self, event: Input.Submitted) -> None:
|
||||
path = Path(event.value)
|
||||
if path.exists():
|
||||
self.deliver_binary(path)
|
||||
else:
|
||||
self.notify(
|
||||
title="Invalid path",
|
||||
message="The path does not exist.",
|
||||
severity="error",
|
||||
)
|
||||
|
||||
|
||||
app = ScreenshotApp()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
@@ -0,0 +1,13 @@
|
||||
import os
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import Pretty
|
||||
|
||||
|
||||
class TerminalEnv(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Pretty(dict(os.environ))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
TerminalEnv().run()
|
||||
@@ -0,0 +1,47 @@
|
||||
[account]
|
||||
|
||||
[app.Calculator]
|
||||
path = "./"
|
||||
command = "python calculator.py"
|
||||
|
||||
|
||||
[app.Easing]
|
||||
slug = "easing"
|
||||
path = "./"
|
||||
command = "textual easing"
|
||||
|
||||
|
||||
[app.Keys]
|
||||
slug = "keys"
|
||||
path = "./"
|
||||
command = "textual keys"
|
||||
|
||||
|
||||
[app.Borders]
|
||||
slug = "borders"
|
||||
path = "./"
|
||||
command = "textual borders"
|
||||
|
||||
|
||||
[app.Demo]
|
||||
name = "Demo"
|
||||
slug = "demo"
|
||||
path = "./"
|
||||
command = "python -m textual"
|
||||
|
||||
[terminal.Terminal]
|
||||
name = "Terminal"
|
||||
path = "./"
|
||||
terminal = true
|
||||
|
||||
[app.OpenLink]
|
||||
name = "Open Link"
|
||||
slug = "open-link"
|
||||
path = "./"
|
||||
command = "python open_link.py"
|
||||
|
||||
[app.Download]
|
||||
name = "Download"
|
||||
slug = "download"
|
||||
path = "./"
|
||||
command = "python download.py"
|
||||
@@ -0,0 +1,24 @@
|
||||
from textual import on
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import Button
|
||||
|
||||
|
||||
class OpenLink(App[None]):
|
||||
"""Demonstrates opening a URL in the same tab or a new tab."""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Button("Visit the Textual docs", id="open-link-same-tab")
|
||||
yield Button("Visit the Textual docs in a new tab", id="open-link-new-tab")
|
||||
|
||||
@on(Button.Pressed)
|
||||
def open_link(self, event: Button.Pressed) -> None:
|
||||
"""Open the URL in the same tab or a new tab depending on which button was pressed."""
|
||||
self.open_url(
|
||||
"https://textual.textualize.io",
|
||||
new_tab=event.button.id == "open-link-new-tab",
|
||||
)
|
||||
|
||||
|
||||
app = OpenLink()
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
Reference in New Issue
Block a user