Hi James,


On 29 October 2010 05:06, James d'Arcy <james.darcy@wraithbane.com> wrote:
Conceptually what I want to do is create items in the ListCtrl and associate a
unique identifier with each item. This identifier would not be displayed in the
UI. An Int64 would be ideal but a String would be fine. When a user clicks on
the item, I want to be able to retrieve this identifier and use it to find the
associated data, in this case a database record.

The items are inserted into the ListCtrl ok with the following code:

showStudy :: ListCtrl l -> (Int,DicomStudy) -> IO ()
showStudy wgDbTable (idx,dcmStudy) = do
  listCtrlInsertItemWithData wgDbTable idx $ studyUid dcmStudy
  set wgDbTable [item idx := [(patientName . studyPatient) dcmStudy,
                              studyDescription dcmStudy,
                              studyDate dcmStudy]]

and the event handler looks like this:

onDbTableEvent :: HasturContext -> EventList -> IO ()
onDbTableEvent HasturCtx {guiDbTable=wgDbTable, guiSeriesList=wgSeriesList} event =
  case event of
    ListItemSelected idx -> do
      studyUid <- listCtrlGetItemData wgSeriesList idx
      infoM "Hastur" $ "DB Table event: " ++ show studyUid
      propagateEvent
    otherwise             ->
      propagateEvent

When the user clicks on the ListCtrl item I get an error dialog pop up saying:

"Couldn't retrieve information about list control item X"

where X is idx. The infoM call produces:

"DB Table event: 0"

What am I missing? I've tried listCtrl{Get/Set}Data, listCtrl{Get/Set}Text all
with the same result. listCtrlDeleteItem in the event handler doesn't delete the
item but produces the result: False.


showStudy wgDbTable (idx,dcmStudy) = do
  listCtrlInsertItemWithData wgDbTable idx $ studyUid dcmStudy
  set wgDbTable [item idx := [(patientName . studyPatient) dcmStudy,
                              studyDescription dcmStudy,
                              studyDate dcmStudy]]

I suspect that the problem comes from mixing WXCore and WX functionality (highlighted in red above if you are using an HTML mailer - otherwise look at the . I doubt that it is safe to do this.
 
Below is an example I have tested. I've tried to make it as simple as possible, so it has a fixed list of (displayed) items, each of which has an Integer key (which is not displayed - it is stored as a data item). I'll write it up more fully in my blog (http://wewantarock.wordpress.com) shortly, but for the moment, just note that I am using the WXCore functions to insert the item data and the displayed contents.

module Main () where

import Graphics.UI.WXCore
import Graphics.UI.WX

-- Int data and strings for each column
entries
  = [ (100, ["BouncingBalls.hs"           ,"2402"  ,"Jul 19 16:50"])
    , (101, ["ByeDemo.hs"                 ,"1414"  ,"Jul 13 23:18"])
    , (102, ["Camels.hs"                  ,"7633"  ,"Aug 20 11:57"])
    , (103, ["Controls.hs"                ,"3862"  ,"Aug 20 11:57"])
    , (104, ["HelloWorld.hs"              ,"1028"  ,"Aug 15 10:09"])
    , (105, ["ImageViewer.hs"             ,"3756"  ,"Aug 20 11:57"])
    , (106, ["Layout.hs"                  ,"1075"  ,"Jul 13 23:18"])
    , (107, ["ListCtrl.hs"                 ,"750"  ,"Sep  8 16:22"])
    , (108, ["Minimal.hs"                  ,"147"  ,"Jul 13 23:18"])
    , (109, ["Paint.hs"                   ,"1024"  ,"Aug 20 11:57"])
    , (110, ["Process.hs"                 ,"2261"  ,"Aug 20 11:57"])
    , (111, ["TimeFlows.hs"               ,"4929"  ,"Aug 20 11:57"])
    , (112, ["TimeFlowsEx.hs"             ,"8648"  ,"Aug 20 11:57"])
    , (113, ["desert.bmp"                ,"61302"  ,"Jul 13 23:31"])
    ]

main :: IO ()
main
  = start gui

gui :: IO ()
gui
  = do -- main gui elements: frame, panel, text control, and the notebook
       f       <- frame [text := "List Sample"]
       -- panel: just for the nice grey color
       p       <- panel f []
       textlog <- textCtrl p [enabled := False, wrap := WrapLine]

       -- use text control as logger
       textCtrlMakeLogActiveTarget textlog
       logMessage "logging enabled"             

       -- list control
       l  <- listCtrl p [columns := [("Name", AlignLeft, 120)
                                    ,("Size", AlignRight, -1)
                                    ,("Date", AlignRight, -1)]]
       set l [on listEvent := onListEvent l]
       mapM_ (setItem l) entries
       -- specify layout
       set f [layout     := container p $ margin 10 $
                            column 5 [ fill  $ widget l
                                     , hfill $ widget textlog
                                     ]
             ,clientSize := sz 400 300
             ]
       return ()
  where
    onListEvent lc eventList
      = case eventList of
          ListItemSelected idx    -> do
                                     dat <-listCtrlGetItemData lc idx
                                     logMessage ("item selected: " ++ show idx ++ " Data: " ++ show dat)
          ListItemDeselected idx  -> logMessage ("item de-selected: " ++ show idx)
          other                   -> logMessage ("list control event.")
    setItem lc (key_val, col_txts) =
          do
      count <- listCtrlGetItemCount lc
      idx <- listCtrlInsertItemWithLabel lc count (show count) (-1)
      mapM_ (\(column, col_txt) -> listCtrlSetItem lc idx column col_txt (-1)) (zip [0..] col_txts)
      listCtrlSetItemData lc idx key_val


Is what I am trying to achieve possible or would I be better maintaining a
Data.Map with a mapping between idx and the relevant data? If so, how would that
affect things if the ListCtrl contents are re-ordered (e.g. sort by date)?

Hopefully the example shows how to do what you want...


Sorry if this seems a stupid query, I'm new to Haskell and trying to learn after
years of using imperative languages.

It's an entirely sensible question if you ask me, but then I'm also a recovering imperative programmer :-)

Regards
Jeremy