pyTermTk - Your first Calculator¶
Intro¶
This example shows how to use signals and slots to implement the functionality of a calculator widget, and how to use TTkGridLayout to place child widgets in a grid. Due to the modular nature of pyTermTk, the same result may be achieved in multiple ways, for the sack of simplicity I will use a procedural approach avoiding to create a calculator widget.
Design¶
First of all we need a rough idea about the layout we want to achieve. Thanks to my amazing paint.py I draw my idea and I used it to check the grid placement of any widget
Col: 0 1 2 3
|-------|-------|-------|-------| Row:
╔═════════════════════════════════╗ ---
║ ┌─────────────────────────────┐ ║ |
║ │ r:0,c:0, rspan:1, cspan:4 │ ║ | 0
║ └─────────────────────────────┘ ║ ---
║ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ║ |
║ │ 1,0 │ │ 1,1 │ │ 1,2 │ │ 1,3 │ ║ | 1
║ └─────┘ └─────┘ └─────┘ └─────┘ ║ ---
║ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ║ |
║ │ 2,0 │ │ 2,1 │ │ 2,2 │ │ 2,3 │ ║ | 2
║ └─────┘ └─────┘ └─────┘ └─────┘ ║ ---
║ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ║ |
║ │ 3,0 │ │ 3,1 │ │ 3,2 │ │ 3,3 │ ║ | 3
║ └─────┘ └─────┘ └─────┘ │ │ ║ ---
║ ┌─────────────┐ ┌─────┐ │ 2,1 │ ║ |
║ │ 4,0 1,2 │ │ 4,2 │ │ │ ║ | 4
║ └─────────────┘ └─────┘ └─────┘ ║ ---
╚═════════════════════════════════╝
Start Coding¶
Initialize the window¶
From calculator.001.py
# If you want to try without installation, run from the pyTermTk root folder
PYTHONPATH=`pwd` tutorial/calculator/calculator.001.py
First thing first I need a parent widget with a grid layout that I can use to place the elements of my calculator
import TermTk as ttk
# Create a root object (it is a widget that represent the terminal)
root = ttk.TTk()
# Create a window and attach it to the root (parent=root)
calculatorWin = ttk.TTkWindow(parent=root, pos=(1,1), size=(30,17), title="My first Calculator")
# Create a grid layout and set it as default for the window
winLayout = ttk.TTkGridLayout()
calculatorWin.setLayout(winLayout)
Once we have out layout object (winLayout) ready we can add all the widgets of calculator to it
Add all the widgets of calculator to it¶
From calculator.002.py
Based on the positions and sizes defined in the design layout, I place all the widgets on the TTkGridLayout (winLayout)
# If you want to try without installation, run from the pyTermTk root folder
PYTHONPATH=`pwd` tutorial/calculator/calculator.002.py
# Define the Label and attach it to the grid layout at
# Position (Row/Col) (0,0) and (Row/Col)Span (1,4)
# I force the Max Height to 1 in order to avoid this widget to resize vertically
resLabel = ttk.TTkLabel(text="Results", maxHeight=1)
winLayout.addWidget(resLabel, 0,0, 1,4)
# Define the Numeric Buttons and attach them to the grid layout
btn1 = ttk.TTkButton(border=True, text="1")
btn2 = ttk.TTkButton(border=True, text="2")
btn3 = ttk.TTkButton(border=True, text="3")
btn4 = ttk.TTkButton(border=True, text="4")
btn5 = ttk.TTkButton(border=True, text="5")
btn6 = ttk.TTkButton(border=True, text="6")
btn7 = ttk.TTkButton(border=True, text="7")
btn8 = ttk.TTkButton(border=True, text="8")
btn9 = ttk.TTkButton(border=True, text="9")
winLayout.addWidget(btn1, 1,0) # Colspan/Rowspan are defaulted to 1 if not specified
winLayout.addWidget(btn2, 1,1)
winLayout.addWidget(btn3, 1,2)
winLayout.addWidget(btn4, 2,0)
winLayout.addWidget(btn5, 2,1)
winLayout.addWidget(btn6, 2,2)
winLayout.addWidget(btn7, 3,0)
winLayout.addWidget(btn8, 3,1)
winLayout.addWidget(btn9, 3,2)
# Adding the "0" button on the bottom which alignment is
# Position (Row/Col) (4,0) (Row/Col)span (1,2)
# Just to show off I am using another way to attach it to the grid layout
winLayout.addWidget(btn0:=ttk.TTkButton(border=True, text="0"), 4,0, 1,2)
# Define the 2 algebric buttons
winLayout.addWidget(btnAdd:=ttk.TTkButton(border=True, text="+"), 1,3)
winLayout.addWidget(btnSub:=ttk.TTkButton(border=True, text="-"), 2,3)
# The Enter "=" button (2 rows wide )
winLayout.addWidget(btnRes:=ttk.TTkButton(border=True, text="="), 3,3, 2,1)
# Last but not least an extrabutton just for fun
winLayout.addWidget(mysteryButton:=ttk.TTkButton(border=True, text="?"), 4,2)
This code will produce this result:
╔════════════════════════════╗
║ My first Calculator ║
╟────────────────────────────╢
║Results ║
║┌─────┐┌─────┐┌─────┐┌─────┐║
║│ 1 ││ 2 ││ 3 ││ + │║
║╘═════╛╘═════╛╘═════╛╘═════╛║
║┌─────┐┌─────┐┌─────┐┌─────┐║
║│ 4 ││ 5 ││ 6 ││ - │║
║╘═════╛╘═════╛╘═════╛╘═════╛║
║┌─────┐┌─────┐┌─────┐┌─────┐║
║│ 7 ││ 8 ││ 9 ││ │║
║╘═════╛╘═════╛╘═════╛│ = │║
║┌────────────┐┌─────┐│ │║
║│ 0 ││ ? ││ │║
║╘════════════╛╘═════╛╘═════╛║
╚════════════════════════════╝
Cool isn’t it?
Operation and results events¶
From calculator.004.py
# If you want to try without installation, run from the pyTermTk root folder
PYTHONPATH=`pwd` tutorial/calculator/calculator.004.py
# Define 2 slots to handle the Add and Sub operations
@pyTTkSlot()
def setOperationAdd():
mathElements['operation'] = "ADD"
@pyTTkSlot()
def setOperationSub():
mathElements['operation'] = "SUB"
# Connect them to the clicked signal of the buttons
btnAdd.clicked.connect(setOperationAdd)
btnSub.clicked.connect(setOperationSub)
# Same for the "=" button
@pyTTkSlot()
def executeOperation():
if mathElements['operation'] is not None:
if mathElements['operation'] == "ADD":
res = mathElements['a'] + mathElements['b']
resLabel.setText(f"{mathElements['a']} + {mathElements['b']} = {res}")
else: # "SUB" Routine
res = mathElements['a'] - mathElements['b']
resLabel.setText(f"{mathElements['a']} - {mathElements['b']} = {res}")
# reset the values
mathElements['a'] = res
mathElements['b'] = 0
mathElements['operation'] = None
btnRes.clicked.connect(executeOperation)