2013-05-10 7 views
8

TV 시리즈의 진행 상황을 추적하는 작은 애플리케이션을 작성했습니다. 이 애플리케이션은 하스켈에서 reactive banana의 기능 반응 형 프로그래밍 (FRP)으로 작성되었습니다.반응성 바나나 및 gtk2hs를 포함하는 반응 테이블

응용 프로그램을 수행 할 수 있습니다

  • 추가/테이블에

App Screenshot

내가 문제가를 작성해야 시리즈의 시즌과 에피소드를

  • 변화를 새로운 TV 시리즈를 제거 새로운 TV 시리즈를 테이블에 추가하고 새로운 이벤트를 연결하는 코드. here의 CRUD 예제는 목록에서 요소를 선택하기위한 요구 사항이 더 많아서별로 도움이되지 않았습니다.

    reactiveListDisplay 함수와 같은 reactiveTable 함수를 CRUD Example에서 FRP 방식으로 작성하려면 어떻게해야합니까? 네트워크가 컴파일 된 후 제거 버튼, 시즌 및 에피소드 스핀 버튼에 대한 이벤트가 어떻게 추가 될 수 있습니까?

    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 방법을 리팩토링하고 싶다.

    편집 : 나는 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입니다.

    새로운 위젯을 추가하는 기본 개념은 새로운 동작 및 이벤트와 함께 이른바 "동적 이벤트 전환"을 사용하는 것입니다. 근본적으로 이것은 새로 생성 된 이벤트와 행동을 다시 네트워크에 넣는 방법입니다.

    새 위젯을 만드는 작업에는 두 부분이 있습니다. 첫 번째 부분은 liftIO을 사용하여 위젯을 만드는 것입니다. 두 번째는 입력을 받아 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, ...) 
    

    그래서이 기능은 새로운 위젯을 만들고, "트림"그 값을 입력하고 반환합니다. 지금 당신은 실제로이 값을 사용해야합니다 :

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

    이 새로운 시리즈의 이름이 추가 할 때마다와 이벤트를 포함하는 Event String해야한다.

    이제 모든 새 시즌의 흐름이 있으므로 stepper과 같은 항목을 사용하여 목록의 항목을 하나의 동작으로 결합 할 수 있습니다.

    모든 위젯에서 집계 정보를 얻는 것을 포함하여 자세한 내용은 실제 예제 코드를 참조하십시오.

    +0

    대단한 답변을 보내 주셔서 감사합니다. 나는 대답을 받아들이 기 전에 결과를 게시하고 그 결과를 게시합니다. – SvenK