how to merge two signals with SampleOn in Elm - elm

I try to merge two signals. One is Mouse.clicks and another is Keyboard.space.
On clicks, I should get a Signal(Int,Int) from Mouse.position as return value
On space, I should get something different so I can identify different signal is triggered.
My idea is:
type Event = Click | Space
mergedSignal : Signal Event
mergedSignal =
let
clickSignal = map (\event -> Click) Mouse.clicks
timeoutSignal = map (\event -> Space) Keyboard.space
in
merge clickSignal timeoutSignal
and get position somehow:
positionOnClickSignal:Signal (Int,Int)
positionOnClickSignal = sampleOn Mouse.clicks Mouse.position
Obviously, it is wrong.

It sounds like you want the mouse position to carry over as part of the event. In that case, you could redefine Event as
type Event
= Click (Int, Int)
| Space
Inside your mergedSignal, the clickSignal is currently just checking for Mouse.clicks but based on your description and other example, I think you actually want that to be based off positionOnclickSignal, which gives you a Signal (Int, Int), and using that, you can now populate the (Int, Int) portion of the Click (Int, Int) event like this:
clickSignal =
map Click positionOnClickSignal
You'll notice that I took out the parenthesis in the above. That is more idiomatic for Elm, because Click is in essence a function that takes one (Int, Int) parameter, which will be passed in from the map function. It could have easily been written like this:
clickSignal =
map (\pos -> Click pos) positionOnClickSignal
Now, if you're just trying to see some debug text of this on screen, a quick and easy way to go about that is to use show from the Graphics.Element package.
import Graphics.Element exposing (show)
main =
map (show << toString) mergedSignal
That will give you some debug text shown as the only thing on the page, and you could easily toss it up on http://elm-lang.org/try for testing.

Related

How to update "variables" in main with IO and GTK [duplicate]

Trying to learn to write applications with Gtk2Hs I'm getting difficulties bridging the gap between the event driven Gtk2HS and the persistent state of my model. So to simplify, lets say that I have this simple application
module Main where
import Graphics.UI.Gtk
import Control.Monad.State
main = do
initGUI
window <- windowNew
button <- buttonNew
set button [buttonLabel := "Press me"]
containerAdd window button
-- Events
onDestroy window mainQuit
onClicked button (putStrLn ---PUT MEANINGFUL CODE HERE---)
widgetShowAll window
mainGUI
and the state of my application is how many times the button has been pressed. Seeing other posts like this they rely on MVars or IORefs which do not seem satisfactory to me, because in the future maybe I will want to refactor the code so the state lives on its own context.
I think that the solution should use the State monad using a step function like:
State $ \s -> ((),s+1)
but I'm not sure about the implications, how to do that in the above code or even if that monad is the right solution to my problem.
There's basically two approaches:
Use a pointer of some kind. This is your IORef or MVar approach. You can hide this behind a MonadState-like interface if you like:
newtype GtkT s m a = GtkT { unGtkT :: ReaderT (IORef s) m a } deriving (Functor, Applicative, Monad, MonadIO)
runGtkT = runReaderT . unGtkT
instance MonadIO m => MonadState s (GtkT s m) where
get = GtkT (ask >>= liftIO . readIORef)
put s = GtkT (ask >>= liftIO . flip writeIORef s)
Pull an "inversion of control" style trick. Write a callback that prints a number, then replaces itself with a new callback that prints a higher number.
If you try to use State or StateT directly, you're gonna have a bad time.

Cannot read property 'kids' of undefined - or how to break a circular dependency of signals in Elm?

While elm-make succeeds, I get the following error in the browser:
Cannot read property 'kids' of undefined
I assume it's because I have a circular dependency of signals:
model -> clicks -> model
Here is the relevant code:
model : Signal Model
model =
Signal.foldp update initialModel clicks
clicks : Signal Action
clicks =
let
clickPosition = Mouse.position
|> Signal.sampleOn Mouse.clicks
tuplesSignal = Signal.map3 (,,) clickPosition Window.dimensions model
in
...
It feels like model is implemented as a common practice in Elm, so I should challenge the clicks -> model dependency.
Here is some context:
I'm building a sliding puzzle game using canvas:
When user clicks a tile that can move, it should move. Otherwise, the click should be ignored.
clicks will produce the following actions: Left, Right, Up, Down
For example, if user clicks on tiles 12, 11, 8, 15 (in this order), clicks should be: Down -> Right -> Up
The problem is, that in order to calculate which tile was clicked, I need to know the board dimensions (e.g. 4 rows and 4 columns in the picture above). But, board dimensions are stored in the model (imagine user interface that allows users to change board dimensions).
How do I get out of this circular dependency?
In this case I think you should go more low-level in what you call an input, and accept click positions as inputs. Then you can compose an update function in your Signal.foldp out of two others:
The first turns the clicks and model into a Maybe Direction (assuming the Left, Right, Up, Down are constructors of type Direction)
The second function that takes a Direction value and the model to calculate the new model. You can use Maybe.map and Maybe.withDefault to handle the Maybe part of the result of the first function.
Separating these two parts, even though you produce and consume Direction immediately, makes your system more self-documenting and shows the conceptual split between the raw input and the restricted "actual" inputs.
P.S. There are conceivable extensions to the Elm language that would allow you to more easily write this input preprocessing in signal-land. But such extensions would make the signal part of the language so much more powerful that it's unclear if it would make "the right way" to structure programs harder to discover.

How to convert Element into Form?

How can I convert Mouse.position into Form, so I can display it in a collage? The following code displays <Signal> instead of the actual mouse coordinates:
render (x, y) =
let mousePos = toForm (show Mouse.position)
in collage 400 400 [mousePos]
It's curious that in this example http://elm-lang.org/examples/mouse-position, the show function actually transforms Mouse.position into a string with coordinates, but that is because the show function is used to filter a Signal(Int, Int) into a tuple of Signal values.
So my question is, how do I convert a Signal(Int, Int) into a Form, so that it shows the tuple values?
You are looking for Graphics.Collage.toForm which has the type Element -> Form.
It also sounds like you don't quite understand what Signal.map is doing. It takes a function to be applied to each value of a Signal. I've tried to use it in several contexts in the following example.
import Graphics.Element exposing (..)
import Graphics.Collage
import Graphics.Collage exposing (Form)
import Mouse
--This is the function you are trying to construct.
--It takes in a position, converts it to an element,
--using show and then converts it to a Form.
formPosition : (Int, Int) -> Form
formPosition pos =
let element = show pos -- element : Element
in Graphics.Collage.toForm element
-- We now want to apply our formPosition function to the
-- Signal containing all mouse position changes.
-- So we use Signal.map to apply formPosition to all values
-- of Mouse.position
formSignal : Signal Form
formSignal = Signal.map formPosition Mouse.position
-- Eventually we want to render this on the screen and the
-- function to do this requires a List Form not just a single
-- Form. So we write a function which returns a Singleton list
-- and apply it to each value in our formSignal.
formListSignal : Signal (List Form)
formListSignal = Signal.map (\n -> [n]) formSignal
-- Finally, we must turn that into a Signal Element to render
-- on the screen. We partially apply Graphics.Collage.collage
-- to return an element of size 400x400 and apply it to the
-- values of formListSignal by using Signal.map again
elementSignal : Signal Element
elementSignal = Signal.map (Graphics.Collage.collage 400 400) formListSignal
-- Finally we hand this off to main and it renders
main : Signal Element
main = elementSignal
A simpler version would likely combine all of the conversions into a single function. I just wanted to emphasize how Signal.map worked. I hope this helps!

Create a Signal from a List

Is it possible to create a Signal from a List? Essentially what I want is something with the signature List a -> Signal a. I know that a Signal represents a time-varying value and so something like this doesn't actually make any sense (i.e. I can't think of a reason to use it in production code).
I could see applications of it for testing though. For example, imagine some function which depended on the past values of a Signal (via foldp for instance) and you wanted to make assertions about the state of the system given the signal had received values x, y, and z.
Note that there wouldn't have to be anything special about the Signal denoting that it only would ever receive a fixed number of values. I'm thinking of it more like: in production you have a Signal of mouse clicks, and you want to test that from a given starting position, after a given set of clicks, the system should be in some other known state. I know you could simulate this by calling the function a fixed number of times and feeding the results back in with new values, I'm just wondering if it is possible.
I guess it's possible. You use a time-based signal, and map the values from the list over it:
import Time
import Graphics.Element exposing (show)
list = [1..10]
signalFromList : List a -> Signal a
signalFromList list =
let
(Just h) =
List.head list
time =
Time.every Time.second
maybeFlatMap =
flip Maybe.andThen
lists =
Signal.foldp (always <| maybeFlatMap List.tail) (Just list) time
in
Signal.filterMap (maybeFlatMap List.head) h lists
main = Signal.map show <| signalFromList list
However!
It shouldn't be hard to do testing without signals. If you have a foldp somewhere, in a test you can use List.foldl over a list [x,y,z] instead. That should give you the ability to look at the state of your program after inputs x, y, z.
I don't think there is any way it can be done synchronously in pure elm (Apanatshka's answer illustrates well how to set up a sequence of events across time and why it's a bad idea). If we look at how most Signals are defined, we'll see they all head down into a native package at some point.
The question then becomes: can we do this natively?
f : List a -> Signal a
I often think of (Signal a) as 'an a that changes over time'. Here we provide a List of as, and want the function to make it change over time for us.
Before we go any further, I recommend a quick look at Native/Signal.js: https://github.com/elm-lang/core/blob/master/src/Native/Signal.js
Let's say we get down to native land with our List of as. We want something a bit like Signal.constant, but with some extra behaviour that 'sends' each a afterwards. When can we do the sending, though? I am guessing we can't do it during the signal construction function, as we're still building the signal graph. This leaves us a couple of other options:
something heinous with setTimeout, scheduling the sending of each 'a' at an appropriate point in the future
engineering a hook into the elm runtime so that we can run an arbitrary callback at the point when the signal graph is fully constructed
To me at least, the former sounds error prone, and I hope the latter doesn't exist (and never does)!
For testing, your suggestion of using a List fold to mimic the behaviour of foldp would be the way I would go.

How to create a custom mouse cursor in Matplotlib

I am interested in creating a custom mouse cursor, so that during drag and pick events on certain lines or points, the mouse changes from an arrow to a hand (or other symbol).
What is the best method of doing this?
I assume this is possible since the mouse cursor changes to a small cross hair during zoom operations. If possible, a solution using the PyQt/PySide backend would be preferable.
What you need is mpl_canvas. Follow this tutorial to set one up.
With an mpl_canvas, you can then set up events that get triggered.
fig = matplotlib.figure.Figure()
cid = fig.canvas.mpl_connect('button_press_event', your_method)
There are several kinds of signals under here (listed under Events).
With your signal set up, your_method gets called, with an event parameter. So do something like:
def your_method(event):
print('Your x and y mouse positions are ', event.xdata, event.ydata)
Click on the corrosponding Class and description links to see what exactly is in event. for a specific mpl_canvas Event.
In your specific case, to change how the mouse looks your_method should look something like:
def your_method(event):
#changes cursor to +
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))