« Using ASS to Access FileMaker

Posted by Andy Monitzer on January 16, 2002 [Feedback (2) & TrackBack (0)]

First, ASS is more a work-in-progress than a mature product, but since I'm a notorious early adaptor (using Mac OS X since DP3), I couldn't refuse to experiment a bit with it.

 

// What We're Going To Do

Well, one blank spot in the field of Cocoa is database access. Apple removed EOF (the database framework for Cocoa) before 10.0, basically telling all application-developers that they don't need databases anyways [OK, I'll stop here :-)].

Anyways, I've got a (licenced) copy of FileMaker Pro 5.5 lying around here, so I thought "Hey, FileMaker has an AppleScript interface!" and began to think about a small project...

I'm asthmatic, so I need to keep a medical record of "peakflow" values (basically describing how well I'm breathing currently), which I've got to measure twice per day and report to my M.D. about once per month. I began that task like everybody else, in a booklet, drawing the curve manually. When that booklet was full, I thought "WTF - I've got a computer!" and began to look for software that would do the management and curve drawing for me - and only found one Access database (currently offline) and some commercial solutions for Windows - not quite what I was looking for.

However, FileMaker (when adding a good chart drawing app like RagTime) is very well suited for this kind of task. Been there, done that. It works very well, but one thing is missing: a nice customized interface for it.

I never thought about doing that in Cocoa/Objective-C, because I just was too lazy to get into developing for FileMaker. But the big plus of AppleScript is that nothing you can do is complicated (you just can't do everything ;-). So, we're going to write a Cocoa-interface for my database in AppleScript!

 

// The Database

I've got a mature database design, which was developed over a significant amount of time and takes a specialist's feedback into account. I've named it "Peakflow" (I expect you to know how to handle FileMaker):

Field NameTypeOptions
DateDateCreation Date
TimeTimeCreation Time
ValueNumberRequired Value, Range 300-900
CommentText 

Well, that's it. When a new entry is created, the current date & time are automatically inserted (that should be used when I just did the measuring), but still editable in case I'm inserting an old value. Value is a number between 300 and 900 (usually around 600), a medical doctor could explain it - I can't. Comment is a field for notes when something extraordinary happens like when I'm taking additional medication or I'm sick. You can download an example database here. It includes some example data, but rename it to "Peakflow" after downloading and fixing the type.

When I'm about to report to my M.D., I'm exporting the whole database to RagTime and build that chart (date/time over value). Afterwards I'm removing all entries to keep it clean.

 

// The Project

Now it's time to create the project (template is "AppleScript application"). I'm calling it "PeakflowAS", in memory of "PeakflowWO" and "PeakflowD2W", my former attempts on creating a nice interface to my peakflow database.

 

// The Interface

Ok, here comes the most important part for us developers: How the interface will look.

AppleScript Studio is just an addition to Cocoa, so you can use all the things you're probably used to. If you're not used to Interface Builder, do the nice tutorial on Apple's Developer pages, because I'm not going to explain all the details of it.

Now open MainMenu.nib. Our application has a single window:

PeakflowAS interface
Figure 1: Our interface for accessing the peakflow database.

The table view is used for displaying all entries in the database, the text fields below it for adding a new entry.

One important thing to remember when using ASS is: Name your controls! When you'd access your controls using e.g. "button 1 of window 1", you'd be pretty pissed off when you want to add an additional table view later. And don't forget that this name can only be set in the inspectors AppleScript-section which you can access by pressing Cmd-6.

So, I'm calling the window "peakflow window", the scroll view "scroller", the table view "peakflowtable", the text fields "date field", "time field", "value field" and "comment field". The table view's columns are named "Date", "Time", "Value" and "Comment". It's wise to get the window an Auto Save Name.

Now there's a tricky thing: That table view needs a data source. There are two ways in ASS to get one: Create it yourself, or use the one Apple provides. Since I'm a lazy person, I'm using the one by Apple. So switch the IB-palette to the (probably) rightmost palette, the AppleScript one. There's currently only a single blue block in there, that's the data source. Drag it to the project's window in IB (not PB). Then control-drag a connection from the table view (not the scroll view that surrounds it!) to our object, select "dataSource" from the inspector that pops up and press "Connect".

I've done some additional tricks using formatters for the text fields, but that's up to you to implement (it's not required and has no effect on the rest of the project).

Now we'll add the required event handlers. The first one is the window's open event, second one is the button's clicked event. That's it, next step is Project Builder!

 

// The Code

Open Application.applescript. IB should have added some two event handlers, "on will open theObject" and "on clicked theObject". The first one we'll implement is "will open", which should initialize the table view.

on will open theObject
	tell application "FileMaker Pro" to ¬
		set peakflow to records of database "Peakflow"

Now we've loaded the whole database into our app.

	set datasource to data source of table view ¬
		"peakflowtable" of scroll view "scroller" of window "peakflow window"

This is only for more convenience later (AppleScript is a pretty verbose language). datasource is now the object we had added in IB in the previous section.

	set update views of datasource to false

For filling our table, we have to disable the display update, otherwise we would get very slow. Next thing is to initialize the data source:

	tell datasource
		delete data columns
		make new data column at the end of the data columns ¬
			with properties {name:"Date"}
		make new data column at the end of the data columns ¬
			with properties {name:"Time"}
		make new data column at the end of the data columns ¬
			with properties {name:"Value"}
		make new data column at the end of the data columns ¬
			with properties {name:"Comment"}
		delete data rows
	end tell

The two "delete"-commands aren't really necessary, since the object is empty anyways, but it's always wise to clean up before doing anything. The other commands tell the data source what columns the table view does provide (they have to be the same you've defined for the table view's column names! And they're case sensitive).

Now we're ready to fill the data source with our data:

	repeat with n from 1 to (count of peakflow)
		
		set theRow to make new data row at the end of ¬
			the data rows of datasource
		
		set contents of data cell "Date" of theRow to ¬
			item 1 of item n of peakflow
		set contents of data cell "Time" of theRow to ¬
			item 2 of item n of peakflow
		set contents of data cell "Value" of theRow to ¬
			item 3 of item n of peakflow
		set contents of data cell "Comment" of theRow to ¬
			item 4 of item n of peakflow
	end repeat

That's pretty straight-forward. The only thing left is to actually let the view draw our new data:

	set update views of datasource to true
end will open

Ok, first step is done. If you're not in a hurry, you can now test our new list view by pressing build & run (the icon with the hammer and the monitor in PB's toolbar).

That should work fine now. The next step is a bit more complicated: get that add-button to work.

The first thing we'll do is to write the data to add into some local variables. The value is a required parameter, so we have to check first if it's empty (that if-block is about two pages long!).

on clicked theObject -- add button
	set theValue to string value of text field "value field" ¬
		of window of theObject
	if theValue is not "" then
		set theDate to string value of text field "date field" ¬
			of window of theObject
		set theTime to string value of text field "time field" ¬
			of window of theObject
		set theComment to string value of text field "comment field" ¬
			of window of theObject

Now we're ready for creating the actual entry in the database, inserting the non-empty variables from above. Since we're writing a friendly app, we check for errors and present a nice dialog if anything fails (don't forget to clean up). Note that a missing date or time field will be automatically filled by FileMaker (if you created the database like I told you to), so we don't have to care about that.

		tell application "FileMaker Pro"
			set theRecord to create new record at the end of the records
			try
				set cellValue of cell "Value" of theRecord to theValue
				if theDate is not "" then
					set cellValue of cell "Date" of theRecord to theDate
				else
					set theDate to cellValue of cell "Date" of theRecord
				end if
				if theTime is not "" then
					set cellValue of cell "Time" of theRecord to theTime
				else
					set theTime to cellValue of cell "Time" of theRecord
				end if
				if theComment is not "" then set cellValue of ¬
					cell "Comment" of theRecord to theComment
			on error
				delete theRecord
				tell me to display dialog "FileMaker refused to ¬
					insert the new entry." buttons {"Abort"} ¬
					default button 1
				return
			end try
		end tell

Ok, the database is up to date. But our table view isn't! There are two possibilities for updating it:

  1. Reload the table (that's what I would do in Objective C).
  2. Add the entry to the end of the list.

Since option #1 takes forever in AppleScript, here's the code for #2 (it's pretty much the same like in the repeat-block of on will open theObject):

		set update views of datasource to false
		
		set theRow to make new data row at the end ¬
			of the data rows of data source of table view ¬
			"peakflowtable" of scroll view "scroller" of window of theObject
		
		set contents of data cell "Date" of theRow to theDate
		set contents of data cell "Time" of theRow to theTime
		set contents of data cell "Value" of theRow to theValue
		set contents of data cell "Comment" of theRow to theComment

		set update views of datasource to true

Ok, now we'll remove the already inserted values from the text fields, since we don't want double entries.

		set string value of text field "value field" of window ¬
			of theObject to ""
		set string value of text field "comment field" of window ¬
			of theObject to ""
		set string value of text field "date field" of window ¬
			of theObject to ""
		set string value of text field "time field" of window ¬
			of theObject to ""

Finally, we can close our oversized if-block and tell the user that the value-field is required (in a cool sheet attached to the window!):

	else
		display dialog "The value-field is required for new entries." ¬
			buttons {"OK"} default button 1 attached to window of theObject
	end if
end clicked

So, that's it for now. You can now play around with the application.

 

// Improvements

Here are some suggestions on how to improve the app:

  • Add the ability to modify existing entries (that's the on change cell value theObject value theValue row theRow table column tableColumn-call, I didn't get it to work for whatever reason).
  • Add the ability to remove rows of data (I don't have a clue on how to do that in AppleScript).

 

// Conclusion

I hope you get an impression on what's possible in ASS and what to expect (like hundreds of "a of b of c of d"). My impression is that ASS is not suited for professional apps (that's what Objective C is for), but it's pretty nice for small apps for personal use.


Comments

Hello, nice tutorial.

Suppose you have two or more applications that you want to make work together in harmony. Suppose these applications also support Applescript. This means you have a way of getting these applications to talk to each other simply using scripts!!

Objective-C/Cocoa supports loading and compiling of Applescript(s) on the fly -- you can even build one out of a custom NSMutableString if you wish. You simply need to include the AppleScriptKit framework in your project. If you take a look at the NSAppleScript documentation it should become clear how you can harness the power of Applescript within your Cocoa applications.

It is my impression this would be suitable for both professional and personal apps.

Posted by: x11 on May 25, 2003 02:45 PM

WOW, A really useful Applscript site at last. Thanks!

I wonder if you can advise how to connect to a Sybase database I am building. The provision of a slick AS front end would be a great project.

Thanks.

Posted by: PGarfeild on May 28, 2003 04:51 AM
Post a comment