How to Create Web Apps with Python, HTML and Thonny

Create Web Apps with Python, HTML and Thonny
(Image credit: Tom's Hardware)

Python is a glue. We can use it to join different elements of code together. As a language, Python is easy to learn, and human readable which makes it one of the most effective languages for learning and general purpose programming. Part of Python’s charm are the many modules of code which can be easily inserted into a project. 

Thonny is a powerful yet simple editor for Python and, with the release of version 4, we wanted to use it to create a project. In this how to we shall use the latest version of Thonny to create a web application that will pull Raspberry Pi stock data from rpilocator.com and use it to populate a table in our app.

RSS is a great way to share a stream of information. It can be used to serve news headlines, such as the Tom’s Hardware RSS feed or even the latest xkcd comic strip.

Nore that the RSS feed from rpilocator is not as up-to-date as the data on rpilocator.com. Think of this project as more of a notification system, than a “sniping” tool.

Installing Thonny 4.0

Thonny is the default Python IDE on the Raspberry Pi, but it is not limited to just that machine. Thonny is also available for Windows, Mac and Linux machines, and it can be used to write Python and MicroPython for devices such as the Raspberry Pi Pico W and ESP32.

1. Open a browser to the Thonny homepage.

(Image credit: Tom's Hardware)

2. Select the download for your OS. For Windows, there are a few options to choose from. The first choice is which version of Python, we would recommend the latest (3.10 at the time of writing). Next is your choice of installing Thonny to your machine, or using a portable version. We recommend installing Thonny to your machine.

(Image credit: Tom's Hardware)

3. Click on the downloaded file to start the installation.

4. Click on “More Info” to continue the installation. The new installation has a certificate that is relatively unknown and has yet to build up a reputation.

(Image credit: Tom's Hardware)

5. Click on “Run anyway” to continue.

(Image credit: Tom's Hardware)

6. Click next to continue.

(Image credit: Tom's Hardware)

7. Accept the License agreement.

(Image credit: Tom's Hardware)

8. Select the checkbox to create a desktop icon. This is an optional step, We chose not to do this as we prefer icons in the task bar.

(Image credit: Tom's Hardware)

9. Click Install to start the install process.

(Image credit: Tom's Hardware)

10. Click Finish to end the installation.

(Image credit: Tom's Hardware)

Creating Our Project with Thonny 4.0

Thonny is beginner focused, but don’t be fooled, Thonny is a competent and fully featured editor for makers. Thonny has a multi-window layout that can be edited to suit your needs.

1. Files: This is a basic file manager that can be used to open files in a project. Raspberry Pi Pico W and other MicroPython devices will open an additional pane that we can use to copy files to and from the device.

2. Coding Area: Here is where we create the project for our code. We can have multiple tabs, for multiple files.

3. Python Shell: The Python Shell (REPL, Read, Eval, Print, Loop) is where we can see the output of our code, and also interact with it.

4. Assistant: If your code has a bug, or doesn’t follow a styling guideline, it will be flagged here.

(Image credit: Tom's Hardware)

Installing Modules with Thonny

Python modules (sometimes also referred to as “libraries”) are pre-written segments of code that enable extra functionality. Popular examples include RPI.GPIO and GPIO Zero for the Raspberry Pi. Modules often abstract / simplify complex tasks. In our project we will use two modules. PyWebIO is a module to create HTML content using Python. It also creates a web server that we can use to quickly connect to our app. The second module is Feedparser, an RSS feed reader module that we shall use to read the rpilocator Raspberry Pi stock level feed.

1. Open Thonny and ensure that no projects are open.

2. Click on Tools >> Manage Packages. Thonny has a built-in GUI for the Python 3 package manager “pip”.

(Image credit: Tom's Hardware)

3. Search for pywebio.This is the module that we shall use to generate a web page using Python.

(Image credit: Tom's Hardware)

4. Click Install to download and install the module.

(Image credit: Tom's Hardware)

5. Repeat the previous steps, this time install feedparser. Feedparser is a Python module for RSS feeds.

(Image credit: Tom's Hardware)

6. Click Close to quit the dialog.

(Image credit: Tom's Hardware)

Writing the Project Code

(Image credit: Tom's Hardware)

Our goal is to create a Python project that will use the data from rpilocator’s RSS feed to populate a table. We will grab the current five entries and display them in an HTML table, created using Python.

1. In a new blank document, import two modules from pywebio. The first contains the code to start a simple web server. The pywebio.output module is used to generate HTML elements such as tables and hyperlinks.

from pywebio import start_server
from pywebio.output import *  

2. Import the feedparser module.

import feedparser

3. Create a function called main.

def main():

4. Inside the function create an object, “stock” and use it to store the parsed output of the rpilocator RSS feed.

   stock = feedparser.parse('https://rpilocator.com/feed/')

5. Create three empty lists, in_stock, in_stock_link and category. These will be used to store the data retrieved from the “stock” object containing the RSS data.

   in_stock = []
   in_stock_link = []
   category = []

6. Create a for loop that will iterate five times.

   for i in range(5):

7. Use “append” to add the stock status, link and category (reseller name) to the appropriate list. The RSS data stored in “stock” is a mixture of lists and dictionaries. For the data in a list we can use its numerical index, which is the value of i in our for loop. This will count from 0 to 4 as the for loop iterates. The data stored in a dictionary requires us to know the key (‘entries’ for example). Using the key will return its value.

       in_stock.append(stock['entries'][i]['title'])
       in_stock_link.append(stock['entries'][i]['link'])
       category.append(stock['entries'][i]['category'])

An example of the RSS feed data viewed in Google Chrome (Image credit: Tom's Hardware)

8. Outside of the for loop, create a pop-up notification using “toast”. The message can be a mixture of a strong, and even emojis.

   toast('🍓I found Raspberry Pi in stock!🍓')

9. Use “put_html” to write an HTML H1 heading element to the web page. We can use this function to write any HTML elements to the page, but do take note that the PyWebIO module has many different means to create specialist elements.

   put_html("<h1>Raspberry Pi Stock</h1>")

10. Create a list, “table” and use it to store two columns of data, taken from our in_stock, in_stock_link and category lists. The first row are the column headings Details and URL. In stock will print a brief description of what is in stock. Using “put_link” we create an HTML hyperlink, with the link text being the name of the reseller, stored in the category list, and the address stored in in_stock_link.

   table = [['Details','URL'],
       [in_stock[0], put_link(category[0],url=in_stock_link[0])],
       [in_stock[1], put_link(category[1],url=in_stock_link[1])],
       [in_stock[2], put_link(category[2],url=in_stock_link[2])],
       [in_stock[3], put_link(category[3],url=in_stock_link[3])],
       [in_stock[4], put_link(category[4],url=in_stock_link[4])],
       ]

11. Use PyWebIO’s “put_table” function to create an HTML table from our table object.

   put_table(table)

12. Use “put_link” to create a hyperlink under the table, in this case it takes us to the source of the Raspberry Pi stock levels, rpilocator.

   put_link('Data provided by RPiLocator',url='https://rpilocator.com')

13. Outside of the function, call PyWebIO’s “start_server” function, and pass it three arguments. The arguments are our “main” function which will create the table from the RSS data. The port is set to 8080, and debugging is enabled via the Python shell and on our web page.

start_server(main, port=8080, debug=True)

14. Save the code as RSS-Feed-Reader.py and click Run to start.

(Image credit: Tom's Hardware)

15. Click on the link in the Python shell to open the web page in your default browser.

(Image credit: Tom's Hardware)

Complete Code Listing

from pywebio import start_server
from pywebio.output import *    
import feedparser
def main():
   stock = feedparser.parse('https://rpilocator.com/feed/')
   in_stock = []
   in_stock_link = []
   category = []
   for i in range(5):
       in_stock.append(stock['entries'][i]['title'])
       in_stock_link.append(stock['entries'][i]['link'])
       category.append(stock['entries'][i]['category'])
   toast('🍓I found Raspberry Pi in stock!🍓')
   put_html("<h1>Raspberry Pi Stock</h1>")
   table = [['Details','URL'],
       [in_stock[0], put_link(category[0],url=in_stock_link[0])],
       [in_stock[1], put_link(category[1],url=in_stock_link[1])],
       [in_stock[2], put_link(category[2],url=in_stock_link[2])],
       [in_stock[3], put_link(category[3],url=in_stock_link[3])],
       [in_stock[4], put_link(category[4],url=in_stock_link[4])],
       ]
   put_table(table)
   put_link('Data provided by RPiLocator',url='https://rpilocator.com')
start_server(main, port=8080, debug=True)

Python How Tos

Les Pounder

Les Pounder is an associate editor at Tom's Hardware. He is a creative technologist and for seven years has created projects to educate and inspire minds both young and old. He has worked with the Raspberry Pi Foundation to write and deliver their teacher training program "Picademy".

  • Christopher_115
    "5. Click on “Run anyway” to continue. "

    That's a negative, Ghostrider.
    Reply
  • brandonjclark
    "Part of Python’s charm "

    PyCharm: the Python IDE for Professional Developers by JetBrains

    I see what you did there... ;)
    Reply
  • brandonjclark said:
    "Part of Python’s charm "

    PyCharm: the Python IDE for Professional Developers by JetBrains
    I see what you did there... ;)
    Just use Microsoft code with the appropriate plug-ins. No need for that IDE at all especially if it’s not free. I use sublime text anyway for all my development purposes I don’t need any extra garbage

    Nobody needs a fancy IDE for python LOL

    There editor looks just like Microsoft code which is based on Atom
    Reply
  • Bo-W4GHV
    Looked interesting, but way over my head, I gave it a try. I got the following errors and don't have a clue. I sent them to the feedparser author of possible help.
    I'm still at the beginner level trying to get cute graphics and menus on the screen with TKinter. :)

    Traceback (most recent call last):
    File "C:\Users\Bo\Downloads\Adafruit_CircuitPython_MatrixKeypad-main\examples\RSS-Feed-Reader.py", line 3, in <module>
    import feedparser
    File "C:\Users\Bo\AppData\Roaming\Python\Python310\site-packages\feedparser\init.py", line 39, in <module>
    from . import api
    File "C:\Users\Bo\AppData\Roaming\Python\Python310\site-packages\feedparser\api.py", line 54, in <module>
    from . import http
    File "C:\Users\Bo\AppData\Roaming\Python\Python310\site-packages\feedparser\http.py", line 44, in <module>
    _base64decode = getattr(base64, 'decodebytes', base64.decodestring)
    AttributeError: module 'base64' has no attribute 'decodestring'

    73, Bo W4GHV since '54
    Reply
  • MisterWho
    decodestring() does no longer exists from v3.9. Checkout this docs, use base64.decodebytes() instead.
    Reply
  • Bo-W4GHV
    Thanks! Strangely the program had a comment to that change but failed to fix it!🤔
    Fixing it in 2 files fixed it and it 'ran'
    However the web page results has a list of new errors!
    I'll list them but don't worry/bother if fixing it is a mess.
    Again over my head but looked like one I could get going. 73. Bo

    Traceback (most recent call last):
    File "C:\Users\Bo\AppData\Roaming\Python\Python310\site-packages\pywebio\session\threadbased.py", line 86, in main_task
    target()
    File "C:\Users\Bo\Downloads\Adafruit_CircuitPython_MatrixKeypad-main\examples\RSS-Feed-Reader.py", line 5, in main
    stock = feedparser.parse('https://rpilocator.com/feed/')
    File "C:\Users\Bo\AppData\Roaming\Python\Python310\site-packages\feedparser\api.py", line 238, in parse
    data = convert_to_utf8(result, data, result)
    File "C:\Users\Bo\AppData\Roaming\Python\Python310\site-packages\feedparser\encodings.py", line 244, in convert_to_utf8
    if isinstance(proposed_encoding, collections.Callable):
    AttributeError: module 'collections' has no attribute 'Callable'
    Reply
  • Bo-W4GHV
    Bo-W4GHV said:
    Thanks! Strangely the program had a comment to that change but failed to fix it!🤔
    Fixing it in 2 files fixed it and it 'ran'
    However the web page results has a list of new errors!
    I'll list them but don't worry/bother if fixing it is a mess.
    Again over my head but looked like one I could get going. 73. Bo

    Traceback (most recent call last):
    File "C:\Users\Bo\AppData\Roaming\Python\Python310\site-packages\pywebio\session\threadbased.py", line 86, in main_task
    target()
    File "C:\Users\Bo\Downloads\Adafruit_CircuitPython_MatrixKeypad-main\examples\RSS-Feed-Reader.py", line 5, in main
    stock = feedparser.parse('https://rpilocator.com/feed/')
    File "C:\Users\Bo\AppData\Roaming\Python\Python310\site-packages\feedparser\api.py", line 238, in parse
    data = convert_to_utf8(result, data, result)
    File "C:\Users\Bo\AppData\Roaming\Python\Python310\site-packages\feedparser\encodings.py", line 244, in convert_to_utf8
    if isinstance(proposed_encoding, collections.Callable):
    AttributeError: module 'collections' has no attribute 'Callable'
    I traced down all the comments and it looks like the problem is with the https://rpilocator.com/feed error listed.
    Headed there now. A 'RPI locator' will find about 6 of them here.😂
    Reply
  • Bo-W4GHV
    Bo-W4GHV said:
    I traced down all the comments and it looks like the problem is with the https://rpilocator.com/feed error listed.
    Headed there now. A 'RPI locator' will find about 6 of them here.😂
    WAIT! How dumb I am. I actually have closer to 10 Pi's and need NO more.
    I was thinking it could be easily modified to do for 'fun' /educational things.
    I probably should quit this one.

    Earlier I struck out just trying to scrape the solar data from https://www.hamqsl.com for a cute display.

    I am succeeding in getting a Pi, tkinter,Python, arduino, stepper motor controlling a mag loop antenna.
    Reply
  • MisterWho
    Bo-W4GHV said:
    Thanks! Strangely the program had a comment to that change but failed to fix it!🤔
    Fixing it in 2 files fixed it and it 'ran'
    However the web page results has a list of new errors!
    I'll list them but don't worry/bother if fixing it is a mess.
    Again over my head but looked like one I could get going. 73. Bo

    Traceback (most recent call last):
    File "C:\Users\Bo\AppData\Roaming\Python\Python310\site-packages\pywebio\session\threadbased.py", line 86, in main_task
    target()
    File "C:\Users\Bo\Downloads\Adafruit_CircuitPython_MatrixKeypad-main\examples\RSS-Feed-Reader.py", line 5, in main
    stock = feedparser.parse('https://rpilocator.com/feed/')
    File "C:\Users\Bo\AppData\Roaming\Python\Python310\site-packages\feedparser\api.py", line 238, in parse
    data = convert_to_utf8(result, data, result)
    File "C:\Users\Bo\AppData\Roaming\Python\Python310\site-packages\feedparser\encodings.py", line 244, in convert_to_utf8
    if isinstance(proposed_encoding, collections.Callable):
    AttributeError: module 'collections' has no attribute 'Callable'


    AttributeError: module 'collections' has no attribute 'Callable'

    Removed from v3.10


    In the article the author uses Thonny 4.0.0.

    Thonny 4.0.0 comes with Python 3.10.6 - this script would've NEVER worked without any error! O.o
    Reply