Metadata-Version: 2.1
Name: pyqt5-math-widget
Version: 1.1.0
Summary: PyQt5 widgets for rendering and editing mathematical formulas
Author: pyqt5-math-widget
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Intended Audience :: Developers
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: PyQt5>=5.15
Provides-Extra: sympy
Requires-Dist: sympy>=1.12; extra == "sympy"

# pyqt5-math-widget

A PyQt5 library for rendering and interactively editing mathematical formulas.
No LaTeX engine required — everything is rendered natively via `QPainter`.

---

## Installation

```bash
pip install pyqt5-math-widget
```
---

## Widgets

| Class | Purpose |
|---|---|
| `MathView` | Read-only formula renderer |
| `MathEditor` | Interactive formula editor with toolbar |
| `MathDialog` | Modal dialog wrapping `MathEditor` |

---

## MathView — read-only renderer

### Basic usage

```python
from pyqt5_math_widget import MathView, FracNode, TextNode, SqrtNode

view = MathView()
view.set_node(FracNode(TextNode("1"), SqrtNode(TextNode("x"))))
layout.addWidget(view)
```

### From a FormulaModel

```python
from pyqt5_math_widget import MathView, FormulaModel

model = FormulaModel()
model.insert_frac()
model.insert_greek("pi")

view = MathView.from_model(model)
```

### API

```python
view.set_node(node)           # Set formula from a FormulaNode
view.set_model(model)         # Attach a FormulaModel
view.set_text("sin(x)")       # Set from plain text
view.set_zoom(1.5)            # Scale factor (default 1.0)
view.zoom()                   # -> float
view.set_background(color)    # QColor fill
view.set_transparent(True)    # Transparent background
view.set_padding(16, 12)      # Horizontal, vertical padding in px
view.set_foreground(color)    # Global text colour
view.to_pixmap(scale=2.0)     # -> QPixmap (for export/saving)
view.to_latex()               # -> str
view.to_sympy()               # -> str
view.to_text()                # -> str
view.model()                  # -> FormulaModel
```

---

## MathEditor — interactive editor

### Basic usage

```python
from pyqt5_math_widget import MathEditor

editor = MathEditor()
editor.formula_changed.connect(lambda s: print("sympy:", s))
editor.latex_changed.connect(lambda s: print("latex:", s))
layout.addWidget(editor)
```

### Constructor parameters

```python
MathEditor(
    parent=None,
    show_toolbar=True,          # Show button toolbar below canvas
    toolbar_groups=None,        # List of group names to show (see below)
    zoom=1.0,                   # Initial zoom level
    readonly=False,             # Disable editing
)
```

### Toolbar groups

Pass a subset of these strings to `toolbar_groups`:

| Group | Contents |
|---|---|
| `"struct"` | fraction, sqrt, power, subscript, integral, sum, product, limit, matrix, ... |
| `"operators"` | +, -, ·, ×, =, ≠, ≤, ≥, ±, ≈, ∂, ∇, ∈, ∀, →, ... |
| `"greek"` | α β γ δ ε θ λ μ ν π ρ σ τ φ ψ ω Σ Π Γ Δ Θ Ω ∞ ... |
| `"functions"` | sin cos tan arcsin log ln exp max min det tr ... |
| `"edit"` | Delete, Clear, ← →, Undo, Redo |

```python
editor.set_toolbar_groups(["struct", "greek", "edit"])
```

### Programmatic insertion API

```python
editor.insert_frac()                   # Fraction a/b
editor.insert_sqrt(nth=False)          # Square root
editor.insert_sqrt(nth=True)           # Nth root
editor.insert_power(wrap_current=True) # Superscript
editor.insert_subscript()              # Subscript
editor.insert_power_sub()              # Combined x^n_m
editor.insert_parens("(", ")")         # Parentheses (any bracket pair)
editor.insert_parens("[", "]")         # Square brackets
editor.insert_abs()                    # |x|
editor.insert_integral(definite=True)  # Definite integral
editor.insert_integral(definite=False) # Indefinite integral
editor.insert_sum(with_limits=True)    # Sum Σ
editor.insert_product(with_limits=True)# Product Π
editor.insert_limit()                  # lim
editor.insert_matrix(2, 2, "(")        # 2x2 matrix with round brackets
editor.insert_matrix(3, 3, "[")        # 3x3 matrix with square brackets
editor.insert_overline()               # Overline
editor.insert_underline()              # Underline
editor.insert_func("sin")             # sin(...) function
editor.insert_greek("alpha")          # α
editor.insert_operator("->")          # →
editor.insert_text("x", italic=True)  # Plain character
editor.insert_node(my_node)           # Raw FormulaNode
```

### Editing control

```python
editor.delete_at_cursor()    # Delete at cursor (Backspace)
editor.clear()               # Clear entire formula
editor.undo()                # Ctrl+Z
editor.redo()                # Ctrl+Y
editor.can_undo()            # -> bool
editor.can_redo()            # -> bool
editor.cursor_left()         # Move cursor left
editor.cursor_right()        # Move cursor right
```

### Output

```python
editor.to_sympy()    # "integrate(sin(x), x)"
editor.to_latex()    # r"\int \sin(x)\,dx"
editor.to_text()     # "integrate(sin(x), x)"
editor.to_pixmap(scale=2.0)   # -> QPixmap
```

### Display control

```python
editor.set_zoom(1.5)
editor.set_readonly(True)
editor.set_toolbar_visible(False)
editor.model()                # -> FormulaModel for direct access
editor.set_model(model)       # Replace model
```

---

## MathDialog — modal dialog

```python
from pyqt5_math_widget import MathDialog
from PyQt5.QtWidgets import QDialog

dlg = MathDialog(
    parent=self,
    title="Insert Formula",
    initial_text="sin(x)",
    toolbar_groups=["struct", "greek", "edit"],
    show_output=True,
    zoom=1.1,
)
if dlg.exec_() == QDialog.Accepted:
    print(dlg.result_sympy())
    print(dlg.result_latex())
    print(dlg.result_text())
```

Connect the signal without blocking:

```python
dlg = MathDialog(parent=self)
dlg.accepted_formula.connect(lambda s: print("inserted:", s))
dlg.show()
```

---

## FormulaModel — programmatic tree manipulation

```python
from pyqt5_math_widget import FormulaModel

model = FormulaModel()
model.insert_frac()
model.insert_text("x")
model.move_right()
model.insert_text("2")

print(model.to_sympy())  # (x) / (2)
print(model.to_latex())  # \frac{x}{2}

model.undo()
model.redo()
model.clear()
```

---

## Formula node tree

Build a formula tree and display it statically:

```python
from pyqt5_math_widget import (
    MathView, SeqNode, TextNode, FracNode, SqrtNode,
    PowerNode, IntegralNode, SumProdNode, MatrixNode,
    ParenNode, AbsNode,
)

root = SeqNode([
    TextNode("E"),
    TextNode("="),
    FracNode(
        TextNode("m"),
        SqrtNode(SeqNode([TextNode("1"), TextNode("-"), PowerNode(TextNode("v"), TextNode("2"))])),
    ),
])

view = MathView()
view.set_node(root)
```

### Available node types

| Class | Description |
|---|---|
| `TextNode(text, size, italic, bold, color)` | Text / symbol |
| `PlaceholderNode(size)` | Editable placeholder |
| `SeqNode(children)` | Horizontal sequence |
| `FracNode(num, den)` | Fraction |
| `SqrtNode(body, index=None)` | Square / nth root |
| `PowerNode(base, exp)` | Superscript |
| `SubNode(base, sub)` | Subscript |
| `PowerSubNode(base, exp, sub)` | Combined sup+sub |
| `ParenNode(body, left, right)` | Scalable brackets |
| `AbsNode(body)` | Absolute value |
| `IntegralNode(body, var, lower, upper)` | Integral |
| `SumProdNode(kind, body, var, lower, upper)` | Sum / Product |
| `LimitNode(body, var, approach, direction)` | Limit |
| `MatrixNode(rows, left, right)` | Matrix |
| `OverlineNode(body)` | Overline |
| `UnderlineNode(body)` | Underline |

Each node implements:

```python
node.to_sympy()   # SymPy expression string
node.to_latex()   # LaTeX string
node.to_text()    # Plain text
node.measure(painter)  # -> RenderMetrics
node.draw(painter, x, y, hits, selected, cursor_id)
node.collect_ids()     # -> List[int]
node.clone()           # deep copy
```

---

## Constants and maps

```python
from pyqt5_math_widget import GREEK_MAP, OPERATOR_MAP

GREEK_MAP["alpha"]   # -> "α"
OPERATOR_MAP["->"]   # -> "→"
```

---

## Integration

```python
from pyqt5_math_widget import MathDialog
from PyQt5.QtWidgets import QPushButton

btn = QPushButton("\u03A3")
btn.clicked.connect(lambda: self._open_formula_editor())

def _open_formula_editor(self):
    dlg = MathDialog(initial_text=self._input.text(), parent=self.window())
    if dlg.exec_():
        self._input.setText(dlg.result_sympy())
```

---

## License

MIT
