10 minute GUI for your Prolog app

We’ll be using a tiny prolog program that can describe the number of stops on a train route, given an origin and a destination. All the source code, including the Prolog, is available on GitHub. In the Prolog, suffice to say without reading the code, we have a predicate station/1 that describes station names, and route_length/3 that given an origin and a destination can tell us the number of stops between them.

Step 1: Get SWI-Prolog Talking to Python

We’re blessed by yuce and contributors here, who provide us with pyswip, an interface between Python and Swi-Prolog. With a quick pip install pyswip we can create a Python program (in a file called trains.py) that’ll query our Prolog:

#!/usr/bin/env python3
from pyswip import Prolog

swipl = Prolog()
swipl.consult("trains.pl")

STATIONS = [ans["S"] for ans in swipl.query("station(S)")]

def route_length(From, To):
    return next(swipl.query(f"route_length({From}, {To}, L)"))["L"]

def main():
    for station in STATIONS:
        print(station)

    print(route_length(STATIONS[0], STATIONS[1]))

if __name__ == "__main__":
    main()

So we consult our trains.pl file, then we use the interface to build a list of STATIONS via a query. Then we write a proxy function route_length that will query route_length/3 in the forward direction. Finally in main we use what we’ve defined so we can observe some output: python trains.py

Step 2: Make a Command Line Application

Let’s turn this into an application, it’ll take two arguements for the origin and destination stations. Then it’ll print out the route length. To do this we add another import and change the main function.

#!/usr/bin/env python3
from argparse import ArgumentParser

from pyswip import Prolog

swipl = Prolog()
swipl.consult("trains.pl")

STATIONS = [ans["S"] for ans in swipl.query("station(S)")]

def route_length(From, To):
    return next(swipl.query(f"route_length({From}, {To}, L)"))["L"]

def main():
    parser = ArgumentParser(description="Find the length of a route between two stations")
    parser.add_argument("-o", "--origin", choices=STATIONS, required=True)
    parser.add_argument("-d", "--destination", choices=STATIONS, required=True)
    args = parser.parse_args()
    print(route_length(args.origin, args.destination))

if __name__ == "__main__":
    main()

We’re using part of Python’s standard library to handle parsing command line arguments, which makes this step relatively simple. Now we run it with python trains.py -o chicago -d denver

Step 3: GUI

Suprisingly the easiest step of all, with a pip install Gooey. Thanks to chriskiehl and contributors. All we need to do is add an import and a decorator:

    #!/usr/bin/env python3
    from argparse import ArgumentParser

    from gooey import Gooey
    from pyswip import Prolog

    swipl = Prolog()
    swipl.consult("trains.pl")

    STATIONS = [ans["S"] for ans in swipl.query("station(S)")]

    def route_length(From, To):
        return next(swipl.query(f"route_length({From}, {To}, L)"))["L"]

    @Gooey
    def main():
        parser = ArgumentParser(description="Find the length of a route between two stations")
        parser.add_argument("-o", "--origin", choices=STATIONS, required=True)
        parser.add_argument("-d", "--destination", choices=STATIONS, required=True)
        args = parser.parse_args()
        print(route_length(args.origin, args.destination))

    if __name__ == "__main__":
        main()

This we run with python trains.py, and we’re done. From SWI-Prolog to a GUI via Python in less than 10 minutes!