5

私が達成したいこと:

ユーザーがキャンバスをクリックするたびに、そのクリックからマウス座標を取得して を構築し、Point x yこの状態を に保存[Point]して、後でユーザーがボタンをクリックしたときに、それ[Point]を何らかの関数の入力として使用できるようにします。

私がやったこと:

Point次のような単一値コンストラクターを使用して、データ型を定義しました。

data Point = Point {
  x :: Int,
  y :: Int
} deriving (Show, Eq)

UIユーザーインターフェイスを定義するために3ペニー(モナド?)をセットアップしまし400 x 400 canvasbutton

import qualified Graphics.UI.Threepenny as UI
import Graphics.UI.Threepenny.Core
import Control.Monad

canvasSize = 400

setup :: Window -> UI ()
setup window = do
  return window # set title "Haskell GUI"

  canvas <- UI.canvas
    # set UI.height canvasSize
    # set UI.width canvasSize
    # set style [("border", "solid black 1px"), ("background", "#eee")]

  button <- UI.button #+ [string "Do stuff"]

  getBody window #+
    [
    column [element canvas],
    element canvas,
    element button
    ]

  on UI.mousedown canvas $ \(x, y) -> do
    -- Need to create a point x y and add it to a list here

  on UI.click button $ const $ do
    -- Need to get the list of points here

  return ()

次に、main 内で UI を実行する関数を定義します。

runGui :: IO ()
runGui = startGUI defaultConfig setup

なので、最初はユーザーがクリックした場所に点を描く作業をしていました。これはかなり簡単に実現できPoint x yました。ラムダ引数で a をmousedown作成し、キャンバスに描画しました。私はそれを解決したのでそのコードを省略しています。現在の問題がそれに関連しているとは思いません(つまり、そのラムダのスコープ内でポイントを構築して描画する)。

そのラムダのスコープへのバインドを描画してから破棄する代わりに、Pointそのポイントをリストに保存したいと思います。ユーザーがボタンをクリックしたときにそのリストを読み取れるようにしたいと思います。

私はFRPスタイルについて少し調査をBehaviour行いましたEventhttp://hackage.haskell.org/package/threepenny-gui-0.4.2.0/docs/Reactive-Threepenny.html )。これは何かであると理解していますこれは、還元パターンのようなものを作成するのに役立ちますが、私の脳は溶け始めています.

別の StackOverflow の投稿 ( Mixing Threepenny-Gui and StateT ) に基づいて、Threepenny UI イベントに接続してイベント ストリームを作成し、accumBそのストリームからの各イベントをいくつかの状態動作のストリームに蓄積するために使用することになっていることを収集します。次に、その状態の動作を適用してイベントストリームに戻し、その最終ストリームで観察してUIを更新します(単純だと思います... xD)

少なくともそれが私が集めたものであり、リンクされた StackOverflow の質問への回答でコードをテストし、その特定の質問で提起された問題を解決しました。しかし、イベントストリーム (そのスニペットではカバーされていません) でx yマウスの位置をキャプチャし、それを使用してストリームを構築する必要があり、それが私が立ち往生している場所です。受け入れられた回答コードを目的に合わせて変更して実装しようとしましたが、ピースがどのように組み合わされるかを明らかに誤解しているため、大量の型エラーが発生しました。mousedownPoint

リンクされたStackOverflowの質問で受け入れられた回答のコードを変更しようとしたのは次のとおりです。

-- This *should* be the bit that converts 
-- (x, y) click events to Point x y Event stream
let canvasClick = UI.mousedown canvas
    newPointStream = (\(x, y) -> Point x y) <$ (canvasClick)

-- This *should* be the bit that turns the 
-- Point x y Event stream into a "behaviour" stream
counter <- accumB (Point 0 0) newPointStream

誰でも光を当てることができますか?私は壁にいます:-(

4

1 に答える 1

5

FRPの良い点の 1 つは、使用したくthreepenny-guiない場合FRP を使用する必要がないことです。ここでの最も簡単なアプローチは、おそらく次の変更可能な参照を使用することData.IORefです。

import Data.IORef

setup window = do
  -- ...

  pointsRef <- liftIO (newIORef [] :: IO (IORef [Point]))

  on UI.mousedown canvas $ \(x, y) -> do
    liftIO $ modifyIORef' pointsRef ((Point x y) :)

  on UI.click button $ const $ do
    points <- liftIO $ readIORef pointsRef
    -- use the list of points here

これにより、ポイントの変更可能なリストが作成pointsRefされ、それが に初期化され[]、マウスダウンごとに新しいポイントが先頭に追加されます。ボタンをクリックすると、ポイントのリストが読み込まれます。

別のアプローチは、FRP を使用することです。let pointEv = (uncurry Point) <$> UI.mousedown canvasを与えますEvent Pointlet pointPrependEv = fmap (\p -> \list -> p : list) pointEv次に、 を与えるために行うことができますEvent ([Point] -> [Point])。次に、 を使用して、毎回のポイントのリストを格納するpointsB <- accumB [] pointsPrependEvを取得します。Behavior [Point]最後に、 for each ボタンを押すために使用pointsB <@ UI.click buttonします。Event [Point]これで、ボタンを押すたびにイベントが作成され、その値が現時点でのポイントのリストになっているため、registerまたは の他の関数を使用して、このイベントで計算を実行できますthreepenny-gui。完全なプログラムは次のとおりです。

setup window = do
  -- ...

  let pointEv = (uncurry Point) <$> UI.mousedown canvas
      pointPrependEv = fmap (\p -> \list -> p : list) pointEv
  pointsB <- accumB [] pointPrependEv
  let buttonPressEv = pointsB <@ UI.click button
  -- use point list here via buttonPressEv

編集:あなたの質問で、上記のほとんどをすでに理解していることに気づきました。あなたの唯一のエラーは、やろうとしたことですaccumB [] newPointsStreamドキュメントを見ると、タイプはaccumB :: MonadIO m => a -> Event (a -> a) -> m (Behavior a); Event (a -> a)これには単純な ではなく が必要であることに注意してくださいEvent a。したがって、オリジナルはで使用する前に (新しいポイントごとに、ポイントの入力リストに追加する関数を返す) にEvent Point変換する必要があります。Event ([Point] -> [Point])accumB

于 2020-01-07T22:36:58.487 に答える