10 minutes to make a GUI for your SWI-Prolog App via Python

13th June 2019

SWI-Prolog is often used to make quick little applications for personal use, but what if you want to share your application with someone who is not familiar with running Prolog? How about we take advantage of Python's rich eco-system to create a GUI app in 10 minutes? Start the clock, let's go!


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 for Gooey. 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!

Step 4: Profit?! Happy coding.

Paul

Post Tags


All Tags



LIFE IS better when we share.