how to make a calculator 10: part 2
open the project
I `change directory`_ to the
calculatorfolder_cd calculatorI use
pytest-watcherto run the testsuv run pytest-watcher . --nowthe terminal_ shows
rootdir: .../pumping_python/calculator configfile: pyproject.toml collected 10 items tests/test_calculator.py ..... [ 50%] tests/test_calculator_website.py ... [ 80%] tests/test_streamlit_calculator.py .. [100%] ======================== 10 passed in X.YZs =========================I open another terminal_ then use uv_ in the
calculatorfolder_uv run streamlit run src/streamlit_calculator.pythe terminal_ shows
Collecting usage statistics. To deactivate, set browser.gatherUsageStats to false. You can now view your Streamlit app in your browser. Local URL: http://localhost:8501 Network URL: http://ABC.DEF.GHI.JKL:8501 External URL: http://MNO.PQR.STU.VWX:8501I use ctrl/option on the keyboard and click on
http://localhost:8501with the mouse to open the browser and it shows
how to use raw strings and escape characters
I add
rand\to escape the-character because it means something to streamlit_, which is why it does not show on the button in theadd_buttons_to_column_4function28def add_buttons_to_column_4(column_4): 29 column_4.button(label='/', key='/', width='stretch') 30 column_4.button(label='X', key='X', width='stretch') 31 column_4.button(label=r'\-', key=r'\-', width='stretch') 32 column_4.button(label='+', key='+', width='stretch') 33 column_4.button(label='=', key='=', width='stretch')KeyError: '-'I change the
-tor'\-'in test_streamlit_calculator_columns_and_buttons intest_streamlit_calculator.py27 def test_streamlit_calculator_columns_and_buttons(self): 28 self.assertEqual(len(self.tester.columns), 4) 29 30 for column, keys in ( 31 (0, ('<-', '7', '4', '1', '+/-')), 32 (1, ('C', '8', '5', '2', '0')), 33 (2, ('AC', '9', '6', '3', '.')), 34 # (3, ('/', 'X', '-', '+', '=')), 35 (3, ('/', 'X', r'\-', '+', '=')), 36 ): 37 for key in keys:the test passes
rmakes this a raw string_ which means the characters are taken as they are, not what they stand for\escapes the character so that-is taken exactly as it is to show the character on the button.'-'means something different in streamlit_ so it does not show up as a label for the button
I add
rand\to escape the+character because it means something to streamlit_, which is why it does not show on the button in theadd_buttons_to_column_4function, instreamlit_calculator.py28def add_buttons_to_column_4(column_4): 29 column_4.button(label='/', key='/', width='stretch') 30 column_4.button(label='X', key='X', width='stretch') 31 column_4.button(label=r'\-', key=r'\-', width='stretch') 32 column_4.button(label=r'\+', key=r'\+', width='stretch') 33 column_4.button(label='=', key='=', width='stretch')KeyError: '+'I change the
+tor'\+'in test_streamlit_calculator_columns_and_buttons intest_streamlit_calculator.py27 def test_streamlit_calculator_columns_and_buttons(self): 28 self.assertEqual(len(self.tester.columns), 4) 29 30 for column, keys in ( 31 (0, ('<-', '7', '4', '1', '+/-')), 32 (1, ('C', '8', '5', '2', '0')), 33 (2, ('AC', '9', '6', '3', '.')), 34 # (3, ('/', 'X', '-', '+', '=')), 35 # (3, ('/', 'X', r'\-', '+', '=')), 36 (3, ('/', 'X', r'\-', r'\+', '=')), 37 ): 38 for key in keys:the test passes
rmakes this a raw string_ which means the characters are taken as they are, not what they stand for\escapes the character so that+is taken exactly as it is to show the character on the button.'+'means something different in streamlit_ so it does not show up as a label for the button
I remove the commented lines
27 def test_streamlit_calculator_columns_and_buttons(self): 28 self.assertEqual(len(self.tester.columns), 4) 29 30 for column, keys in ( 31 (0, ('<-', '7', '4', '1', '+/-')), 32 (1, ('C', '8', '5', '2', '0')), 33 (2, ('AC', '9', '6', '3', '.')), 34 (3, ('/', 'X', r'\-', r'\+', '=')), 35 ): 36 for key in keys: 37 with self.subTest(key=key): 38 self.assertEqual( 39 ( 40 self.tester.columns[column] 41 .button(key) 42 .label 43 ), 44 key 45 ) 46 47 48# Exceptions seenI check the browser
yes! The calculator shows all the labels.
how to show the numbers when I click on them
I want the calculator to show the number when I press a button
I add a variable to name the container so I can use it to show the numbers
50def main(): 51 streamlit.title('Calculator') 52 display = streamlit.container(border=True) 53 54 column_1, column_2, column_3, operations = streamlit.columns(4) 55 add_buttons_to_column_1(column_1) 56 add_buttons_to_column_2(column_2) 57 add_buttons_to_column_3(column_3) 58 add_buttons_to_column_4(operations)I add a function to show the text of the button when it is clicked
1import streamlit 2 3 4def show(display, number): 5 display.write(number) 6 7 8def add_buttons_to_column_1(column_1):`streamlit buttons`_ have an
on_clickparameter that lets me call a function when a button is pressed. It also takes an argument namedargswhere I can pass in the positional arguments that the function I give for theon_clickparameter takes. I pass the function and thedisplayvariable with a value as the arguments for the7button in theadd_buttons_to_column_1function8def add_buttons_to_column_1(column_1): 9 column_1.button(label='<-', key='<-', width='stretch') 10 column_1.button( 11 label='7', key='7', width='stretch', 12 on_click=show, args=[display, '7'], 13 ) 14 column_1.button(label='4', key='4', width='stretch') 15 column_1.button(label='1', key='1', width='stretch') 16 column_1.button(label='+/-', key='+/-', width='stretch')================== 26 failed, 12 passed in A.BCs ===================the terminal_ for the application shows NameError
NameError: name 'display' is not definedI add a parameter for the
displayvariable in the function signature foradd_buttons_to_column_18def add_buttons_to_column_1(column_1, display):the terminal_ shows KeyError for 27 sub tests and the terminal_ for the streamlit_ application shows TypeError
TypeError: add_buttons_to_column_1() missing 1 required positional argument: 'display'I add the
displayvariable to the call toadd_buttons_to_column_1in themainfunction57def main(): 58 streamlit.title('Calculator') 59 display = streamlit.container(border=True) 60 61 column_1, column_2, column_3, operations = streamlit.columns(4) 62 add_buttons_to_column_1(column_1, display) 63 add_buttons_to_column_2(column_2) 64 add_buttons_to_column_3(column_3) 65 add_buttons_to_column_4(operations)the test passes
I go to the browser and click on the
7button
7shows on the display. Yes!I make the same change to the other numbers in the first column
8def add_buttons_to_column_1(column_1, display): 9 column_1.button(label='<-', key='<-', width='stretch') 10 column_1.button( 11 label='7', key='7', width='stretch', 12 on_click=show, args=[display, '7'], 13 ) 14 column_1.button( 15 label='4', key='4', width='stretch', 16 on_click=show, args=[display, '4'] 17 ) 18 column_1.button( 19 label='1', key='1', width='stretch', 20 on_click=show, args=[display, '1'] 21 ) 22 column_1.button( 23 label='+/-', key='+/-', width='stretch', 24 on_click=show, args=[display, '+/-'] 25 )the test is still green
I do the same thing for the numbers in the second column
28def add_buttons_to_column_2(column_2): 29 column_2.button( 30 label='C', key='C', width='stretch', type='primary', 31 ) 32 column_2.button( 33 label='8', key='8', width='stretch', 34 on_click=show, args=[display, '8'] 35 ) 36 column_2.button( 37 label='5', key='5', width='stretch', 38 on_click=show, args=[display, '5'] 39 ) 40 column_2.button( 41 label='2', key='2', width='stretch', 42 on_click=show, args=[display, '2'] 43 ) 44 column_2.button( 45 label='0', key='0', width='stretch', 46 on_click=show, args=[display, '0'] 47 )the terminal_ shows KeyError for 20 sub tests and the terminal_ for the application shows NameError
I add
displayto the function signature of theadd_buttons_to_column_2function28def add_buttons_to_column_2(column_2, display):the terminal_ shows KeyError for 22 sub tests and the terminal_ for the application shows TypeError
TypeError: add_buttons_to_column_2() missing 1 required positional argument: 'display'I add
displayto the call to theadd_buttons_to_column_2function in themainfunction78def main(): 79 streamlit.title('Calculator') 80 display = streamlit.container(border=True) 81 82 column_1, column_2, column_3, operations = streamlit.columns(4) 83 add_buttons_to_column_1(column_1, display) 84 add_buttons_to_column_2(column_2, display) 85 add_buttons_to_column_3(column_3) 86 add_buttons_to_column_4(operations)the test passes
I make the same change for the numbers in the third column
47def add_buttons_to_column_3(column_3): 48 column_3.button( 49 label='AC', key='AC', width='stretch', type='primary', 50 ) 51 column_3.button( 52 label='9', key='9', width='stretch', 53 on_click=show, args=[display, '9'], 54 ) 55 column_3.button( 56 label='6', key='6', width='stretch', 57 on_click=show, args=[display, '6'], 58 ) 59 column_3.button( 60 label='3', key='3', width='stretch', 61 on_click=show, args=[display, '3'], 62 ) 63 column_3.button( 64 label='.', key='.', width='stretch', 65 on_click=show, args=[display, '.'], 66 )green
I go to the browser to test the numbers and they show up in the box, with one problem - every time I press a button it shows a new number. I want the numbers to stay so that I can make numbers that have more than one digit
close the project
I close all files
I click in the terminal_, then use q to leave the tests
I `change directory`_ to the parent
cd ..
review
I now have three different versions of the same calculator:
Pure Python (chapters 1–8)
Flask website (chapter 9)
Streamlit web app (chapter 10) — the fastest and most beautiful version
The core calculator code never changed. All my tests still protect it. This is the real power of Test-Driven Development.
code from the chapter
what is next?
You have completed an amazing journey from pure functions to real web applications!
You now know how to:
Build programs with Test-Driven Development
Turn them into Flask websites
Turn them into beautiful Streamlit apps
rate pumping python
If this has been a 7 star experience for you, please `CLICK HERE to leave a 5 star review of pumping python`_. It helps other people get into the book too