MacDevCenter    
 Published on MacDevCenter (http://www.macdevcenter.com/)
 See this if you're having trouble printing code examples


Build Your Own Blogging Application, Part 1

by Matthew Russell
11/09/2004

This article is the first in a two-part series that illustrates how to harness the power of Perl and Tcl/Tk along with your .Mac iDisk to build your own blogging application. You'll also learn about XHTML and bash scripting along the way.

If you've never worked with Perl, Tcl/Tk, or XHTML before, don't fret. Getting familiar is fun and will make you a more informed connoisseur of the fine development tools available for Mac OS X. Plus, for this series, no previous knowledge is required.

If you're not interested in these tools, then read along anyway and use those of your choice. The concepts used to build the blogging app are general enough to apply to Python and Ruby, amongst the gamut of others. With that said, let's get acquainted with Tcl/Tk and XHTML -- the basic tools we need to build the front end.

A Very Brief Intro to Tcl/Tk

Tcl stands for "Tool Command Language." Like its relatives Perl, Ruby, and Python, it's a freely available scripting language designed for ease of rapid prototyping and for gluing together pieces of code written in languages such as C++ and Java.

Editor's Note -- We have some related Tcl articles on MacDevCenter.com that you might want to search for, including those by Michael Norton and Christopher Roach.

It's handy comrade, Tk, is a toolkit extension that allows you to develop nice graphical user interfaces (GUIs) with very little effort using widgets. Professor John Ousterhout developed Tcl/Tk in the 1980s at Berkeley, and it's been a hit ever since. In order to develop GUIs with Tcl's Tk extensions, you'll need the Wish Shell. Wish is an abbreviation for 'windowing shell.' The Aqua port is available at apple.com if you don't already have it.

One of the most common problems when working in the terminal is having paths set up correctly, so make sure that the command wish is linked to your Aqua port by typing which wish in your shell. If you've accepted the default installation settings you should get back /usr/bin/wish8.4 or something very similar. If you get back something like /sw/bin/wish, your installation may be pointing to Fink's X Windows installation. You'll need to specify absolute paths or change your path to point to the Aqua ported Wish Shell unless you like the X Windows appearance.

Before building a GUI, you might be interested to know that the basic syntax of a Tcl command is very simple. Constructs are expressed as commands with arguments in the following form:

command argument1 argument2 ... argumentN

For example, the most famous of all programs is as simple as saving the following text into a file named helloWorld.tcl using your editor of choice, and then typing wish helloWorld.tcl in your terminal, while in the same directory. Use any editor, but an editor like Vim with syntax highlighting enhances productivity by signaling syntax errors.

button .helloWorld -text "Hello..." \
	-command {puts stdout "...World!"}
pack .helloWorld -padx 10 -pady 10

In only two lines of code, you've cooked up a GUI to display a "Hello..." button and instructed the button widget to display "...World!" in your terminal window with each click. In case you're thinking I've miscounted the number of lines, note that backslashes break across multiple lines, just like they do in a terminal window or a makefile. This concept is particularly important because commands can have many arguments.

In order to produce readable Tcl/Tk code, you should display it as clearly as possible. Breaking multiple arguments across lines and indenting helps that to happen. Also, note that there cannot be a space character after the backslash. If there is a space, you may not notice it, but the interpreter will hiss at you and your code won't run. If you have an error around a backslash, check for the trailing space.

You might also be interested to know that the dot prefixed to the name of the button widget is mandatory. Tk's naming convention defines widgets in relation to one another. The main window of the application is specified by just a dot. Widgets in the main widow has a dot prefix; widgets inside widgets have a prefix specifying their container widgets, and so forth. In a moment you'll see some code that references the main window explicitly with a dot to give the window a title and some other examples of this syntax.


Hello World!

The button command should be fairly self-explanatory. The -text argument grouping designates the text displayed on the button, and the -command argument grouping specifies what to do when the button is clicked.

Instead of using puts to display text to standard output, we could have specified other actions such as a procedure to execute. Finally, the command pack specifies that the pack geometry manager should arrange the button widget inside the window.

Obviously, we need more than a button for our blogging app, so it's time to make some choices. The relevant Tk widgets for building a minimally functional blogging app include the frame, text, scrollbar, menubutton, menu, and dialog tk_getOpenFile widgets. Each accomplishes what it sounds like, and you can learn more by reading the manual pages for any of them in your terminal. For example, type man scrollbar to read the documentation on the scrollbar widget or type man pack to learn more about the pack geometry manager.

XHTML: A Better HTML

Before eagerly hammering away at the keyboard with your newly acquired knowledge of Tk widgets, you must determine how to display the content to your internet audience. We already know that plain vanilla text is a sore to the eye by today's web standards, so let's decide to use some of the basic markups from XHTML, like making text bold or italic. Using XHTML to mark up a document is easy, but a short summary of a few constructs is in order.

HTML stands for HyperText Markup Language. The text you read on the web is hypertext, and it is "marked up" because tags are placed around it to designate its presentation. For example, enclosing text inside the "emphasis" tags like <em>some text</em> tells the browser to render it as emphasized (usually in italics). Most of the markups for HTML are simple and learning how to use them usually generalizes to several others. If you right click on any HTML document you can choose to view its source and find all kinds of goodies.

'But why the X prefix on HTML?' you ponder. XHTML stands for eXtensible HyperText Markup Language and is a stricter and cleaner HTML. XHTML is an application of XML (eXtensible Markup Language). To make the long story short, the web reached a point where many documents were not "well formed." Prime examples of not being well-formed are when an ending tag is forgotten or not matched, when tags are not entirely in lowercase letters, and when singleton tags like <br> don't explicitly indicate that they're singletons.

For example, the XHTML tag for breaking a line is <br />, not just <br>. The latter doesn't explicitly indicate that there is no closing tag. Most browsers can still work around these problems, but working around problems instead of fixing problems leads to unwanted complexity and time wasted on the workarounds. A nice introduction about what is required of well-formed and valid XML documents is at xml.com.

Conveniently, XML is an already-existing language designed to specify content (not presentation, like HTML). Therefore, if HTML is designed using XML syntax rule like the ones just described, we're able to enforce a grammar that preserves the well-formed structure and more easily lends itself to extension. This nice alternative to what we think of as HTML is called XHTML. As you just learned, XHTML is an XML application, although XHTML is almost identical to HTML 4.01.

Why does this all matter? Because when designing content for the web, you must be careful to follow the official specifications so that as many browsers can display it as correctly as possible. You also enable search agents to more accurately classify the content of your page. By conforming to XHTML standards, we strive to fulfill this objective. The XHTML Reference 1.0 can be found at w3schools.com if you're interested in reviewing more of the details. Although our simple blogging app won't fully unleash the power of XHTML, it's an important design decision to make up front. Next time you're trying to make conversation, just ask someone, "So what's the latest thing you've done with XHTML (emphasizing the X)," and take note of the blank stare you might get back.

Building the Front End

Understanding Tcl/Tk and XHTML now empowers us to actually build the front end for our blogging app. We know that at a minimum, our GUI should allow the user to write entries to the blog app and exit. Since we'd like to be able to use XHTML with as little effort as possible, a menu with basic markups is also a nice convenience as well as the ability to insert images into our blog. Interestingly, the steps involved in accomplishing these basic tasks generalizes to virtually any XHTML markup we'd ever want to use. Now that you're armed with the tools to build this front end, let's get our hands dirty.

In your favorite editor, copy the following lines of code.


################################################
#Matthew Russell 30 Sep 04
################################################
#A Tcl/Tk front end for a blogging application
#that will write XHTML to the iDisk.
#This script relies on script updateBlog.sh
################################################

#create & title a frame for the widgets
frame .fr -width 10c -height 5c
wm title . "Blogging App v0.1"
pack .fr

#configure menubar on top
frame .menubar -relief raised -bd 2
pack .menubar -in .fr -fill x

#put a text widget (our editor) with y scroll
text .fr.ed -width 80 -height 20 -bg white \
	-yscrollcommand ".fr.scroll set" \
	-wrap word
scrollbar .fr.scroll -command ".fr.ed yview" 
pack .fr.scroll -side right -fill y
pack .fr.ed -side left -fill both -expand true
pack .fr -side top -fill both -expand true

#create a file menu
menubutton .menubar.file -text File \
	-underline 0 \
	-menu .menubar.file.menu

menu .menubar.file.menu

#items in file menu
.menubar.file.menu add command \
	-label "Write To Blog" \
	-command WriteToBlog 
.menubar.file.menu add command \
	-label "Write To Blog and Quit" \
	-command WriteToBlogQuit 
.menubar.file.menu add command \
	-label "Quit" \
	-command exit

#create a markup menu button
menubutton .menubar.markup -text Markup \
	-underline 0 \
	-menu .menubar.markup.menu

menu .menubar.markup.menu

#items in the markup menu
.menubar.markup.menu add command \
	-label Bold \
	-command {MarkupSelection "strong"}
.menubar.markup.menu add command \
	-label Italic \
	-command {MarkupSelection "em"}

#create a menu for inserting images
menubutton .menubar.insertImage \
	-text "Insert Image" \
	-underline 0 \
	-menu .menubar.insertImage.menu

menu .menubar.insertImage.menu

#item in the image menu
.menubar.insertImage.menu add command \
	-label "Find Image" \
	-command InsertImageFile

pack .menubar.file -side left
pack .menubar.markup -side left
pack .menubar.insertImage -side left
pack .menubar -side top -fill x

#start the focus in the editor 
focus .fr.ed

#a clean way of displaying error messages
proc ShowMessage {mess} {

	toplevel .messpop \
		-width 10c \
		-height 4c

	grab .messpop
	wm title .messpop "Warning"

	message .messpop.msg \
		-relief raised \
		-bd 2 \
		-text $mess

	button .messpop.okb \
		-text OK \
        -command {destroy .messpop ; 
				  return 0}

	pack .messpop.msg .messpop.okb -side top 
}

# place markup tags around the selection
proc MarkupSelection {markupTag} {
	set theSel [selection get STRING]
	set selLen [string length $theSel]

#if true, then the selection done right to left
if {[string equal \
	$theSel \
	[.fr.ed get insert "insert +$selLen chars"]]} {
	.fr.ed delete \
		insert "insert + $selLen chars"
	.fr.ed insert \
		insert "<$markupTag>$theSel</$markupTag>"
} else {
	.fr.ed delete \
		"insert -$selLen chars" insert
	.fr.ed insert \
		insert "<$markupTag>$theSel</$markupTag>"
}

}

#Write the blog entry to file 
#Then execute a script to post the entry
proc WriteToBlog {} {
	set err [catch {set f [open "newentry" w]} \
		errormessage]
	if {$err == 0} {
		puts $f [.fr.ed get 1.0 end]
		close $f
		#the shell script that updates the blog
		exec ./updateBlog.sh
	} else {
		set ok [ShowMessage "No filename given"]
		}
} 


proc InsertImageFile {} {
	set typelist {
   		{"All Files" {*}}
   		{"GIF Image" {".gif"}}
   		{"JPEG Image" {".jpg"}}
	}

	global env
	set fullName \
	[tk_getOpenFile -title "Find Image File" \
					-initialdir $env(HOME) \
			 		-filetypes $typelist]

#use the full filename for the tag in the editor
#abbreviate it once it's copied to the iDisk
.fr.ed insert \
	   insert \
"<img src='$fullName' width= height= />"
}

proc WriteToBlogQuit {} {
	WriteToBlog
	exit
}

In order to fire up the editor, type wish editor.tcl into your terminal window, while in the same directory as the file you just copied. Most of the front end should be fairly straightforward, based on our previous discussion of Tcl/Tk. We set up a frame, title it, and then add in a menubar. Once that's done we add in the actual menus themselves and define their actions. We also insert a text widget that allows us to enter our writing. That's all mechanical enough. The interesting parts of the editor involve what the menu choices actually accomplish.


Your nice-looking editor that was almost too easy to make.

Let's start with the markup menu. Following good software design principles, you and I have produced some procedures to accomplish functionality based upon varying parameters.

In the editor, procedure MarkupSelection handles marking up the selection based upon a parameter that indicates the content of the tag. There's a bit of a nuisance you have to work around when dealing with selection in Tcl/Tk. What you can find out easily is where the insertion cursor is located and the length of the selection. What you have to do some detective work to discover is whether the act of selecting the text was accomplished right to left or left to right. If selection was accomplished left to right, then we'd just delete the characters from the insertion cursor (at the right of the selection) back to the beginning of the selection. If it was accomplished right to left, then we'd delete characters from the insertion cursor (at the left end of the selection) to the end of the selection.

Fortunately, we can compare the selection itself to sequences of characters defined relatively to the insertion cursor to determine which way the selection occurred. (Say that three times as fast as you can.) Based on one of these conditions, the procedure just sends an insert and delete command to the editor along with a range of characters for its arguments.

Next, let's have a look at the image menu. To insert an image, a procedure executes that displays a dialog box to the user. The user navigates through their directory, finds the image to insert, and when the user clicks OK, the tag specifying the location of the image gets inserted.

The actual process of rendering the image is a job for the viewer of the blog's browser once the entry is posted to the web. We'll work through the process of copying the image file to the iDisk and changing the absolute path in the image tag to one relative to the iDisk's directory structure in the next article. For now, trust that we're doing the right thing here. The interesting part of the InsertImageFile procedure involves the special variable env. global is a special, top-level scope that is outside of any procedure.

In order to get the values of our environment variables, we can declare a special global variable env and use it to access any variable obtained by typing env in our terminal window. We'll use the $HOME environment variable to specify a suitable location for directory browsing. A final detail to notice is that the dialog box will return paths that contain space characters. For now, this is fine, but keep in mind that we'll need to account for it when we actually try to copy the file to the iDisk later.

Before closing today's work, let's examine the WriteToBlog procedure. You'll notice that there's a line that references a shell script that you haven't written yet. The script involves several exciting new tools and concepts, so we need to save that one for next time.

For now, take note that the script exists and will ensure the grunt work of updating the blog that takes place at the back end. We can still understand the rest of the procedure, however, without knowing the details of this script. The idea is that we take the information stored in the text widget and store it to a file entitled newentry. The updateBlog.sh script uses this file to do its work. There's also a bit of basic error-checking we do to make sure the file opens successfully. Notice that getting the contents of the text editor is as simple as using the get command as an argument to the text widget and specifying that we want all of the text.

For Next Time

Consider implementing a markup for inserting links into your blog entry and review procedures ShowMessage and WriteToBlogQuit. There are many options, but think about the pros and cons of your choices before making a design decision.

You might also look into XHTML's specification for aligning text. Using the existing editor's functionality and the XHTML reference at w3schools.com, you should have all of the technical prowess you need to accomplish this task.

You'll find that a little bit of knowledge goes a long way in an app like this one. If you're really ambitious, dig into the text widget's tagging capabilities and try to add basic syntax highlighting for the XHTML commands you've included in your editor. We can only do so much here, but given sufficient time and effort, you can create truly amazing applications with Tcl/Tk. The best way to learn is to practice!

In the next lesson, you'll learn how to write the scripts we need to make procedure WriteToBlog functional, how to copy the image files from our local disk to our iDisk, and how to wrap some of that into a bash script that allows a measure of configurability. Once that's done, your blog will be ready for the world. The question is whether the world will be ready for your blog.

Matthew Russell is a computer scientist from middle Tennessee; and serves Digital Reasoning Systems as the Director of Advanced Technology. Hacking and writing are two activities essential to his renaissance man regimen.


Return to MacDevCenter.com.

Copyright © 2009 O'Reilly Media, Inc.