From HaskellWiki
Revision as of 15:11, 19 August 2007 by M (talk | contribs) (Adding additional compound widgets)

Jump to: navigation, search

Getting Phooey to Run

This is very interesting. Getting this apparently simple proof of concept working is difficult for me. I've tried on Suse 10.2 and Windows XP. files I used on XP:


The recommended wxwindows 2.4.2 requires a wxhaskell dependent on ghc640. The problem is that arrows require base >=2.0 I could update Cabal to 1.1.7 using darcs etc. But I find no instructions on how to update the base-1.0 which comes with GHC 6.4.0. Even with instructions I think it requires a C compiler or more?

I use WinXP, GHC 6.6, and wxWidgets 2.4.2. I compiled wxWidgets and wxHaskell from sources. Conal 23:47, 20 February 2007 (UTC)

Yes, this looks nice; I can report partiual success on linux. I was able to build using ghc6.6 on Fedora 5, using wxGTK version 2.6.3 (built from source) and then wxHaskell. Phooey examples ran, however titles (window and panel) did not appear correctly, nor did text output, e.g. on the "shopping list" example the "total" field remains blank. On checking the wxHaskell demos, it seems the problem is in there. User:Djd 04:54, 14 May 2007

Thanks for the report. I expected wxHaskell to take care of cross-platform compatibility, but I guess not. Hm. Conal 17:26, 14 May 2007 (UTC)

Adding additional compound widgets

Hi Conal, I am looking to extend Phooey to include a record editor widget, a record list widget, as well as a tree widget. The first two being based on Grid. This is so that I can continue my work on HGene. i.e. the record editor would allow users to edit Person records, the list widget would show the children of a person and the tree to allow them to view a tree of Persons. For the editor I have so far (in the imperative world so far)

-- List of field name, field getter, field setter
type EditorProps a = [ ( String, a -> String, a -> String -> a )]

-- Create an editor for 'a'. 
-- Returns a grid, a means of reseting the record being edited, an extractor and notifier 
mkEditor :: Show a => Window b -> EditorProps a -> a  -> IO ( (Grid (), a -> IO(), IO a, IO () -> IO () ))

Is this in line with where you think Phooey can go and is this on the right track? Mark_Wassell

Hi Mark. Your design looks good to me. I'd love so hear/see how it goes. Conal 15:39, 14 August 2007 (UTC)
Hi Conal, Take a look at Extending Phooey for what I have so far. Mark_Wassell
Wonderful! Hey, I see you're using Phooey's Monad interface. Have you tried doing your extensions with the Applicative interface? I'm planning to eliminate the Monad & Arrow interfaces and just leave Applicative. I can instead keep Monad and continue to layer Applicative on top of it (A.UI = M.UI `O` Source), if there's value in doing so. Conal 14:53, 18 August 2007 (UTC)
I see your UI is defined recursively (mdo), which works for the monadic interface, but not for the applicative one. I don't even know what the interface for would be for applicatives. Perhaps afix :: f (a -> a) -> f a. Conal 16:07, 18 August 2007 (UTC)

Intermediate results and self-updating widgets

Hi Conal, I have been experimenting with Phooey for a little bit now. I think it is very cool how you separate the layout of elements and the wiring of which widgets affects other widgets, while keeping the library functional (as opposed to imperative). Thus, if I sound negative below it is just because I am playing the devils advocate.

There seem to be no easy way of representing intermediate results. For example, if you have apples, bananas, shovels, and spades. And you want to have two intermediate results, namely fruits and tools. I came up with the following program:

-- Have to make phantom1 & phantom2, otherwise the intermediate results are not shown. We also
-- need to reference them from the last line in the UI-monad.
basket :: UI (Source ())
    = title "Shopping List" $
      do a <- apples
         b <- bananas
         let fruitsTotal = liftA2 (+) a b
         phantom1 <- title "Fruit" $ showDisplay fruitsTotal
         c <- shovels
         d <- spades
         let toolsTotal = liftA2 (+) c d
         phantom2 <- title "Tools" $ showDisplay toolsTotal
         title "Basket" $ showDisplay $ liftA2 (+) fruitsTotal (liftA3 (const . const) toolsTotal phantom1 phantom2)
         -- The code below is not enough to show the intermediate results:
         -- title "Basket" $ showDisplay $ liftA2 (+) fruitsTotal toolsTotal

apples, bananas, shovels, spades :: UI (Source Int)
apples  = title "apples"  $ sl0 3
bananas = title "bananas" $ sl0 7
shovels = title "shovels" $ sl0 2
spades  = title "spades"  $ sl0 5

It seems to be more troublesome than it should be.

I was thinking that if we had an intermediate result widget defined as:

type IRWidget a = a -> Source a -> UI (Source a)

then we could make the basket program simpler. I have tried to do just that:

{-# OPTIONS -fglasgow-exts #-}
{-# OPTIONS -Wall #-}

module Examples.IRWidget where

import qualified Graphics.UI.WX as WX
import Control.Applicative
import Graphics.UI.Phooey.Monad
import Control.DataDriven
import Control.Monad.Writer
import Graphics.UI.Phooey.Imperative
import Control.Compose

-- IR = Intermediate Result
type IRWidget a = a -> Source a -> UI (Source a)

run :: IO ()
run = runUI basket

basket :: UI (Source ())
    = title "Shopping List" $
      do a <- apples
         b <- bananas
         fruits <- title "Fruit" $ showDisplayIR 0 (liftA2 (+) a b)
         c <- shovels
         d <- spades
         tools  <- title "Tools" $ showDisplayIR 0 (liftA2 (+) c d)
         title "Basket" $ showDisplay $ liftA2 (+) fruits tools

apples, bananas, shovels, spades :: UI (Source Int)
apples  = title "apples"  $ sl0 3
bananas = title "bananas" $ sl0 7
shovels = title "shovels" $ sl0 2
spades  = title "spades"  $ sl0 5

sl0 :: IWidget Int
sl0 = islider (0,10)

-- An output widget, which also returns a value
-- However, it unfortunately do not allow recursiveness. Could be usefull for creating a
-- sorted list.
irWidget :: (WX.Commanding widget, WX.Widget widget) => MkWidget widget a (IRWidget a)
irWidget layf mkWid attr initial mySrc = mkWidget layf $ \ win ->
  do wid  <- mkWid win [ attr WX.:= initial ]
     src  <- commandSource wid attr
     return (wid, (src <* attachSink attr wid mySrc, mempty))

-- Copied from Graphics.UI.Phooey.Monad
commandSource :: WX.Commanding widget =>
                 widget         -- ^ widget
              -> WX.Attr widget a  -- ^ attribute
              -> IO (Source a)
commandSource wid attr = fmap (dd (WX.get wid attr)) (commandNews wid)

-- Attaches a Source to an WX.Attr (attribute)
attachSink ::
     WX.Attr w a
  -> w
  -> Control.Compose.O ((,) (News IO)) IO a
  -> DataDriven IO ()
attachSink attr wid src = joinDD (sink attr wid <$> src)

-- Makes a sink from an attribute
sink :: WX.Attr w a -> w -> a -> IO ()
sink attr wid x = WX.set wid [ attr WX.:= x ]

-- Better if it did not use read, but in stead read directly from the attaches source, as it would
-- 1) not require that (Read a)
-- 2) no risk that the value was not-readable (which make a runtime error). It could be not
--    readable if the user wrote something in the textEntry or if the initial value was bad.
-- We should properly also get rid of the initial value. It should just get it from the source.
showDisplayIR :: (Read a, Show a) => a -> Source a -> UI (Source a)
showDisplayIR = irWidget WX.hfill WX.textEntry (WX.mapAttr read (\_ b -> show b) WX.text)

What do you think of this approach? Is this compatible with the idea behind Phooey?

Also, how would you do self-updating widgets? For example a list that sorted itself whenever the GUI-user added a new element to the list. One should be careful not to end up with eternal recursion.

Also mutually recursive widgets could be interesting. I realise that you do some recursion in your examples, but that is not to the value of the widgets, just to there "limits" (e.g. how low/high can the slider go).

Mads 15:16, 19 August 2007 (GMT)