Applications

In this section we are showing some practical applications.

Calculator

The first example is a pocket calculator.

First, we just do the layout for the buttons:

App.lb = Label('0.0')
App.lb.grid(columnspan=4)

Button('AC', ).grid(row=1)
Button('±').grid(row=1, column=1)
Button('%').grid(row=1, column=2)
Button(':').grid(row=1, column=3)
../_images/calc1.png
"""Create calculator buttons."""
from tklib import *

class Demo(App):
    def __init__(self): 
        super().__init__()

        App.lb = Label('0.0')
        App.lb.grid(columnspan=4)

        Button('AC', ).grid(row=1)
        Button('±').grid(row=1, column=1)
        Button('%').grid(row=1, column=2)
        Button(':').grid(row=1, column=3)
        
        Button('7').grid(row=2, column=0)
        Button('8').grid(row=2, column=1)
        Button('9').grid(row=2, column=2)
        Button('x').grid(row=2, column=3)
        
        Button('4').grid(row=3, column=0)
        Button('5').grid(row=3, column=1)
        Button('6').grid(row=3, column=2)
        Button('-').grid(row=3, column=3)
        
        Button('1').grid(row=4, column=0)
        Button('2').grid(row=4, column=1)
        Button('3').grid(row=4, column=2)
        Button('+').grid(row=4, column=3)

        Button('0').grid(row=5, columnspan=2, sticky='we')
        Button('.').grid(row=5, column=2)
        Button('=').grid(row=5, column=3)

if __name__ == '__main__':
    Demo().run()

calc1.py

Then we are going to add callback functions to the keys:

Button('7', 'App.lb["text"]  = float(App.lb["text"])*10 + 7').grid(row=2, column=0)
Button('8', 'App.lb["text"]  = float(App.lb["text"])*10 + 8').grid(row=2, column=1)
Button('9', 'App.lb["text"]  = float(App.lb["text"])*10 + 9').grid(row=2, column=2)
Button('x').grid(row=2, column=3)
../_images/calc2.png
"""Create calculator buttons."""
from tklib import *

class Demo(App):
    def __init__(self): 
        super().__init__()

        s = ttk.Style()
        s.configure('TButton', font='Arial 18', padding=5)

        App.lb = Label('0', font='Arial 36')
        App.lb.grid(columnspan=4, sticky='e')

        Button('C', 'App.lb["text"]  = 0.0').grid(row=1)
        Button('±').grid(row=1, column=1)
        Button('%').grid(row=1, column=2)
        Button(':').grid(row=1, column=3)
        
        Button('7', 'App.lb["text"]  = float(App.lb["text"])*10 + 7').grid(row=2, column=0)
        Button('8', 'App.lb["text"]  = float(App.lb["text"])*10 + 8').grid(row=2, column=1)
        Button('9', 'App.lb["text"]  = float(App.lb["text"])*10 + 9').grid(row=2, column=2)
        Button('x').grid(row=2, column=3)
        
        Button('4', 'App.lb["text"]  = float(App.lb["text"])*10 + 4').grid(row=3, column=0)
        Button('5', 'App.lb["text"]  = float(App.lb["text"])*10 + 5').grid(row=3, column=1)
        Button('6', 'App.lb["text"]  = float(App.lb["text"])*10 + 6').grid(row=3, column=2)
        Button('-').grid(row=3, column=3)
        
        Button('1', 'App.lb["text"]  = float(App.lb["text"])*10 + 1').grid(row=4, column=0)
        Button('2', 'App.lb["text"]  = float(App.lb["text"])*10 + 2').grid(row=4, column=1)
        Button('3', 'App.lb["text"]  = float(App.lb["text"])*10 + 3').grid(row=4, column=2)
        Button('+').grid(row=4, column=3)

        Button('0', 'App.lb["text"]  = float(App.lb["text"])*10 + 0').grid(row=5, columnspan=2, sticky='we')
        Button('.').grid(row=5, column=2)
        Button('=').grid(row=5, column=3)

if __name__ == '__main__':
    Demo().run()

calc2.py

It turns out that it is simpler to place the calculator logic into a separate function, based on a single character:

def calculate(self, c):
    """Calculator for 4 basic operations."""
    if c in '0123456789.±+-/*%c=':
        if c in '0123456789':
            if self.pos:
                self.val += int(c) * 10**self.pos
                self.pos -= 1
            else:
                self.val *= 10
                self.val += int(c)
        if c == '.':
            self.pos = -1
        if c == '%':
            self.val *= 0.01
        elif c in '+-/*':
            self.val2 = self.val
            self.val = 0
            self.pos = 0
            self.op = c
        elif c == '=':
            e = str(self.val2) + self.op + str(self.val)
            self.val = eval(e)
        if c == 'c':
            self.val = 0
            self.pos = 0

        self.lb['text'] = self.val
../_images/calc3.png
"""Create calculator buttons."""
from tklib import *

class Demo(App):
    def __init__(self): 
        super().__init__()

        s = ttk.Style()
        s.configure('TButton', font='Arial 18', padding=5)

        self.lb = Label('0', font='Arial 36', width=15, anchor='e')

        self.val = 0
        self.pos = 0
        self.val2 = 0

        App.root.bind('<Key>', self.cb)

    def cb(self, event=None):
        """React to key press events."""
        c = event.char
        if c != '':
            self.calculate(c)

    def calculate(self, c):
        """Calculator for 4 basic operations."""
        if c in '0123456789.±+-/*%c=':
            if c in '0123456789':
                if self.pos:
                    self.val += int(c) * 10**self.pos
                    self.pos -= 1
                else:
                    self.val *= 10
                    self.val += int(c)
            if c == '.':
                self.pos = -1
            if c == '%':
                self.val *= 0.01
            elif c in '+-/*':
                self.val2 = self.val
                self.val = 0
                self.pos = 0
                self.op = c
            elif c == '=':
                e = str(self.val2) + self.op + str(self.val)
                self.val = eval(e)
            if c == 'c':
                self.val = 0
                self.pos = 0

            self.lb['text'] = self.val

if __name__ == '__main__':
    Demo().run()

calc3.py

And finally we put everything together. New Calculator instances can be created via a button press or via a menu.

../_images/calc4.png
"""Create calculator buttons."""
from tklib import *

class Calculator():
    def __init__(self):
        self.win = Window('Calculator')
        s = ttk.Style()
        s.configure('TButton', font='Arial 18', padding=5)

        self.lb = Label('0', font='Arial 44', width=15, anchor='e')
        self.lb.grid(columnspan=4)

        buttons = 'c±%/789*456-123+ 0.='
        for i, b in enumerate(buttons):
            Button(b, lambda c=b: self.calculate(c)).grid(row=i//4+1, column=i%4)
        self.val = 0
        self.pos = 0
        self.val2 = 0
        self.op = ''

        self.win.top.bind('<Key>', self.cb)

    def cb(self, event=None):
        """React to key press events."""
        c = event.char
        if c != '':
            self.calculate(c)

    def calculate(self, c):
        """Calculator for 4 basic operations."""
        if c in '0123456789.±+-/*%c=':
            if c in '0123456789':
                if self.pos:
                    self.val += int(c) * 10**self.pos
                    self.pos -= 1
                else:
                    self.val *= 10
                    self.val += int(c)
            elif c == '.':
                self.pos = -1
            elif c == '%':
                self.val *= 0.01
            elif c == '±':
                self.val *= -1
            elif c in '+-/*':
                self.val2 = self.val
                self.val = 0
                self.pos = 0
                self.op = c
            elif c == '=':
                e = str(self.val2) + self.op + str(self.val)
                self.val = eval(e)
            if c == 'c':
                self.val = 0
                self.pos = 0

            self.lb['text'] = self.val


class Demo(App):
    def __init__(self): 
        super().__init__()
        Button('New calculator', Calculator)
        Menu('App')
        Item('Calculator', Calculator)

if __name__ == '__main__':
    Demo().run()

calc4.py