2013-05-10 7 views
8

私はTVシリーズの進捗状況を追跡する小さなアプリケーションを作成しました。このアプリケーションはHaskellでreactive bananaという機能応答プログラミング(FRP)で書かれています。反応性のあるバナナとgtk2hsを持つリアクションテーブル

アプリケーションができます。

  • は私が書いて問題を抱えている
  • チェンジシリーズ

App Screenshot

のシーズンとエピソードをテーブルに削除/新しいテレビシリーズを追加します新しいテレビシリーズをテーブルに追加し、新しいイベントを結ぶコード。 hereのCRUDの例は、リストから要素を選択するだけの要件が増えているため、私にはあまり役に立ちませんでした。

reactiveListDisplayのような機能をFRP方式でCRUD Exampleから書き込むにはどうすればよいですか?ネットワークがコンパイルされた後に、削除ボタンとシーズンとエピソードのスピンボタンのイベントを追加するにはどうすればよいですか?

data Series = Series { name :: String 
        , season :: Int 
        , episode :: Int 
        } 


insertIntoTable :: TableClass t => t -> SeriesChangeHandler -> SeriesRemoveHandler -> Series -> IO() 
insertIntoTable table changeHandler removeHandler (Series name s e) = do 
    (rows, cols) <- tableGetSize table 
    tableResize table (rows+1) cols 

    nameLabel  <- labelNew $ Just name 
    adjustmentS <- adjustmentNew (fromIntegral s) 1 1000 1 0 0 
    adjustmentE <- adjustmentNew (fromIntegral e) 1 1000 1 0 0 
    seasonButton <- spinButtonNew adjustmentS 1.0 0 
    episodeButton <- spinButtonNew adjustmentE 1.0 0 
    removeButton <- buttonNewWithLabel "remove" 
    let getSeries = do 
      s <- spinButtonGetValue seasonButton 
      e <- spinButtonGetValue episodeButton 
      return $ Series name (round s) (round e) 
     handleSeries onEvent widget handler = do 
      onEvent widget $ do 
       series <- getSeries 
       handler series 

    handleSeries onValueSpinned seasonButton changeHandler 
    handleSeries onValueSpinned episodeButton changeHandler 
    onPressed removeButton $ do 
     series <- getSeries 
     containerRemove table nameLabel 
     containerRemove table seasonButton 
     containerRemove table episodeButton 
     containerRemove table removeButton 
     removeHandler series 

    let tadd widget x = tableAdd table widget x (rows - 1) 
    tadd nameLabel  0 
    tadd seasonButton 1 
    tadd episodeButton 2 
    tadd removeButton 3 
    widgetShowAll table 


main :: IO() 
main = do 

    initGUI 

    window  <- windowNew 
    scroll  <- scrolledWindowNew Nothing Nothing 
    table  <- tableNew 1 5 True 
    addButton <- buttonNewWithLabel "add series" 
    vbox  <- vBoxNew False 10 

    containerAdd window vbox 
    boxPackStart vbox addButton PackNatural 0 

    let networkDescription :: forall t. Frameworks t => Moment t() 
     networkDescription = do 

      addEvent <- eventButton addButton 

      (changeHandler,fireChange) <- liftIO $ newAddHandler 
      changeEvent <- fromAddHandler changeHandler 
      (removeHandler,fireRemove) <- liftIO $ newAddHandler 
      removeEvent <- fromAddHandler removeHandler 

      let insertIntoTable' = insertIntoTable table fireChange fireRemove 
       addSeries e = do 
        s <- addSeriesDialog 
        liftIO $ insertIntoTable' s 

      liftIO $ mapM_ insertIntoTable' initSeries 

      reactimate $ addSeries   <$> addEvent 
      reactimate $ updateSeries conn <$> changeEvent 
      reactimate $ removeSeries conn <$> removeEvent 

    network <- compile networkDescription 
    actuate network 

    onDestroy window $ do 
     D.disconnect conn 
     mainQuit 

    widgetShowAll window 
    mainGUI 

単純なコールバックを使用するのではなく、イベントとビヘイビアを使用するinsertIntoTableメソッドをリファクタリングします。

EDIT:

私はListStoreバックエンドでGTK TreeViewを試してみました。このシナリオでは、動的イベント切り替えは必要ありません。私は、イベントの挿入、変更、削除からリスト動作を得るために、以下の関数reactiveListを書いています。それは動作します^^

reactiveList :: (Frameworks t) 
    => ListStore a 
    -> Event t (Int,a) -- insert event 
    -> Event t (Int,a) -- change event 
    -> Event t (Int,a) -- remove event 
    -> Moment t (Behavior t [a]) 
reactiveList store insertE changeE removeE = do 

    (listHandler,fireList) <- liftIO $ newAddHandler 

    let onChange f (i,a) = do 
      f i a 
      list <- listStoreToList store 
      fireList list 

    reactimate $ onChange (listStoreInsert store)   <$> insertE 
    reactimate $ onChange (listStoreSetValue store)  <$> changeE 
    reactimate $ onChange (const . listStoreRemove store) <$> removeE 

    initList <- liftIO $ listStoreToList store 
    fromChanges initList listHandler 


main :: IO() 
main = do 

    initGUI 

    window  <- windowNew 
    addButton <- buttonNewWithLabel "add series" 
    vbox  <- vBoxNew False 10 
    seriesList <- listStoreNew (initSeries :: [Series]) 
    listView <- treeViewNewWithModel seriesList 

    treeViewSetHeadersVisible listView True 

    let newCol title newRenderer f = do 
      col <- treeViewColumnNew 
      treeViewColumnSetTitle col title 
      renderer <- newRenderer 
      cellLayoutPackStart col renderer False 
      cellLayoutSetAttributes col renderer seriesList f 
      treeViewAppendColumn listView col 
      return renderer 

    newCol "Image" cellRendererPixbufNew $ \s -> [cellPixbuf :=> newPixbuf s] 
    newCol "Name" cellRendererTextNew $ \s -> [cellText := name s] 
    seasonSpin <- newCol "Season" cellRendererSpinNew $ \s -> 
     [ cellRendererSpinAdjustment :=> adjustmentNew (fromIntegral (season s)) 1 1000 1 0 0 
     , cellText := (show $ season s) 
     , cellTextEditable := True 
     ] 
    episodeSpin <- newCol "Episode" cellRendererSpinNew $ \s -> 
     [ cellRendererSpinAdjustment :=> adjustmentNew (fromIntegral (episode s)) 1 1000 1 0 0 
     , cellText := (show $ episode s) 
     , cellTextEditable := True 
     ] 

    containerAdd window vbox 
    boxPackStart vbox listView PackGrow 0 
    boxPackStart vbox addButton PackNatural 0 

    let networkDescription :: forall t. Frameworks t => Moment t() 
     networkDescription = do 

      (addHandler,fireAdd) <- liftIO $ newAddHandler 
      maybeSeriesE <- fromAddHandler addHandler 
      (removeHandler,fireRemove) <- liftIO $ newAddHandler 
      removeE <- fromAddHandler removeHandler 

      -- when the add button was pressed, 
      -- open a dialog and return maybe a new series 
      askSeriesE <- eventButton addButton 
      reactimate $ (const $ fireAdd =<< askSeries) <$> askSeriesE 

      -- ommit all nothing series 
      let insertE = filterJust maybeSeriesE 
       insert0E = ((,) 0) <$> insertE 

      seasonSpinE <- eventSpin seasonSpin seriesList 
      episodeSpinE <- eventSpin episodeSpin seriesList 
      let changeSeason (i,d,s) = (i,s {season = round d}) 
       changeEpisode (i,d,s) = (i,s {episode = round d}) 
      let changeE = (changeSeason <$> seasonSpinE) `union` (changeEpisode <$> episodeSpinE) 

      listB <- reactiveList seriesList insert0E changeE removeE 
      listE <- (changes listB) 

      reactimate $ (putStrLn . unlines . map show) <$> listE 
      reactimate $ insertSeries conn  <$> insertE 
      reactimate $ updateSeries conn . snd <$> changeE 
      reactimate $ removeSeries conn . snd <$> removeE 

      return() 

    network <- compile networkDescription 
    actuate network 

    onDestroy window $ do 
     D.disconnect conn 
     mainQuit 

    widgetShowAll window 
    mainGUI 

私はコメントや提案があります。

+0

私たちにはいくつかのコードがあれば役に立ちます。特に、基礎となるデータ構造は何ですか? – isturdy

答えて

3

あなたの問題は、CRUDよりもBar Tabのほうがはるかに近いと言えます。

新しいウィジェットを追加するための基本的なアイデアは、新しい動作やイベントとともに、いわゆる「動的イベント切り替え」を使用することです。本質的に、これは新たに作成されたイベントやビヘイビアをネットワークに戻す方法です。

新しいウィジェットを作成するアクションには、2つの部分があります。最初の部分は、liftIOを使用してウィジェットを作成することです。 2番目は入力を取得し、trimEまたはtrimBを適切に使用することです。 (私はGTK使用する方法がわからない:P)をGTK-具体的な詳細のほとんどを残して、それはこのような何かを見てみましょう:

let newSeries name = do 
    label <- liftIO . labelNew $ Just name 
    liftIO $ tadd labelNew 0 
    {- ... the rest of your controls here ... -} 
    seasonNumber <- trimB $ getSpinButtonBehavior seasonButton 
    {- ... wrap the rest of the inputs using trimB and trimE ... -} 
    return (label, seasonNumber, ...) 

ので、この機能は、新しいウィジェットを作成し、「トリミング」の値を入力して返します。今、あなたは実際にこれらの値を使用する必要があります。ここに

newSeasons <- execute (FrameworkMoment newSeries <$> nameEvents) 

nameEventsは、新シリーズの名前、あなたがそれを追加するたびに使用してイベントを含むEvent Stringでなければなりません。

すべての新しい季節のストリームがあるので、stepperのようなものを使用して、リストのエントリの1つの動作にすべてを組み合わせることができます。

すべてのウィジェットから集計情報を取得するなどの詳細については、実際のサンプルコードをご覧ください。

+0

偉大な答えをありがとう。私は答えを受け入れる前に、それを試して結果を投稿します。 – SvenK

関連する問題