mirror of
				https://github.com/Textualize/textual-web.git
				synced 2025-10-17 02:36:40 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			146 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			146 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from decimal import Decimal
 | ||
| 
 | ||
| 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 = {
 | ||
|         "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("×", 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:
 | ||
|             try:
 | ||
|                 self.query_one(f"#{button_id}", Button).press()
 | ||
|             except NoMatches:
 | ||
|                 pass
 | ||
| 
 | ||
|         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()
 | 
