Prolog 4/4: GUI

Prolog 4/4: GUI

The Plan

I will write a GUI for last week’s RSS reader in order to explore how to write a GUI in a logic based language using Prolog’s XPCE toolkit.

The XPCE toolkit is:

  • An IDE for SWI-Prolog.
  • A library for writing GUI in Prolog.
  • An object layer for Prolog.

What to Expect

When this article is complete:

  • You will have:
    • A GUI front end for the RSS reader from last week
  • You will know:
    • How to use SWIPL-Win.
    • How to write a GUI in Prolog.

Files Used in this Project

  • rssGui.txt: The GUI code. (Change the extension from txt to pl)
  • rss.txt: The original RSS reader file, used here as a library. (Change the extension from txt to pl)
  • feeds.txt: Sample RSS feed config file.

The Code

Compiling the Code… More or Less

I discovered that compiling GUI code for XPCE is easier said than done. I made several attempts, read a couple of tutorials and got nowhere. If there are any Prolog guru’s reading who wouldd like to set me straight, please leave a comment.

On the other hand, running the code on the is pretty easy:

  1. Download the files for this project and re-name them as described above.
  2. Double click on “”
  3. In the SWIPL window, type, rssGui.

At that point, you should have a window with the RSS reader in it.

Loading Libraries And Other Activities

:- consult(rss).
:- pce_autoload(finder, library(find_file)).
:- pce_global(@finder, new(finder)).

To load the rss library from last week, tell Prolog to consult it. The two lines afterward load the file finder into memory. This makes it possible to open a file finder to select the rss config file.

Laying Out the GUI, and Adding Hooks

rssGui :-
  new(Frame, frame('RSS Reader')),
  new(RssFeeds, browser),
  send(Frame, append(RssFeeds)),
  send(new(NewsList, browser(news)), right, RssFeeds),
  send(RssFeeds, select_message, message(@prolog, newsDisplay, RssFeeds, NewsList)),
  send(new(Buttons, dialog), below(RssFeeds)),
  send(Buttons, append(button(load, message(@prolog, load, RssFeeds)))),
  send(Frame, open).

XPCE makes it possible to access objects in Prolog by adding three predicates:

  • new: Instantiates a new object.
  • send: Sends information to the object.
  • get: Gets information from an object.

To make a new GUI window, first a frame is created(Line 2) which contains all the GUI elements.
Lines 3 and 4 create a List Box to contain the channel names and adds it to the Frame.
Line 5 adds a List Box to the right side of the channel list box for displaying news titles.
Line 6 adds an action so that when a value is selected in RssFeeds, the newsDisplay predicate is fired with RssFeeds and NewsList for arguments.
Line 7 adds a container for a button. Line 8 adds a button to the dialog which has the text “load” and fires load when clicked.
Line 9 opens the frame.

Read the Config and Load the RSS Feeds

load(Browser) :-
  get(@finder, file, exists := @on, FileName),
  readConfig(FileName, URLs),
  send(Browser, clear),
  foreach(member(URL, URLs), readAndListChannels(Browser, URL)).

Line 2 opens up a standard file dialog and unifies the selected file with FileName, from there the code is much like the display code from last week.

I used a separate predicate to write each of the URLs to avoid potential logical contradictions in the foreach.

Fetch the Feed and Add it to the RssFeeds Browser

readAndListChannels(Browser, URL) :-
  catch((rssFetch(URL, XML),
        rssRead(XML, RSS),
        foreach(member(channel(Name, News), RSS), 
          send(Browser, append, create(dict_item, Name, Name, News)))), 
      readAndListChannels(Browser, URL)).

Again, much of this is similar to the code to display the feeds on the command line. The only real change is in Line 5, where the data from each channel is wrapped into a dictionary item with:

  1. identifier: Name
  2. key value: Name
  3. object: News

As before, this will keep trying until it succeeds or blows the stack.

Display the News Titles in the NewsList Browser

newsDisplay(RssFeeds, NewsList) :-
  get(RssFeeds, selection, Channel),
  get(Channel, object, News), 
  get(News, size, SizeVal),
  send(NewsList, clear),
  foreach(between(1, SizeVal, LineNumber), 
      sendVector(NewsList, News, LineNumber)).

Line 2 unifies Channel with the currently selected item in RssFeeds, the dictionary item from readAndListChannels.
Line 3 pulls the object from the dictionary item, a vector.
Line 4 determines the length of the vector.
Line 5 clears the NewsList so we don’t just keep adding to the end.
Line 6-7 iterates over each of the values in the vector and appends them to NewsList with sendVector.

Append a Cell from a Vector to a Browser

sendVector(Browser, Vector, Index) :-
  get(Vector, element, Index, Value),
  send(Browser, append, Value).

Given a Browser, Vector and Index, sendVector pulls the Indexth element from the Vector and appends it to the Browser.

Final Summary

Prolog’s logic based semantics make for an interesting programming experience. Up to this point I had trouble imagining how you’d deal with dynamic situations, like user input or a file being read in, in context to a language that is based on logic. Now that I’ve spent a month working with it, it’s an incredibly elegant way to solve problems. That being said, I think I’m going to stick with Python for my day to day scripting needs.

Coming Up

Next week is a fifth Monday, so I’ll be working with a tool, Robot Framework. Robot Framework is a Python based glue language for writing test cases in a clear format which is readable to non-technical users.

In February, I’ll be working with Squeak. Squeak several neat features, it is:

  • generally considered the first full object oriented language
  • where unit tests frameworks came from (in the form that most of us are used to today)
  • contained entirely in its own design environment
  • designed to be useful as an education language
  • host to Seaside a powerful web framework