Slide presentations in reStructuredText -> S5 -> PDF

16 03 2010

Let’s say you want to create a slide presentation and you are not very much into presentation software.

S5 is a good enough HTML based alternative for a slideshow presentation. reStructuredText and rst2s5 can free you from writing HTML yourself…

S5 can generate a "printer-friendly" version of your slides. But I was really missing a way to create a PDF version of my slides to ease its distribution. I finally found a tool that could handle that Prince.

Example (slides.rst):

.. include:: <s5defs.txt>

======================================================================
reStructuredText to PDF
======================================================================

in 2 easy steps

:Author: Eduardo Schettino


(1) rst2s5
=======================

rst => s5

::

  rst2s5 --theme=small-white slides.rst slides.html


(2) prince
=======================

s5 => PDF

::

  prince --media projection -s page.css slides.html -o slides.pdf

Where page.css controls the PDF page size:

@page { size: 1280px 800px }

Caveats

  • Prince is not OpenSource though it provides a free license for non-commercial user.
  • Page footer is displayed only on first page.
  • Some CSS tweaking might be necessary depending on your theme.




inotify & text editors (emacs, vim, …)

7 03 2010

This post describes how to get notifications on file modifications made through a text editor. Using python on linux (ubuntu).

I am working on adding some inotify goodness to doit. For that I want to receive one, and one only, notification every time a file is modified. Inotify makes the hard work of watching the file system and Pyinotify provides a python interface. But using it was not straight-forward as I expected. The problem is that editors manipulate files on its own ways…

I have started with a modified example from Pyinotify tutorial to watch for file modifications. The IN_MODIFY event looked like perfect to me, it is an event that is called when “file was modified”.

It worked fine when I used “echo”. But than when I tried with Emacs I got 3 notifications. With VIM it was even worst, I got no notifications and an error message!

[Pyinotify ERROR] The pathname ‘notify.py’ of this watch <Watch wd=1 mask=4095 auto_add=False proc_fun=None path=notify.py exclude_filter=<function at 0x257e398> dir=False > has probably changed and couldn’t be updated, so it cannot be trusted anymore. To fix this error move directories/files only between watched parents directories, in this case e.g. put a watch on ‘.’.

The error message is pretty clear, I should watch the folder containing the file… In order to understand what was going on I came up with the following code. It watch for all inotify events on file’s folder:

import os.path
import pyinotify

class EventHandler(pyinotify.ProcessEvent):
    def process_default(self, event):
        print "==> ", event.maskname, ": ", event.pathname


wm = pyinotify.WatchManager()  # Watch Manager
#mask = pyinotify.IN_MODIFY
mask = pyinotify.ALL_EVENTS
ev = EventHandler()
notifier = pyinotify.Notifier(wm, ev)

for watch_file in ['notify.py']:
    watch_dir = os.path.dirname(os.path.abspath(watch_file))
    wm.add_watch(watch_dir, mask)

notifier.loop()

I got the following results doing modifications on the file with echo, emacs and vim:

  • echo a >> notify.py
  • ==> IN_OPEN : /my_folder/notify.py
    ==> IN_MODIFY : /my_folder/notify.py
    ==> IN_CLOSE_WRITE : /my_folder/notify.py

    Exactly what I expected :)

  • emacs (C-x C-s)
  • ==> IN_CREATE : /my_folder/.#notify.py
    ==> IN_MODIFY : /my_folder/notify.py
    ==> IN_OPEN : /my_folder/notify.py
    ==> IN_MODIFY : /my_folder/notify.py
    ==> IN_CLOSE_WRITE : /my_folder/notify.py
    ==> IN_DELETE : /my_folder/.#notify.py

    I am not trying to understand emacs internals… but I can notice that IN_CLOSE_WRITE raised just one event.

  • vim (:w)
  • ==> IN_MODIFY : /my_folder/.notify.py.swp
    ==> IN_CREATE : /my_folder/4913
    ==> IN_OPEN : /my_folder/4913
    ==> IN_ATTRIB : /my_folder/4913
    ==> IN_CLOSE_WRITE : /my_folder/4913
    ==> IN_DELETE : /my_folder/4913
    ==> IN_MOVED_FROM : /my_folder/notify.py
    ==> IN_MOVED_TO : /my_folder/notify.py~
    ==> IN_CREATE : /my_folder/notify.py
    ==> IN_OPEN : /my_folder/notify.py
    ==> IN_MODIFY : /my_folder/notify.py
    ==> IN_CLOSE_WRITE : /my_folder/notify.py
    ==> IN_ATTRIB : /my_folder/notify.py
    ==> IN_MODIFY : /my_folder/.notify.py.swp
    ==> IN_DELETE : /my_folder/notify.py~
    ==> IN_CLOSE_WRITE : /my_folder/.notify.py.swp
    ==> IN_DELETE : /my_folder/.notify.py.swp

    It seems Vim do not modify the file directly. It just move/replaces the original one with the edited through a “swap file”. IN_CLOSE_WRITE was called for the target file only once…

I played around a bit more and it is clear that IN_CLOSE_WRITE is what I was really looking for. In order to deal with VIM I should watch the folder and not the file itself. Putting all pieces together I got this code:

import os.path
import pyinotify

class FileModifyWatcher(object):

    def __init__(self, file_list):
        self.file_list = set([os.path.abspath(f) for f in file_list])
        self.watch_dirs = set([os.path.dirname(f) for f in self.file_list])

    def handle_event(self, event):
        if event.pathname in self.file_list:
            print "==> ", event.maskname, ": ", event.pathname

    def loop(self):
        handle_event = self.handle_event
        class EventHandler(pyinotify.ProcessEvent):
            def process_default(self, event):
                handle_event(event)

        wm = pyinotify.WatchManager()  # Watch Manager
        mask = pyinotify.IN_CLOSE_WRITE
        ev = EventHandler()
        notifier = pyinotify.Notifier(wm, ev)

        for watch_this in self.watch_dirs:
            wm.add_watch(watch_this, mask)

        notifier.loop()

if __name__ == "__main__":
    fw = FileModifyWatcher(['notify.py'])
    fw.loop()

To use this, subclass FileModifyWatcher, over-write handle_event. Just pass the list of files to watch to its constructor and thats it ;)