How to build GUI Applications with Python and BeeWare

This article explores the fundamentals of building apps with Python using the BeeWare suite, detailing its features, benefits, and how it compares to other popular frameworks.

How to build GUI Applications with Python and BeeWare
Image by Author

Building applications with Python has become increasingly popular due to the language's simplicity and versatility.

Among the various frameworks and tools available for Python developers, BeeWare stands out as a unique and powerful option.

This article explores the fundamentals of building apps with Python using the BeeWare suite, detailing its features, benefits, and how it compares to other popular frameworks.


Introduction to BeeWare

BeeWare is an open-source collection of tools and libraries for building native applications in Python.

It allows developers to write their app in Python and then deploy it on multiple platforms, including Windows, macOS, Linux, iOS, and Android, with a native user interface.

This cross-platform compatibility is a significant advantage, saving time and resources that would otherwise be spent on developing separate apps for each operating system.

Key Features of BeeWare

  • Native User Interface: Unlike some frameworks that mimic native components, BeeWare uses the native user interface elements of each platform, ensuring that the apps look and feel like they belong on the platform.
  • Python Everywhere: BeeWare apps are written entirely in Python, allowing developers to leverage their existing Python skills and libraries.
  • Toga: A key component of BeeWare, Toga is a Python native, OS-native GUI toolkit. It's an open-source library that provides a common interface to platform-specific native GUI elements.
  • Briefcase: Another crucial tool in the BeeWare suite, Briefcase is used for packaging Python projects as standalone native applications on various platforms.

Advantages of Using BeeWare

  • Cross-Platform Development: Write your code once and deploy it on multiple platforms without significant changes.
  • Native Look and Feel: Applications built with BeeWare blend seamlessly with the native ecosystem of each platform.
  • Pythonic Development: Full access to Python's vast ecosystem of libraries and tools.

Comparing BeeWare with Other Frameworks

While frameworks like Kivy or PyQt also offer cross-platform capabilities, BeeWare's unique selling point is its use of native widgets.

This means apps developed with BeeWare typically have better performance and a more native look and feel compared to those using other Python frameworks.


Challenges and Limitations

Despite its advantages, BeeWare is still growing and may not have as extensive a range of widgets or as much community support as more established frameworks.

Additionally, complex applications requiring highly specialized native features might encounter limitations.


Building a Daily Quote application with BeeWare

This will be the final application that you will create in this tutorial:

Your running BeeWare application

To build this application, the only requirement (besides BeeWare), is to have Python installed. In this tutorial, v3.11 will be used but you can also use a more recent version, downloads are available here.

You will first create a new project and activate your virtual environment to keep the BeeWare libraries separated from the system libraries (recommend). How to do that is beyond the scope of this tutorial, example can be found here.

Let's start now with building your brand new GUI application in Python.

Install BeeWare

Beware is installed similarly to other Python packages, but the recommended way is to execute:

python -m pip install briefcase

It's crucial to use python -m pip instead of just pip. This approach is necessary because Briefcase must verify that it's working with the latest versions of pip and setuptools. A simple invocation of pip alone is unable to update itself.

Starting a BeeWare project

To start a BeeWare project you execute the following command:

briefcase new

You will need to fill in a couple of questions about your project, this is the example information that I filled in:


First, we need a formal name for your application. This is the name that will
be displayed to humans whenever the name of the application is displayed. It
can have spaces and punctuation if you like, and any capitalization will be
used as you type it.

Formal Name [Hello World]: Daily Quote

Next, we need a name that can serve as a machine-readable Python package name
for your application. This name must be PEP508-compliant - that means the name
may only contain letters, numbers, hyphens and underscores; it can't contain
spaces or punctuation, and it can't start with a hyphen or underscore.

Based on your formal name, we suggest an app name of 'dailyquote',
but you can use another name if you want.

App Name [dailyquote]: dailyquote

Now we need a bundle identifier for your application. App stores need to
protect against having multiple applications with the same name; the bundle
identifier is the namespace they use to identify applications that come from
you. The bundle identifier is usually the domain name of your company or
project, in reverse order.

For example, if you are writing an application for Example Corp, whose website
is example.com, your bundle would be ``com.example``. The bundle will be
combined with your application's machine readable name to form a complete
application identifier (e.g., com.example.dailyquote).

Bundle Identifier [com.example]: blog.developer-service

Briefcase can manage projects that contain multiple applications, so we need a
Project name. If you're only planning to have one application in this
project, you can use the formal name as the project name.

Project Name [Daily Quote]: Daily Quote

Now, we need a one line description for your application.

Description [My first application]: Daily Quote application

Who do you want to be credited as the author of this application? This could be
your own name, or the name of your company you work for.

Author [Jane Developer]: Nuno Bispo

What email address should people use to contact the developers of this
application? This might be your own email address, or a generic contact address
you set up specifically for this application.

Author's Email [nuno@developer-service.blog]: developer@developer-service.io

What is the website URL for this application? If you don't have a website set
up yet, you can put in a dummy URL.

Application URL [https://developer-service.blog/dailyquote]: https://developer-service.blog

What license do you want to use for this project's code?

Select one of the following:

    [1] BSD license
    [2] MIT license
    [3] Apache Software License
    [4] GNU General Public License v2 (GPLv2)
    [5] GNU General Public License v2 or later (GPLv2+)
    [6] GNU General Public License v3 (GPLv3)
    [7] GNU General Public License v3 or later (GPLv3+)
    [8] Proprietary
    [9] Other

Project License [1]: 1

What GUI toolkit do you want to use for this project?

Select one of the following:

    [1] Toga
    [2] PySide2 (does not support iOS/Android deployment)
    [3] PySide6 (does not support iOS/Android deployment)
    [4] PursuedPyBear (does not support iOS/Android deployment)
    [5] Pygame (does not support iOS/Android deployment)
    [6] None

GUI Framework [1]: 1

Generating a new application 'Daily Quote'
Using app template: https://github.com/beeware/briefcase-template, branch v0.3.16
Using existing template (sha f6a23f4b5608307891bdc3487c39e7b3c046f185, updated Fri Nov  3 15:30:43 2023)

Application 'Daily Quote' has been generated. To run your application, type:

    cd dailyquote
    briefcase dev

Fill free to fill in your desired information, the only point to keep in mind is to choose Toga as the GUI toolkit.

Running the Application (developer mode)

To run the application (in developer mode), all you need to execute are the commands mentioned in the previous output:

cd dailyquote
briefcase dev

Keep in mind that you need to input the proper folder name in this command depending on the information that you previously provided.

You should have a running application similar to:

Your running BeeWare application

By default, BeeWare includes a default menu with default options, like this 'About' information.

Generated Application Code

How was this code generated? It was done by the briefcase new boilerplate execution:

# dailyquote/src/dailyquote/app.py

"""
Daily Quote application
"""
import toga
from toga.style import Pack
from toga.style.pack import COLUMN, ROW


class DailyQuote(toga.App):

    def startup(self):
        """
        Construct and show the Toga application.

        Usually, you would add your application to a main content box.
        We then create a main window (with a name matching the app), and
        show the main window.
        """
        main_box = toga.Box()

        self.main_window = toga.MainWindow(title=self.formal_name)
        self.main_window.content = main_box
        self.main_window.show()


def main():
    return DailyQuote()

Let's break down the key components of the code:

DailyQuote Class:

  • This class DailyQuote inherits from toga.App, making it the main application class.
  • Inside the class, there is a method startup(self) which is overridden from the parent class. This method is called when the application starts.

Startup Method:

  • The startup method is where the initial setup of the application's GUI is defined.
  • main_box = toga.Box(): This line creates a new box layout, which can be used to arrange other GUI elements. No widgets are added to this box in the current code.
  • self.main_window = toga.MainWindow(title=self.formal_name): This creates the main window for the application, with the title being the formal name of the app.
  • self.main_window.content = main_box: This sets the content of the main window to be the main_box layout created earlier.
  • self.main_window.show(): This displays the main window on the screen.

Adding a Button to the Main Window

In order to get a random daily quote and display it in the application window, you need to add widgets, in this case, a button.

You can start by adding a button:

# dailyquote/src/dailyquote/app.py

"""
Daily Quote application
"""
import toga
from toga.style import Pack
from toga.style.pack import COLUMN, ROW


class DailyQuote(toga.App):

    def startup(self):
        """
        Construct and show the Toga application.

        Usually, you would add your application to a main content box.
        We then create a main window (with a name matching the app), and
        show the main window.
        """
        main_box = toga.Box(style=Pack(direction=COLUMN))

        button = toga.Button(
            "Get Random Quote",
            on_press=self.get_random_quote,
            style=Pack(padding=5)
        )

        main_box.add(button)

        self.main_window = toga.MainWindow(title=self.formal_name)
        self.main_window.content = main_box
        self.main_window.show()

    def get_random_quote(self, widget):
        """
        Get a random quote from the API
        """
        pass


def main():
    return DailyQuote()

Let's break down the new and changed code:

  • The main_box layout was updated to have a column direction
  • A new button was added with a title, a function that is called when the button is pressed, and a style with some padding
  • A get_random_quote function was added as a placeholder for the upcoming logic

Getting and Displaying the Random Quote

To download the random quote, you will use the free API from Quotable. No API key is required and it provides a method to return a random quote.

Calling the API is as easy as using the requests library:

# dailyquote/src/dailyquote/app.py

(... previous code...)

    def get_random_quote(self, widget):
        """
        Get a random quote from the API
        """
        response = requests.get("https://api.quotable.io/quotes/random?limit=1")
        if response.status_code == 200:
            json_response = response.json()[0]
            quote = json_response["content"]
            author = json_response["author"]
            self.main_window.info_dialog(
                "Random Quote",
                f"{quote} \n- {author}"
            )

Let's break down the code:

  • response = requests.get("https://api.quotable.io/quotes/random?limit=1"):
    • This line sends an HTTP GET request to the quotable.io API to fetch a random quote. The limit=1 parameter suggests that it requests only one quote.
  • The method checks if the response's status code is 200, which indicates a successful HTTP request.
    • If the request is successful, json_response = response.json()[0] parses the JSON response and accesses the first element (the API returns a list of quotes).
  • The quote text (quote) and the author's name (author) is extracted from the JSON response.
  • self.main_window.info_dialog("Random Quote", f"{quote} \n- {author}"):
    • This line displays an information dialog box with the title "Random Quote".

Packaging and Distributing the Application

Running the application in developer mode is useful when testing and debugging, but not ideal for distribution to end users.

For that, you are going to package the application so you can then distribute it.

Packaging

The first time you package a BeeWare application, you need a couple of configuration files. Fortunately, there is a command that helps you to scaffold them:

briefcase create

This command performed:

  • Application Template Generation: Briefcase creates an application template, which includes numerous files and configurations essential for building a native installer.
  • Support Package Download and Installation: Briefcase adopts a straightforward packaging approach, incorporating a complete, standalone Python interpreter in each application it builds.
  • Installation of Application Requirements: The process includes installing any third-party modules needed at runtime. These dependencies are installed via pip directly into your application’s installer.
  • Application Code Installation: Your application’s unique code and resources, like runtime images, are copied into the installer.
  • Additional Resources Installation: Finally, the process involves adding extra resources required by the installer, such as application icons and splash screen images.

Building and Running

Now you can build the application, meaning creating the binary file with:

briefcase build

You now should have a Windows executable file in the directory build\dailyquote\windows\app\src\Daily Quote.exe.

Alternatively, you can also run the native application with:

briefcase run

But you probably got an error like this:

[dailyquote] Starting app...
===========================================================================
Log started: 2023-11-16 11:36:10Z
PreInitializing Python runtime...
PythonHome: D:\GitHub\PythonBeeWare\dailyquote\build\dailyquote\windows\app\src
PYTHONPATH:
- D:\GitHub\PythonBeeWare\dailyquote\build\dailyquote\windows\app\src\python311.zip
- D:\GitHub\PythonBeeWare\dailyquote\build\dailyquote\windows\app\src
- D:\GitHub\PythonBeeWare\dailyquote\build\dailyquote\windows\app\src\app_packages
- D:\GitHub\PythonBeeWare\dailyquote\build\dailyquote\windows\app\src\app
Configure argc/argv...
Initializing Python runtime...
Running app module: dailyquote
---------------------------------------------------------------------------
Application quit abnormally (Exit code -6)!
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "D:\GitHub\PythonBeeWare\dailyquote\build\dailyquote\windows\app\src\app\dailyquote\__main__.py", line 1, in <module>
    from dailyquote.app import main
  File "D:\GitHub\PythonBeeWare\dailyquote\build\dailyquote\windows\app\src\app\dailyquote\app.py", line 4, in <module>
    import requests
    from dailyquote.app import main
  File "\app\dailyquote\app.py", line 4, in <module>
    import requests
ModuleNotFoundError: No module named 'requests'

Since we are using a third-party library, in this case requests, you will need to add those external dependencies to the configuration file called pyproject.toml:

# pyproject.toml

...

requires = [
    "requests"
]

...

To re-read this configuration or if you have updated your source code you will need to run this command before rebuilding your application:

briefcase update -r

The extra parameter -r indicates to reload the requirements.

Now you can build and run as before with:

briefcase build
briefcase run

Which runs the native application, so you should see something like this:

Native Application Running

Installer

You could now just send the .exe file to the end users and they could run the application just fine, but providing an installer gives a more professional touch.

For that, you run:

briefcase package

Which should output:


*************************************************************************
** WARNING: No signing identity provided                               **
*************************************************************************

    Briefcase will not sign the app. To provide a signing identity,
    use the `--identity` option; or, to explicitly disable signing,
    use `--adhoc-sign`.

*************************************************************************

[dailyquote] Building MSI...
Compiling application manifest...
Compiling... done
Compiling application installer...                                                                                                                                                                           
dailyquote.wxs
dailyquote-manifest.wxs
Compiling... done
Linking application installer...                                                                                                                                                                             
Linking... done
                                                                                                                                                                                                             
[dailyquote] Packaged dist\Daily Quote-0.0.1.msi

Ignore for now the warning about signing, that is an extra step and not always necessary to provide the application to end users.

You can now provide the installer file dist\Daily Quote-0.0.1.msi to end users and they will see the Windows familiar install wizard.

Congratulations, you have just created, built, and distributed your first BeeWare application.

You can of course build much more complex applications, but this structure provides you with the building blocks for all applications.

Full source code is available at: https://github.com/DevAsService/PythonBeeWare


Conclusion

BeeWare, in combination with Python, presents an attractive framework for developing native applications across multiple platforms.

Its Pythonic approach simplifies the development process, making it an excellent choice for beginners and experienced developers alike.

While there are some limitations, the benefits of using BeeWare, especially for cross-platform development, are significant.

As the community grows and the toolset evolves, BeeWare is poised to become an even more powerful tool in the app development landscape.


Thank you for reading and I will see you on the Internet.

This post is public so feel free to share it.

If you like my free articles and would want to support my work, consider buying me a coffee: