6

我目前正在使用 reactive-banana 和 reactive-banana-wx 包重新设计一个遗留的 wxHaskell。然而,为了避免动态网络构建(我遇到了 MVar 上的线程块),我现在通过预先构建一组固定的 wxHaskell 小部件来模拟这一点,我根据需要设置了这些小部件的可见性。可见性由sink采用Behavior. 但是,wxHaskell 要求在所有这些小部件都通过 适当地修改sink之后,需要随后更改包含这些小部件的面板的布局。这意味着sink-ing 实际上应该是网络的一部分,因此它是一个可以触发并等待布局更改的事件。就目前而言,一个sinksink将您“带出”事件网络,操作完成后无法触发事件。我确实尝试过适应sink这样的事情:

sink' :: Frameworks t =>
    w -> [Prop' t w] -> Moment t (Event t ())
sink' widget props = do
    es <- mapM sink1 props
    return $ unions es
  where
    sink1 (attr :== b) = do
        x <- initial b
        liftIOLater $ set widget [attr := x]
        e <- changes b
        return $ (\x -> unsafePerformIO $ set widget [attr := x]) <$> e

然而,unsafePerformIO并没有被执行。如何实现所需的行为,即允许(wxHaskell)IO 通过Event?

4

1 回答 1

2

基本上,您似乎想确保reactimate按特定顺序执行 IO 操作?也就是说,您要确保在设置小部件属性之后设置布局。

有几种方法可以指定顺序:

  1. 使用union,unionWith和/或collect确定同时事件的顺序。
  2. 使用按它们在monadreactimate中出现的顺序执行的事实。(虽然严格来说,当您从动态事件切换中Moment使用组合器时,这不再适用。)observeE

在您的特定情况下,可以按如下方式应用这些想法。

对于 1,你可以制作一个包含 IO 动作的事件,稍后将其与布局结合起来

sink' :: Frameworks t =>
    w -> [Prop' t w] -> Moment t (Event t (IO ()))
sink' widget props = do
    es <- mapM sink1 props
    return $ foldr1 (unionWith (>>)) es
  where
    sink1 (attr :== b) = do
        x <- initial b
        liftIOLater $ set widget [attr := x]
        e <- changes b
        return $ (\x -> set widget [attr := x]) <$> e

对于2,您可以简单地使用普通sink功能,只需确保最后设置布局即可。

do
    sink widget1 [ visible :== bBool ]
    sink window1 [ layout  :== bLayout ]

monad 中函数的顺序sink保证了布局是最后设置的。


另请注意,从反应香蕉 0.7 开始,您可以使用动态事件切换来对一组可变的小部件进行建模。有关演示,请参阅BarTab.hs 示例。此示例还设置了布局。

您表示在使用动态网络时遇到了 MVar 块。这可能是因为您创建小部件的方式会触发网络中的另一个事件。不幸的是,这在语义上是不合理的——它对应于取决于自身未来版本的值——并且程序通过陷入谷底来做出响应。

于 2012-11-27T16:46:46.317 回答