dotlinux blog

Packaging PyGObject Applications as .deb Packages for the Linux Desktop

PyGObject is a powerful Python binding for the GNOME stack, allowing developers to create sophisticated desktop applications with GTK, GStreamer, and other GNOME technologies. However, distributing these applications to end-users can be challenging. While Python scripts are portable, they require users to have the correct dependencies installed, which can lead to compatibility issues.

Packaging your PyGObject application as a .deb file solves this problem by providing a standardized way to distribute your software on Debian-based systems like Ubuntu, Debian, and Linux Mint. This approach ensures that all dependencies are properly handled, installation is straightforward, and your application integrates seamlessly with the system.

In this comprehensive guide, we'll walk through the entire process of creating a professional .deb package for a PyGObject application, covering everything from project structure to advanced packaging techniques.

2026-05

Table of Contents#

  1. Prerequisites
  2. Project Structure Setup
  3. Creating the Desktop Entry
  4. Building the Debian Control Files
  5. Creating the Installation Scripts
  6. Building the .deb Package
  7. Testing and Distribution
  8. Advanced Packaging Techniques
  9. Conclusion
  10. References

Prerequisites#

Before we begin, ensure you have the following tools installed:

sudo apt update
sudo apt install build-essential devscripts debhelper dh-python python3-all
sudo apt install python3-gi python3-setuptools

You should also have a basic PyGObject application ready for packaging. For this tutorial, we'll use a simple example application called "MyGtkApp".

Project Structure Setup#

A well-organized project structure is crucial for successful packaging. Here's the recommended layout:

mygtkapp/
├── src/
│   └── mygtkapp/
│       ├── __init__.py
│       ├── main.py
│       └── ui/
│           └── main_window.glade
├── data/
│   ├── icons/
│   │   └── mygtkapp.png
│   └── applications/
│       └── mygtkapp.desktop
├── debian/
│   ├── control
│   ├── rules
│   ├── changelog
│   ├── compat
│   └── install
└── setup.py

Example Application Files#

src/mygtkapp/main.py:

#!/usr/bin/env python3
 
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gio
import os
import sys
 
class MyGtkApp:
    def __init__(self):
        # Create builder and load UI file
        self.builder = Gtk.Builder()
        
        # Get the directory where the script is located
        if getattr(sys, 'frozen', False):
            # Running as bundled executable
            base_path = sys._MEIPASS
        else:
            # Running as script
            base_path = os.path.dirname(os.path.abspath(__file__))
        
        ui_file = os.path.join(base_path, 'ui', 'main_window.glade')
        self.builder.add_from_file(ui_file)
        
        # Connect signals
        self.builder.connect_signals(self)
        
        # Get main window
        self.window = self.builder.get_object("main_window")
        self.window.show_all()
    
    def on_window_destroy(self, widget, data=None):
        Gtk.main_quit()
    
    def on_button_clicked(self, widget):
        print("Button clicked!")
 
if __name__ == "__main__":
    app = MyGtkApp()
    Gtk.main()

src/mygtkapp/ui/main_window.glade:

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <requires lib="gtk+" version="3.24"/>
  <object class="GtkWindow" id="main_window">
    <property name="can_focus">False</property>
    <property name="default_width">400</property>
    <property name="default_height">300</property>
    <property name="title">My GTK Application</property>
    <signal name="destroy" handler="on_window_destroy" swapped="no"/>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <property name="spacing">10</property>
        <child>
          <object class="GtkLabel">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="label">Welcome to My GTK App!</property>
            <property name="margin">20</property>
          </object>
        </child>
        <child>
          <object class="GtkButton">
            <property name="label" translatable="yes">Click Me!</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <signal name="clicked" handler="on_button_clicked" swapped="no"/>
          </object>
        </child>
      </object>
    </child>
  </object>
</interface>

Creating the Desktop Entry#

The desktop entry file allows your application to appear in the application menu.

data/applications/mygtkapp.desktop:

[Desktop Entry]
Version=1.0
Type=Application
Name=My GTK Application
Comment=A sample PyGObject application
Exec=mygtkapp
Icon=mygtkapp
Categories=GTK;Development;
Terminal=false
StartupNotify=true

Building the Debian Control Files#

The debian/ directory contains all the metadata needed to build your package.

debian/control:

Source: mygtkapp
Section: python
Priority: optional
Maintainer: Your Name <[email protected]>
Build-Depends: debhelper-compat (= 13), dh-python, python3-all, python3-setuptools
Standards-Version: 4.6.0
Homepage: https://example.com/mygtkapp

Package: mygtkapp
Architecture: all
Depends: ${python3:Depends}, ${misc:Depends},
         python3-gi,
         gir1.2-gtk-3.0,
         python3-setuptools
Description: A sample PyGObject application
 MyGtkApp is a demonstration application showing how to package
 PyGObject applications as .deb files. It provides a simple GUI
 interface built with GTK 3.

debian/compat:

13

debian/changelog:

mygtkapp (1.0-1) unstable; urgency=medium

  * Initial release of MyGtkApp

 -- Your Name <[email protected]>  Mon, 01 Jan 2024 12:00:00 +0000

debian/rules:

#!/usr/bin/make -f
 
%:
	dh $@ --with python3 --buildsystem=pybuild
 
override_dh_auto_build:
	# Custom build steps can be added here
 
override_dh_auto_install:
	dh_auto_install
	# Install desktop file and icon
	install -d debian/mygtkapp/usr/share/applications
	install -m 644 data/applications/mygtkapp.desktop debian/mygtkapp/usr/share/applications/
	install -d debian/mygtkapp/usr/share/icons/hicolor/48x48/apps
	install -m 644 data/icons/mygtkapp.png debian/mygtkapp/usr/share/icons/hicolor/48x48/apps/

debian/install:

src/mygtkapp /usr/lib/python3/dist-packages/
data/icons/mygtkapp.png /usr/share/icons/hicolor/48x48/apps/

Creating the Installation Scripts#

setup.py:

from setuptools import setup, find_packages
import os
 
def read(fname):
    return open(os.path.join(os.path.dirname(__file__), fname)).read()
 
setup(
    name="mygtkapp",
    version="1.0",
    packages=find_packages(where="src"),
    package_dir={"": "src"},
    install_requires=[
        "PyGObject",
    ],
    entry_points={
        'console_scripts': [
            'mygtkapp=mygtkapp.main:main',
        ],
    },
    data_files=[
        ('share/applications', ['data/applications/mygtkapp.desktop']),
        ('share/icons/hicolor/48x48/apps', ['data/icons/mygtkapp.png']),
    ],
    author="Your Name",
    author_email="[email protected]",
    description="A sample PyGObject application",
    long_description=read("README.md"),
    license="GPL-3.0",
    keywords="gtk pygobject desktop",
    url="https://example.com/mygtkapp",
    classifiers=[
        "Development Status :: 4 - Beta",
        "Environment :: X11 Applications :: GTK",
        "Intended Audience :: End Users/Desktop",
        "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.8",
        "Programming Language :: Python :: 3.9",
        "Programming Language :: Python :: 3.10",
    ],
)

Building the .deb Package#

Now that all files are in place, let's build the package:

# Navigate to your project directory
cd mygtkapp
 
# Make the rules file executable
chmod +x debian/rules
 
# Build the source package
dpkg-buildpackage -uc -us
 
# Alternatively, build without signing
debuild -i -us -uc -b

After successful execution, you'll find the .deb file in the parent directory:

../mygtkapp_1.0-1_all.deb

Installing the Package#

To install your newly created package:

sudo dpkg -i mygtkapp_1.0-1_all.deb

If there are dependency issues:

sudo apt-get install -f

Testing and Distribution#

Testing the Installation#

  1. Verify files are installed correctly:

    dpkg -L mygtkapp
  2. Test the application:

    mygtkapp
  3. Check desktop integration:

    • Search for "My GTK Application" in your application menu
    • Verify the icon appears correctly

Creating a PPA (Personal Package Archive)#

For wider distribution, consider creating a PPA:

# Create source package
debuild -S -sa
 
# Upload to PPA
dput ppa:your-username/your-ppa mygtkapp_1.0-1_source.changes

Advanced Packaging Techniques#

Handling Dependencies#

For complex applications, you might need additional dependencies:

Enhanced debian/control:

Depends: ${python3:Depends}, ${misc:Depends},
         python3-gi,
         gir1.2-gtk-3.0,
         gir1.2-gstreamer-1.0,
         gir1.2-webkit2-4.0,
         python3-cairo,
         python3-setuptools
Suggests: gir1.2-gtksource-3.0

Creating Systemd Services#

If your application needs to run as a service:

debian/mygtkapp.service:

[Unit]
Description=My GTK Application Service
After=network.target
 
[Service]
Type=simple
User=mygtkapp
ExecStart=/usr/bin/mygtkapp-service
Restart=on-failure
 
[Install]
WantedBy=multi-user.target

Using dh-virtualenv for Virtual Environment Packaging#

For applications with complex Python dependencies:

sudo apt install dh-virtualenv

Modified debian/rules:

#!/usr/bin/make -f
 
%:
	dh $@ --with python-virtualenv

Conclusion#

Packaging PyGObject applications as .deb files significantly improves the user experience by providing a standardized installation method. While the initial setup requires careful attention to detail, the result is a professional-grade distribution package that integrates seamlessly with Debian-based systems.

Remember to:

  • Test your package on clean systems
  • Keep dependencies up to date
  • Follow Debian packaging guidelines
  • Use version control for your packaging files

With these techniques, you can distribute your PyGObject applications confidently, knowing they'll work reliably for your users.

References#

  1. Debian Policy Manual
  2. Debian New Maintainers' Guide
  3. Python Packaging Authority
  4. PyGObject Documentation
  5. GTK Documentation
  6. Desktop Entry Specification

Useful Tools and Commands#

# Check package contents
dpkg -c package.deb
 
# Extract package without installing
dpkg -x package.deb extraction_dir/
 
# Check package metadata
dpkg -I package.deb
 
# Lintian - Debian package checker
sudo apt install lintian
lintian package.deb

By following this comprehensive guide, you'll be well-equipped to package your PyGObject applications professionally and distribute them to users on Debian-based Linux distributions.