import sys from PySide6.QtCore import Qt from PySide6.QtGui import QFont from PySide6.QtWidgets import ( QApplication, QFrame, QHBoxLayout, QLabel, QLineEdit, QListWidget, QMainWindow, QPushButton, QSplitter, QVBoxLayout, QWidget, ) from abacus_core import AbacusCore STYLE = """ QMainWindow, QWidget { background: #111318; color: #e7e9ee; font-family: "Segoe UI"; font-size: 13px; } QFrame#topbar { background: #171a21; border-bottom: 1px solid #292e38; } QLabel#title { font-size: 20px; font-weight: 700; color: #f5f7fb; } QLabel#subtitle { color: #858c99; } QFrame#panel { background: #171a21; border: 1px solid #292e38; border-radius: 12px; } QLabel#panelTitle { color: #9da5b3; font-size: 12px; font-weight: 700; } QLineEdit#input { background: #0d0f13; border: 1px solid #343a46; border-radius: 10px; padding: 12px 14px; color: #ffffff; font-size: 16px; selection-background-color: #3b82f6; } QLineEdit#input:focus { border: 1px solid #4b8cff; } QPushButton { background: #2864d7; border: none; border-radius: 9px; padding: 11px 18px; color: white; font-weight: 700; } QPushButton:hover { background: #3475ef; } QPushButton:pressed { background: #1f55ba; } QPushButton#secondary { background: #242933; color: #cbd1db; } QPushButton#secondary:hover { background: #2d333f; } QListWidget { background: transparent; border: none; outline: none; padding: 4px; } QListWidget::item { padding: 8px 7px; border-radius: 7px; } QListWidget::item:hover { background: #20242d; } QListWidget::item:selected { background: #202a3d; color: #ffffff; } QLabel#result { color: #66d9a8; font-size: 28px; font-weight: 700; } QLabel#status { color: #7e8795; } QSplitter::handle { background: #111318; width: 8px; } """ class AbacusWindow(QMainWindow): def __init__(self): super().__init__() self.abacus = AbacusCore() self.step = 0 self.setWindowTitle("Abacus") self.resize(1100, 720) self.setMinimumSize(850, 560) self._build_ui() self._update_lists() def _build_ui(self): root = QWidget() root_layout = QVBoxLayout(root) root_layout.setContentsMargins(18, 14, 18, 18) root_layout.setSpacing(14) # Header topbar = QFrame() topbar.setObjectName("topbar") top_layout = QHBoxLayout(topbar) top_layout.setContentsMargins(4, 4, 4, 12) title_box = QVBoxLayout() title_box.setSpacing(1) title = QLabel("Abacus") title.setObjectName("title") subtitle = QLabel("Expression calculator") subtitle.setObjectName("subtitle") title_box.addWidget(title) title_box.addWidget(subtitle) top_layout.addLayout(title_box) top_layout.addStretch() self.status = QLabel("Ready") self.status.setObjectName("status") top_layout.addWidget(self.status) root_layout.addWidget(topbar) splitter = QSplitter(Qt.Horizontal) splitter.setChildrenCollapsible(False) # Left: history history_panel = self._make_panel("History") history_layout = history_panel.layout() self.history = QListWidget() self.history.setAlternatingRowColors(False) history_layout.addWidget(self.history) clear_btn = QPushButton("Clear history") clear_btn.setObjectName("secondary") clear_btn.clicked.connect(self.history.clear) history_layout.addWidget(clear_btn) splitter.addWidget(history_panel) # Center center = QWidget() center_layout = QVBoxLayout(center) center_layout.setContentsMargins(0, 0, 0, 0) center_layout.setSpacing(14) calc_panel = self._make_panel("Calculator") calc_layout = calc_panel.layout() self.result = QLabel("0") self.result.setObjectName("result") self.result.setAlignment(Qt.AlignRight | Qt.AlignVCenter) calc_layout.addWidget(self.result) self.input = QLineEdit() self.input.setObjectName("input") self.input.setPlaceholderText("Enter expression, e.g. 2 + 3 * alpha") self.input.returnPressed.connect(self.calculate) calc_layout.addWidget(self.input) buttons = QHBoxLayout() buttons.setSpacing(8) enter = QPushButton("Calculate") enter.clicked.connect(self.calculate) clear = QPushButton("Clear") clear.setObjectName("secondary") clear.clicked.connect(self._clear_input) buttons.addWidget(enter) buttons.addWidget(clear) calc_layout.addLayout(buttons) center_layout.addWidget(calc_panel) help_panel = self._make_panel("Quick reference") help_layout = help_panel.layout() help_text = QLabel( "Variable: alpha = 10\n" "Function: double: alpha * 2\n" "Comment: 2 + 3 # test\n" "Commands from the original console are available through the UI." ) help_text.setStyleSheet("color: #9da5b3; line-height: 1.5;") help_text.setWordWrap(True) help_layout.addWidget(help_text) center_layout.addWidget(help_panel) center_layout.addStretch() splitter.addWidget(center) # Right: variables/functions right = QWidget() right_layout = QVBoxLayout(right) right_layout.setContentsMargins(0, 0, 0, 0) right_layout.setSpacing(14) var_panel = self._make_panel("Variables") var_panel.layout().addWidget(QLabel("Stored values")) self.variables = QListWidget() var_panel.layout().addWidget(self.variables) right_layout.addWidget(var_panel, 1) func_panel = self._make_panel("Functions") func_panel.layout().addWidget(QLabel("Definitions")) self.functions = QListWidget() func_panel.layout().addWidget(self.functions) right_layout.addWidget(func_panel, 1) splitter.addWidget(right) splitter.setSizes([260, 500, 280]) root_layout.addWidget(splitter, 1) self.setCentralWidget(root) def _make_panel(self, title): panel = QFrame() panel.setObjectName("panel") layout = QVBoxLayout(panel) layout.setContentsMargins(14, 13, 14, 14) layout.setSpacing(9) label = QLabel(title) label.setObjectName("panelTitle") layout.addWidget(label) return panel def calculate(self): text = self.input.text().strip() if not text: return self.step += 1 try: result = self.abacus.parse_input(text) except Exception as exc: self.status.setText(f"Error: {exc}") self.result.setText("Error") return if not result: self.input.clear() self.status.setText("Comment / empty input") return original, _, value, comment = result value_text = str(value) line = f"{self.step}. {original} = {value_text}" if comment: line += f" #{comment}" self.history.addItem(line) self.history.scrollToBottom() self.result.setText(value_text) self.input.setText(value_text) self.status.setText("Calculated") self._update_lists() def _clear_input(self): self.input.clear() self.result.setText("0") self.status.setText("Ready") self.input.setFocus() def _update_lists(self): self.variables.clear() for key, value in self.abacus.get_vars().items(): self.variables.addItem(f"{key} = {value}") self.functions.clear() for key, value in self.abacus.get_funcs().items(): self.functions.addItem(f"{key} = {value}") def main(): app = QApplication(sys.argv) app.setStyle("Fusion") app.setFont(QFont("Segoe UI", 10)) app.setStyleSheet(STYLE) window = AbacusWindow() window.show() sys.exit(app.exec()) if __name__ == "__main__": main()