subscribe
Tags:
 
2010
2009
December
November
October
September
August
July
June
May
April
March
February
January
2008
2010-06-24

The Need


Having recently implemented parsing of Markdown code in my blog, and deciding to write most, if not all, of my future documentation in Markdown, what I really needed, was a way to do the following:

  1. write markdown code in a spellchecking text editor
  2. convert the markdown code to HTML
  3. preview the HTML as it would appear in a browser

Simple enough, but unfortunately, all of the solutions that I found were online forms. I wanted something that I could run on my own machine.

The Solution


As is often the case, the solution was to write a quick little application. For this application, I chose to use Python due to the readily available Python Markdown module as well as my "quick/dirty" need and recent Python usage on various projects.

The application uses GTK for the graphical interface, and webkit for HTML rendering.

Without further ado ...
Enter the Python

#!/usr/bin/env python 
''' 
Hey, this is GPLv3 and copyright 2010 Jezra 
''' 
import gtk 
import webkit 
import markdown #http://www.freewisdom.org/projects/python-markdown/ 
global default_file 
default_file = "markdown.txt" 
try: 
    import gtkspell 
    has_gtkspell=True 
except: 
    has_gtkspell=False 
     
class application: 
    def `__init__`(self): 
        #build the UI 
        winder = gtk.Window(gtk.WINDOW_TOPLEVEL) 
        winder.connect("destroy",self.quit) 
        winder.maximize() 
        #add some accellerators 
        #create an accellerator group for this window 
        accel = gtk.AccelGroup() 
        #add the ctrl+q to quit 
        accel.connect_group(gtk.keysyms.q, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE, self.quit_accel ) 
        #add ctrl+s to save 
        accel.connect_group(gtk.keysyms.s, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE, self.save_accel ) 
        #lock the group 
        accel.lock() 
        #add the group to the window 
        winder.add_accel_group(accel) 
        #we need a paned 
        pane = gtk.HPaned() 
        #and a vbox 
        box = gtk.VBox(False) 
        #add the pane to the box 
        box.pack_start(pane) 
        #add the box to the window 
        winder.add(box) 
        #do the text crap for the first pane 
        self.tb = gtk.TextBuffer() 
        tv = gtk.TextView(self.tb) 
        tv.set_wrap_mode(gtk.WRAP_WORD) 
        #try and add spell checking to the textview 
        if has_gtkspell: 
            #what if there is no aspell library? 
            try: 
                self.spell = gtkspell.Spell(tv) 
                self.has_spell_library=True 
            except Exception: 
                #bummer 
                self.has_spell_library=False 
                print Exception.message 
        #add the text view to a scrollable window 
        input_scroll = gtk.ScrolledWindow() 
        input_scroll.add_with_viewport(tv) 
        input_scroll.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC) 
        #add to the pane 
        pane.pack1(input_scroll,True) 
        #make the HTML viewable area 
        self.wv = webkit.WebView() 
        #disable the plugins for the webview 
        ws = self.wv.get_settings() 
        ws.set_property('enable-plugins',False) 
        self.wv.set_settings(ws) 
        out_scroll = gtk.ScrolledWindow() 
        out_scroll.add(self.wv) 
        out_scroll.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC) 
        pane.add2(out_scroll) 
        #we will add buttons at the bottom 
        bbox = gtk.HButtonBox() 
        bbox.set_layout(gtk.BUTTONBOX_START) 
        #we need buttons! 
        savebutton = gtk.Button("Save") 
        savebutton.connect("clicked", self.save) 
        markdownbutton = gtk.Button("_Markdown",use_underline=True) 
        markdownbutton.connect("clicked", self.markdown) 
        #add the buttons to the bbox 
        bbox.pack_start(savebutton,False,False,0) 
        bbox.pack_start(markdownbutton,False,False,0) 
        #add the bbox to the box 
        box.pack_start(bbox,False,False,0) 
        winder.show_all() 
        self.read_default_file() 
    def run(self): 
        gtk.main() 
    def quit(self,widget=None): 
        self.save() 
        gtk.main_quit() 
    def markdown(self,widget): 
        text = self.get_buffer_text() 
        mdtext = markdown.markdown(text) 
        self.wv.load_html_string(mdtext,"file:///") 
    def read_default_file(self): 
        try: 
            f = open(default_file,"r") 
            text = f.read() 
            self.tb.set_text(text) 
            f.close() 
        except: 
            pass 
    def quit_accel(self,accel_group, acceleratable, keyval, modifier): 
        self.quit() 
    def save_accel(self,accel_group, acceleratable, keyval, modifier): 
        self.save() 
    def save(self,widget=None): 
        #get the text 
        text = self.get_buffer_text() 
        f = open(default_file,"w") 
        f.write(text) 
        f.close() 
    def get_buffer_text(self): 
        start_iter = self.tb.get_start_iter() 
        end_iter = self.tb.get_end_iter() 
        text=self.tb.get_text(start_iter,end_iter) 
        return text 
if `__name__`=="`__main__`": 
    a = application() 
    a.run() 

You can download markdowner.tar.gz if you have problems copying the code.

Usage

  • Ctrl+q quits the application
  • Ctrl+s saves the current document
  • Alt+m marks down the text and displays the HTML in the right pane

Note: the 'save' location is hard coded to be "markdown.txt" in the directory where the code is run from.

Now quit reading, and go markdown some text.

Comments
Jon Kulp:
Jezra, this is brilliant!! I use Markdown for all my course syllabi and some other stuff and this is a very cool preview mechanism for it. I normally use vim and then just run a shell script that converts it to html and displays in browser. I'll definitely be using this thing, man! Feature request: syntax highlighting! I have this in vim thanks to some guy who put together a syntax file and shared it on the web.
jezra:
I only *start* the conversation... I mean code. It is up to others to add features. Well, this may turn into a project, and if so, you will be able to add your feature request to whichever source control system get used.
Name:
not required
Email:
not required (will not be displayed)
Website:
not required (will link your name to your site)
Comment:
required
Please do not post HTML code or bbcode unless you want it to show up as code in your post. (or if you are a blog spammer, in which case, you probably aren't reading this anyway).
Prove you are human by solving a math problem! I'm sorry, but due to an increase of blog spam, I've had to implement a CAPTCHA.
Problem:
3 minus 3
Answer:
required
  • Tags:
  • Python