Wednesday, December 22, 2010

iPad VLC Hangs Solution

I recently received an iPad as a Christmas gift from work. Although I have never really considered buying one and haven't even bothered to read up about them. Having said that, I use a mac at home and love it's simplicity, so the iPad should be simple to pick up and run with. Well, let's see just how things progressed...
Upon opening up the iPad box, I expected it to contain a little more in the way of documentation - the mac at least contains a brief overview of the features. With the iPad, it's even less - just an image identifying the various connection ports. This was enough for me to identity the on/off button and upon pressing it, an image directed me to connect it to my machine and open iTunes.

So far so good - I went through a few formalities in terms of license agreements before being presented with the iPad home screen. Having never used an iPad or iPhone before, it took me a few minutes to get up to speed with how to navigate anything. After configuring it to connect to my router, I had Internet access up in a breeze and was happily browsing away.

Next I thought I'd try out something a bit more tricky - playing an avi video. On my mac, I use VLC for playing back all of my video formats. Luckily, there is a VLC app for iPad and after installing it from the app store, I had my first - "ok - what now?" moment. How to transfer files to the iPad. A google search explained that the files are transfers through iTunes via the 'app' tab, and are associated with a particular app. It sounded simple enough transferring files was simple. Although I am still curious as to what happens if you have two apps capable of opening the same file - does it mean you need to have two copies of the file - one for each app?

The interface to VLC is very slick and simple. Everything was going fine until it hung. My first solution was to restart VLC which didn't work, so I resorted to restarting the iPad. While this resolved the problem, it meant restarting it each time VLC hung. There must be another way and luckily there is - a bit of googling reveled the following solution:
  1. Tap the playing area to display the VLC controls
  2. Tap the 'forward' control twice
  3. Tap 'done'
  4. The VLC media selection screen should be displayed
  5. Select the video to play and it should continue from where it hung
While not ideal, it is much better than restarting the whole iPad.

Sunday, October 31, 2010

Installing Heist

Now that I have snap installed, the next part is to install Heist - the X(HT)ML templating library for use with snap.
$ cabal install heist
And that's it - no problems at all.

Saturday, October 16, 2010

Installing Snap (a web framework for Haskell) on Mac OS X Snow Leopard

I haven't been very active writing posts here, but hopefully this will change as I start to document my experience with Snap - a Haskell web framework. I have some experience with Haskell, though have never used it for web applications. Outside of Haskell, I have limited experience developing web applications, and hope to expand my knowledge of both by exploring Snap.

A quick overview of my system:
  • Mac OS X version 10.6.4 (Snow Leopard)
  • Haskell Platform version 2010.2.0.0 which includes ghc version 6.12.3
First impressions are very good as I browse the website and skim over the tutorials and documentation provided. The first step is to install Snap: $ cabal update $ cabal install snap-server After a short while compiling, cabal failed with the following message: cabal: Error: some packages failed to install: bytestring-mmap-0.2.1 failed during the building phase. The exception was: ExitFailure 1 snap-core-0.2.13 depends on bytestring-mmap-0.2.1 which failed to install. snap-server-0.2.13.3 depends on bytestring-mmap-0.2.1 which failed to install. Hmm...trying again to try to narrow down what the problem was: $ cabal install snap-server I get a bit more info: Resolving dependencies... Configuring bytestring-mmap-0.2.1... Preprocessing library bytestring-mmap-0.2.1... Building bytestring-mmap-0.2.1... [1 of 3] Compiling System.IO.Posix.MMap.Internal (System/IO/Posix/MMap/Internal.hs, dist/build/System/IO/Posix/MMap/Internal.o ) ghc: could not execute: /Library/Frameworks/GHC.framework/Versions/612/usr/lib/ghc-6.12.3/ghc-asm cabal: Error: some packages failed to install: bytestring-mmap-0.2.1 failed during the building phase. The exception was: ExitFailure 1 snap-core-0.2.13 depends on bytestring-mmap-0.2.1 which failed to install. snap-server-0.2.13.3 depends on bytestring-mmap-0.2.1 which failed to install. Ok, so the problem is around executing /Library/Frameworks/GHC.framework/Versions/612/usr/lib/ghc-6.12.3/ghc-asm. Looking at the contents of this file, I see that it is a perl script with the perl interpreter location set to: #!/opt/local/bin/perl Running: $ which perl I find that perl is located at /usr/bin/perl. Making the appropriate substitution and trying again, the installation was successful. Note that this problem has been documented here

Thursday, April 15, 2010

Recreating Windows as Required

It has been a while since my last post and my work on Tennis Ladders and Stats has decreased as it's stability has improved. Having said that, I have been working on implementing a number of automated tests using Hunit and ant as the build framework. I've been using the tests to prevent regressions creeping in during some code restructuring. Anyway, I've decided provide a short example outlining one of the key issues I ran into during development. It involves displaying a window which needs to customised each time it is displayed. A simple example is that of an error window which displays a customised error message provided as an argument.

When working with repetitive windows which have the same appearance throughout the duration the application is running, they can simply be created once and a handle returned so that they can be shown when required. In this case, the code for setting up a window might look like this:


{-
loadAddTeamWindow
sets up the add_team_window
-}
loadAddTeamWindow tlasGlade = do
addTeamWindow <- xmlGetWidget tlasGlade castToWindow "add_team_window"
addTeamCancelButton <- xmlGetWidget tlasGlade castToButton "add_team_cancel_button"
addTeamApplyButton <- xmlGetWidget tlasGlade castToButton "add_team_apply_button"
addTeamText <- xmlGetWidget tlasGlade castToEntry "add_team_text"

-- set maxlength of addTeamText
entrySetMaxLength addTeamText maxInputLength

onDelete addTeamWindow $ do
(\_ -> do
-- hide addTeamWindow
widgetHide addTeamWindow
return True)

onShow addTeamWindow $ do
-- clear entry field
entrySetText addTeamText ""
-- set focus to text entry
widgetGrabFocus addTeamText

onClicked addTeamApplyButton $ do
teamname <- get addTeamText entryText
let team = Team {teamClub = teamname, teamPlayers = [], teamPoints = 0,
teamNumMatches = 0, teamGamesFor = 0, teamGamesAgainst = 0}
seasonM <- getCurrentSeason
sectionM <- getCurrentSection
case (teamname == "", seasonM, sectionM, entryTextTooLong teamname) of
(_, Nothing, _, _) -> showErrorWindow "Can not add a team without selecting a season"
(_, _, Nothing, _) -> showErrorWindow "Can not add a team without selecting a section"
(True, _, _, _) -> showErrorWindow "Team name can not be empty"
(_, _, _, True) -> showErrorWindow "Team name is too long"
(False, Just season, Just section, False) -> do
existingTeamNames <- getClubNames season section
case (elem teamname existingTeamNames) of
True -> showErrorWindow ("Cannot add team " ++ teamname ++ " as it already exists")
False -> do
-- add the team
addTeam season section team
-- hide the addTeamWindow
widgetHide addTeamWindow

onClicked addTeamCancelButton $ do
-- hide the addTeamWindow
widgetHide addTeamWindow

return addTeamWindow


Unfortnately the same approach doesn't quite work when we want the window to change each time it is displayed. This time we need to recreate the window each time it is to be displayed and ensure it is destroyed when it is closed. The following code demonstrate this approach:


{-
showErrorWindow
shows the errorWindow with the given message
NOTE:
since the errorMessage changes on each invocation, the errorWindow can't
be shown and hidden as other windows are. Instead, the window must be
destroyed and created again on each invocation. If the window is not
destroyed but hidden, multiple instances of the errorWindow are active
at the same time.
-}
showErrorWindow errorMessage = do
tlasGladeM <- xmlNew "tlas.glade"
let tlasGlade = case tlasGladeM of
(Just tlasGlade) -> tlasGlade
Nothing -> error "Can't find the glade file \"tlas.glade\" in the current directory"

errorWindow <- xmlGetWidget tlasGlade castToWindow "error_window"
okButton <- xmlGetWidget tlasGlade castToButton "error_window_ok_button"
errorLabel <- xmlGetWidget tlasGlade castToLabel "error_label"

onShow errorWindow $ do
labelSetLabel errorLabel errorMessage
widgetGrabFocus okButton

onDelete errorWindow $ (\_ -> do return False)

onClicked okButton $ do
widgetDestroy errorWindow

widgetShowAll errorWindow

Tuesday, February 16, 2010

Tennis Ladders and Stats

I am nearing completion of the development of Tennis Ladders and Stats - a program designed for maintaining team ladders and player statistics in local tennis competitions. I have been involved in local tennis competitions for several years and have assisted in maintaining such statistics in the past. Previously the process was a manual one and while there have been some programs utilised over the past few years, they are rather rigid in design - having a different program for each association, despite each association having very similar requirements. Tennis Ladders and Stats attempts to overcome the shortcomings of previous programs and provide a single program which can be used to provide the common functionality required, regardless of the result format required.

Since the development of Tennis Ladders and Stats is nearing completion, I plan to blog a run down of the various technical challenges I faced and how I solved them during the development process. All development was performed using haskell and gtk2hs for the user interface, parts of which were developed using glade. Hopefully the notes provided will assist others in one way or another.

Friday, February 5, 2010

My First Blog

I've just finished setting up my blog. The hardest part was finding a name which wasn't already in use. Today marks the end of my graduate year at Unico. It has been a very valuable experience and I feel like I have learned so much in such a short span of time. I'm looking forward to the next challenges as I move into my next role...