问题描述
使用reflex-gi-gtk-0.2.0.0时 我可以从事件中访问动态:
submitButtonE4 <- eventOnSignal submitButton #clicked
(
do
let processDyn dynCompany = do
case dynCompany of
Just company -> do
path <- chartAnnualROA company fileOptions800x600 --generateChart company
Gtk.imageClear chartimage
Gtk.set chartimage [#file := T.pack defaultReportPath]
--return x -- path
case T.null $ T.pack path of
True -> return "" --dynCompany
nothing -> return "" -- dynCompany
return $ ffor maybeCompanyDyn processDyn
>>= )
但是为了被评估,我需要将它绑定到一个标签:
sink submitClickStatusLabel [#label :== ffor submitButtonE4 (T.pack . show)]
它不像在 Dynamic (SpiderTimeline x) (IO (Maybe Company))
中那样工作。
所以我必须去获取动态绑定到的信息:
(
do
name <- Gtk.get companyCBoxBoxEntryWidget #text
case Map.lookup name companyMap of
Just company -> do
path <- chartAnnualROA company fileOptions800x600 --generateChart company
Gtk.imageClear chartimage
Gtk.set chartimage [#file := T.pack defaultReportPath]
return path
nothing -> return "../investingRIO/src/Data/Reports/initialChart.svg"
>>= )
现在我可以将它击沉并引起评估。
sink submitClickStatusLabel [#label :== ffor submitButtonE (T.pack . show)]
在使用第一种方法时,我找不到任何强制评估的方法。如何在不下沉到另一个小部件的情况下强制评估?
谢谢
解决方法
我认为您的大部分麻烦来自于您想要在 eventOnSignal
内进行大量工作的事实。这个地方不是为了执行您的业务逻辑的实际繁重工作,也没有为您提供适当的上下文来有效地处理反应性值,例如您目前遇到的 Dynamic
。>
eventOnSignal*
系列函数的实际用例是获取反应式网络的基本输入。按钮提供的输入不携带任何实际信息。它只提供何时按钮被点击的信息。对于这种情况,您通常不想直接使用 eventOnSignal
,而是使用 eventOnSignal0
,所以让我们这样做:
submitClickedE <- eventOnSignal0 submitButton #clicked
由此返回的类型是submitClickedE :: Event t ()
。如您所见,Event
有一个 ()
作为其值,这正是我们想要的,因为仅单击按钮本身不会产生任何值。但是您想对 IO
中的值调用 processDyn
生成函数,所以让我们首先构造您要执行的 IO
操作:
let processDynD = processDyn <$> dynCompany
此处的赋值类型为 processDynD :: Dynamic t (IO (Maybe Company))
。如您所见,IO
尚未执行。幸运的是,reflex
提供了一个操作来在响应值中执行 IO
操作,称为 performEvent :: Event t (Performable m a) -> m (Event t a)
。这种类型有两件事不太适合我们目前的需要。首先,它期望 monad 被执行为 Performable m
,而我们有 IO
,但我们稍后会谈到这一点。第二个也是更紧迫的问题是 performEvent
期望的是 Event
,而不是 Dynamic
。这是有道理的,因为您不能连续执行 IO
操作。您必须决定何时执行 IO
操作。
您希望在单击 submitButton
时执行 IO 的 AIUI。所以我们想要一个在 Event
触发时触发的 submitClickedE
,但它应该触发 processDynD
中的当前值。做这样的事情被称为“用Behavior
对Event
进行采样”并且可以使用运算符(<@)
来完成。在您的情况下,您想对 Dynamic
进行采样,但您始终可以使用 Dynamic
将 Behavior
变成 current
。因此,要获得预期的 Event
,您可以使用:
let processDynE = current processDynD <@ submitClickedE
分配的值为 processDynE :: Event t (IO (Maybe Company))
。但是正如您所看到的,IO
仍然没有被执行。我们现在可以使用前面讨论过的 performEvent
来做到这一点:
processedCompany <- performEvent $ runGtk <$> processDynE
我们使用 runGtk
将 IO
中的 processDynE
提升到所需的 Performable m
。返回值的类型为 processedCompany :: Event t (Maybe Company)
。您现在可以将其放入您的输出标签中,这是您的初衷:
sink submitClickStatusLabel [#label :== T.pack . show <$> processedCompany]
但请注意,与您最初的尝试不同,我们现在以 Event
而不是 Dynamic
结束。如果您确实需要所有这些中的 Dynamic
,则必须使用 Event
从 holdDyn initialValue processedCompany
构造它。但是,您必须提供一个 initialValue
,否则在第一次点击 Dynamic
之前 submitButton
没有任何值。
这是新版本,基于 Kritzefitz 的回答。
从组合框中选择公司的事件,与之前相同
companySelectionE <- eventOnAttribute companyCboxBoxEntryWidget #text
用行为代替动态。
companySelectionB <- hold Nothing $ ffor companySelectionE (`Map.lookup` companyMap)
generateChart(从 processDyn 重命名)返回一个 () 而不是 FilePath,这是一种强制求值的尝试,现在由 performEvent 完成。
let
generateChart company = do
case company of
Just companyJ -> do
chartAnnualROA companyJ fileOptions800x600
Gtk.imageClear chartImage
Gtk.set chartImage [#file := T.pack defaultReportPath]
return ()
Nothing -> return ()
submitClickedE 现在使用 eventOnSignal0 而不是 eventOnSignal
submitClickedE <- eventOnSignal0 submitButton #clicked
从所选公司创建图表现在是一种行为而不是动态。
let generateChartB = generateChart <$> companySelectionB
现在我使用
let generateChartE = generateChartB <@ submitClickedE
使用 performEvent,它消除了我正在创建和下沉的所有标签,以试图让我的 IO 进行评估。它还消除了从 generateChart 返回的 FilePath,以及强制求值的尝试。
processedCompany <- performEvent $ runGtk <$> generateChartE
谢谢帮我清理了很多东西,谢谢。 为了更容易阅读,这里是单引号:
companySelectionE <- eventOnAttribute companyCbox #text
companySelectionB <- hold Nothing $ ffor companySelectionE (`Map.lookup` companyMap)
let
generateChart company = do
case company of
Just companyJ -> do
chartAnnualROA companyJ fileOptions800x600
Gtk.set chartImage [#file := T.pack defaultReportPath]
return ()
Nothing -> return ()
submitClickedE <- eventOnSignal0 submitButton #clicked
let generateChartB = generateChart <$> companySelectionB
let generateChartE = generateChartB <@ submitClickedE
processedCompany <- performEvent $ runGtk <$> generateChartE