When using reflex-gi-gtk-0.2.0.0 I can access a dynamic from within an event:
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
>>= )
But in order to be evaluated, I need to bind it to a label:
sink submitClickStatusLabel [#label :== ffor submitButtonE4 (T.pack . show)]
which does not work as it is in Dynamic (SpiderTimeline x) (IO (Maybe Company)).
So instead I must go and get the info that the dynamic was bound to:
(
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"
>>= )
and now I can sink it and cause evalution.
sink submitClickStatusLabel [#label :== ffor submitButtonE (T.pack . show)]
I am unable to find any way to force the evaluation when using the first method. How do I force the evalution without sinking to another widget?
Thanks
I think most of your trouble comes from the fact, that you want to do substantial amounts of work inside
eventOnSignal. This place is not intended to do the actual heavy lifting of your business logic and it doesn't provide you with the proper context to effectively work with reactive values, such asDynamics, as you are currently experiencing.The actual use case for the
eventOnSignal*family of functions is to obtain basic inputs for your reactive network. The input provided by a button doesn't carry any actual information. It just provides the information when the button has been clicked. For cases like this you usually don't want to useeventOnSignaldirectly, but rathereventOnSignal0, so let's do that:The type returned by this is
submitClickedE :: Event t (). As you can see, theEventhas a()as its value, which is what we want, because merely clicking the button doesn't produce any value by itself. But you want to call anIO-producing function on the value insideprocessDyn, so let's first construct theIOaction you want to execute:The assignment here has the type
processDynD :: Dynamic t (IO (Maybe Company)). As you can see, theIOhasn't been executed yet. Luckilyreflexprovides an operation to executeIOactions inside reactive values, calledperformEvent :: Event t (Performable m a) -> m (Event t a). There are two things about this type, that don't quite fit what we need at the moment. First, it expects the monad to be performed to be aPerformable mwhereas we haveIO, but we will get to that in a moment. The second and more pressing concern is thatperformEventexpects anEvent, not aDynamic. This makes sense, because you can't execute anIOaction continuously. You have to decide when theIOaction is executed.AIUI you want the IO to be executed, when the
submitButtonis clicked. So we want anEventthat fires wheneversubmitClickedEfires, but it should fire the current value insideprocessDynD. Doing something like this is called “sampling aBehaviorwith anEvent” and can be done using the operator(<@). In your case you want to sample aDynamic, but you can always turn aDynamicinto aBehaviorusingcurrent. So to get the expectedEventyou can use this:The assignment has the value
processDynE :: Event t (IO (Maybe Company)). But as you can see, theIOstill hasn't been executed. We can now do that usingperformEventas discussed earlier:We use
runGtkto lift theIOinprocessDynEto the requiredPerformable m. The returned value has the typeprocessedCompany :: Event t (Maybe Company). You can now sink this into your output label, as was your original intention:Note though, that unlike your original attempt, we now ended up with an
Eventinstead of aDynamic. If you actually need aDynamicfrom all of this, you have to construct it from theEventusingholdDyn initialValue processedCompany. But then you have to provide aninitialValuebecause otherwise there is no value for theDynamicbefore thesubmitButtonhas been clicked for the first time.