3

使用 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)]

使用第一种方法时,我找不到任何强制评估的方法。如何在不陷入另一个小部件的情况下强制评估?

谢谢

4

2 回答 2

2

这是基于 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

于 2021-03-05T14:45:12.890 回答
2

我认为你的大部分麻烦来自于你想在内部做大量的工作eventOnSignal。这个地方并不打算对您的业务逻辑进行实际的繁重工作,并且它没有为您提供适当的上下文来有效地使用Dynamic您当前遇到的反应值,例如 s。

eventOnSignal*函数族的实际用例是为您的反应式网络获取基本输入。按钮提供的输入不携带任何实际信息。它仅在单击按钮时提供信息。对于这样的情况,您通常不想eventOnSignal直接使用,而是eventOnSignal0,所以让我们这样做:

submitClickedE <- eventOnSignal0 submitButton #clicked

this 返回的类型是submitClickedE :: Event t (). 如您所见,Eventa()作为它的值,这正是我们想要的,因为仅仅单击按钮本身不会产生任何值。但是你想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 被执行为 a Performable mwhile we have IO,但我们稍后会讲到。第二个也是更紧迫的问题是performEvent期望 a Event,而不是 a Dynamic。这是有道理的,因为你不能IO连续执行一个动作。您必须决定何时执行IO操作。

AIUI 您希望在submitButton单击时执行 IO。所以我们想要一个Event在触发时submitClickedE触发,但它应该触发内部的当前值processDynD。执行此类操作称为“使用 a 采样 a BehaviorEvent,可以使用 operator 来完成(<@)。在您的情况下,您想对 a 进行采样Dynamic,但您始终可以将 aDynamic变成Behaviorusing current。因此,要获得预期Event,您可以使用以下命令:

let processDynE = current processDynD <@ submitClickedE

赋值有值processDynE :: Event t (IO (Maybe Company))。但正如你所看到的,IO仍然没有被执行。我们现在可以使用performEvent前面讨论的方法来做到这一点:

processedCompany <- performEvent $ runGtk <$> processDynE

我们使用runGtk提升IOprocessDynE所需的Performable m. 返回值的类型为processedCompany :: Event t (Maybe Company)。您现在可以将其放入您的输出标签中,就像您的初衷一样:

sink submitClickStatusLabel [#label :== T.pack . show <$> processedCompany]

但请注意,与您最初的尝试不同,我们现在以 aEvent而不是 a结束Dynamic。如果你真的需要 a Dynamicfrom 所有这些,你必须从Eventusing构造它holdDyn initialValue processedCompany。但是你必须提供一个initialValue,否则在第一次点击Dynamic之前没有任何价值。submitButton

于 2021-03-05T11:00:03.390 回答