I am learning how to use the Gloss library to make some animations in Haskell.
Consider the code below which animates a circle that shrinks and expands its radius with time.
import Graphics.Gloss
import Graphics.Gloss.Interface.Pure.Game
type Radius = Float
type RealTime = Float
data World = World Radius RealTime
main :: IO ()
main
= do let initWorld = World 100.0 0
let windowSize = (800,800)
let windowPosition = (200,200)
putStrLn "Before play"
play (InWindow "Wobbling Circle" windowSize windowPosition)
white 40 initWorld renderWorld handleEvent stepWorld
putStrLn "After play"
renderWorld :: World -> Picture
renderWorld (World rad rtime ) = Pictures [ Circle rad ]
handleEvent :: Event -> World -> World
handleEvent _ = id
stepWorld :: Float -> World -> World -- wobbling circle
stepWorld _ (World _ rtime) = World (100 * sin rtime) (rtime+0.1)
I compiled this code with ghc --make -O2 -threaded Main.hs on an Ubuntu 14.04 machine.
When I run the code, the "Before play" statement is printed out and then the animation starts as expected. However, when I close the animation window, the code terminates immediately without printing the "After Play" statement. Why is that?
Presumably you are using the GLUT backend (the default). Take a look at some of the source code for gloss (exactly as it appears with comments):
instance Backend GLUTState where
initBackendState = glutStateInit
initializeBackend = initializeGLUT
-- non-freeglut doesn't like this: (\_ -> GLUT.leaveMainLoop)
exitBackend = (\_ -> System.exitWith System.ExitSuccess)
....
When it exits the main loop, gloss will call exitBackend, whatever it is. For GLUT, that simply calls System.exitWith which naturally will terminate your program. The more sensible thing to do would be to call leaveMainLoop but as the comment in the code says, implementations of glut other than freeglut don't work well with that function (why? who knows. This is what the authors of gloss claim).
Your potential solutions are to use freeglut specifically and modify the source code of gloss to change exitBackend; or use the GLFW backend, which doesn't have this problem.
Related
this is the bouncing ball code. I'm trying to make 'appendFile' run on the update function, so when the ball bounces off the wall, then 'appendFile' will write the px and px values to the file "log.txt"
import Graphics.Gloss
import Graphics.Gloss.Data.ViewPort (ViewPort)
main :: IO ()
main =
simulate
(InWindow "Bouncing ball" (width, height) (50, 50))
white
30
initial
view
update
but I'm having trouble because 'appendFile' only runs on signiture IO. And I don't know how to apply it in a situation like this
update :: ViewPort -> Float -> World -> World
update _ _ World {position = (px, py), velocity = (vx, vy)} =
let
appendFile "Log.txt" ("" ++ show px ++ " " + show py ++ "")
newPx = px + vx
newPy = py + vy
newVx = if newPx >= fromIntegral width || newPx <= 0 then - vx else vx
newVy = if newPy >= fromIntegral height || newPy <= 0 then - vy else vy
in World {position = (newPx, newPy), velocity = (newVx, newVy)}
Haskell is really strict about side effects. Writing to a file is a side effect, and a pure function (like your update) is not allowed to have side effects.
If you merely want to record the data for debugging then you can use the infamous accursed unsafePerformIO, which provides a back door into the IO monad for pure computations. The reason for the "unsafe" bit of the name is that this makes no promises about how often the IO action gets run, or even if it gets run at all.
BUT the code you have above won't actually call appendFile. In fact that is a syntax error; a let introduces values which might be used in the code, but you have no assignment for the result of appendFile.
You would need something more like:
let
... omitted
in seq
(unsafePerformIO $ appendFile "Log.txt" (show px ++ " " ++ show py ++ "\n")
World {position = (newPx, newPy), velocity = (newVx, newVy)}
seq is a magic function which is "strict" in its first argument, so the unsafePerformIO gets evaluated before the new World, even though nothing ever uses the result of that first argument.
However this is a kluge. You should not use unsafePerformIO for production code. It is a lie to the compiler. If you make a habit of it then the compiler will get its revenge, and it will not be pretty.
If this is for production code then you should instead use simulateIO. This takes an update function which returns an IO value, so then you can write update to return an IO World and everyone will be happy.
For my diagramming tool, I'd like to keep the code of the core model isolated from the GUI.
In the following example, the "state " is passed around with vDiag, which is a Tvar. This is a design decision in wx. Now, For my diagramming tool, I 'd like the core model to be "stored" in a fgl Graph, (with complex types in it), and wx will be given only a view on it; say in this example, a list of points for read access when painting, and some functions to write when clicking, dragging, etc.. . I thought first to some Monad stack, but even combining a StateT and the IO from wx does not look trivial , because the io actions are spread all over in the code of the callback (on click , on paint…etc ). It feels like having IO at the bottom of the stack does not fit well anymore.
so how to you pass a STate around, or is it not the way to go ? ( I intuition this is a classic. is this how RFP started ? )
(In the code, which paints red circle when there is a click , the list of points are passed around in a Tvar vDiag. I have tagged "--fgl" where equivalent state accessor would go. and set up a basic fgl test graph accessors to illustrate . I would like to put in a State)
(I originally tried to give it a go without FRP - reactive banana, to understand the problem, but I think I may have already hit it ;-)
module Main where
import Graphics.UI.WX hiding (empty)
import Data.Graph.Inductive
main
= start ballsFrame
ballsFrame
= do
vDiag <- varCreate []
--gDiag <- initg -- fgl
frame <- frame [text := "Demo"]
p <- panel frame []
file <- menuPane [text := "&File"]
quit <- menuQuit file [on command := close frame]
set frame [text:= "testing", menuBar := [file] ]
set p [on click := drawBins vDiag p , on paint := paintDiag vDiag ]
-- fgl pass the var around
return ()
where
drawBins d ppanel pt =
do varUpdate d (pt:)
-- addpoint f g -- fgl : insert a point
repaint ppanel
-- paint the balls
paintDiag vdiag dc view
= do balls <- varGet vdiag -- getPointsFromGraph
-- fgl : change to get the list of points
set dc [brushColor := red, brushKind := BrushSolid]
mapM_ (drawDiag dc) balls
drawDiag dc pt
= circle dc pt 10 []
-- basic fgl test graph accessors I would like to put in a State and replace vDiag
initg:: Gr Point String
initg = mkGraph [(1,pt 10 10),(2,pt 30 30)] [(1,2,"truc"), (2,1,"revtruc")]
getPointsFromGraph :: Graph gr => gr b b1 -> [b]
getPointsFromGraph g = map snd $ labNodes g
-- getPointsFromGraph initg = [Point {pointX = 10, pointY = 10},Point {pointX = 30, pointY = 30}]
addpoint :: DynGraph gr => a -> gr a b -> gr a b
addpoint p g = -- add a point p into graph p
insNode (4,p) g
I'm not so experienced in Haskell and I've just started using Gtk2Hs so this might be a silly question.
I have the following Line type defined:
type Coord = (Int,Int)
type Line = (Coord,Coord)
And I have a function which draws a list of Lines on a DrawingArea. The problem is that this function draws all the Lines at the same time but I would like to draw them one at a time with a little delay between two Lines.
render :: [Line] -> IO ()
render lines =
do initGUI
win <- windowNew
windowSetTitle win "Animation"
win `onDestroy` mainQuit
can <- drawingAreaNew
can `onSizeRequest` return (Requisition 400 400)
can `onExpose` drawCanvas lines can
but <- buttonNewWithLabel "Quit"
but `onClicked` mainQuit
hbox <- hBoxNew False 0
boxPackStart hbox but PackRepel 150
vbox <- vBoxNew False 5
containerAdd vbox can
containerAdd vbox hbox
containerAdd win vbox
widgetShowAll win
mainGUI
This function gets called when the DrawingArea is exposed:
drawCanvas :: [Line] -> DrawingArea -> event -> IO Bool
drawCanvas lines can _evt =
do dw <- widgetGetDrawWindow can
drawWindowClear dw
gc <- gcNew dw
mapM_ (\(a,b) -> drawLine dw gc a b) lines
return True
I have considered using a StateT to keep track of which Lines have yet to be drawn but I don't know how to implement the animation. Even if a call widgetShowAll every time after altering the state, the window doesn't get shown until mainGUI is called.
Is it possible to make a new thread that continuously updates the state while drawCanvas somehow takes care of the drawing? If so, could somebody please show me an example of such behavior? Or is there a better approach perhaps?
Gtk2Hs allows to set up "timers" that call functions at regular intervals. So I would do the following:
Because most of Gtk2Hs happens in the IO monad, use an IORef or an MVar to store the state of the animation and change it anywhere.
Add a call to timeoutAdd before mainGUI, like this: timeoutAdd (update can lines) 100 to set up a timer that will run every 100 milliseconds and call a new function "update" with the DrawingArea and the IORef/MVar with the animation state as parameters.
In the function "update" change the animation state and call widgetQueueDraw can so that the drawing area is re-exposed. That will call "drawCanvas" automatically because it is connected to the expose event. "update" has to return an IO Bool. Returning False will stop the timer.
To update the animation state, I would use a tuple. The first element would store the line to be drawn, and the second element would store the list of the other lines.
I find it meaningful to update the animation's state and the drawing in separate functions.
I have a program which performs a long-going calculation where the result is shown as a plot.
I am currently using Chart-0.14 for this. I want to show the partial results, and update during calculations.
Graphics.Rendering.Chart.Gtk.updateCanvas :: Renderable a -> DrawingArea -> IO Bool seems to do that, but I do not find any way to get a DrawingArea from the plot. The function renderableToWindow :: Renderable a -> Int -> Int -> IO () does not return anything (and furthermore it does not return before the window is closed).
I would like to do something like the following:
main = do
drawingArea = forkRenderableToWindow (toRenderable $ plotLayout $
plot [0,0.1..10] sin "sin(x)") 640 480
updateCanvas (toRenderable $ plotLayout $ plot [0,0.1..10] sin "sin(x)") drawingArea
How should I do this? Would I need to reimplement the functions in Graphics.Rendering.Chart.Gtk with a version that returns the DrawingArea and in some way (how would I do this? forkIO?) returns immediately without closing the window?
You are looking for createRenderableWindow and then you need to use the GTK operations to work on the given Window - I don't think the Chart package exports any higher level operations on Windows.
EDIT2: So ignore the below - it doesn't work even with GUI initilization. My comment was a guess based on types.
EDIT:
Here is some example code. Understand, I'm just piecing things together based on the types. There might be better ways to do things if you ask someone who actually knows the library.
Below we use:
createRenderableWindow - this was the crux of my answer
castToDrawingArea - This is needed to get a DrawingArea from the Window type provided by GTK. These casts are taking place of C++ OO inheritance, I think.
widgetShowAll - because we haven't actually displayed the window, we best do that. I stole this function after looking at the source for renderableToWindow.
updateCanvas - I just saw this in the haddock documentation and figured it is why you wanted a DrawingArea in the first place.
Now for the code:
import Graphics.Rendering.Chart.Gtk
import Graphics.Rendering.Chart.Renderable
import Graphics.UI.Gtk.Misc.DrawingArea
import qualified Graphics.UI.Gtk as G
main = do
win <- createRenderableWindow emptyRenderable 400 400
let draw = castToDrawingArea win
G.widgetShowAll win
updateCanvas emptyRenderable draw
I have a DrawingArea onto which I can draw using primitives such as drawRectangle and drawLine. How do I draw text onto that area? I'm most interested in something that quickly outputs a single line of text.
Graphics.UI.Gtk.Gdk.Drawable.layoutLine seems to be what I want, but it wants a Graphics.Rendering.Pango.Layout.LayoutLine as input. How do I construct that LayoutLine?
Are there better alternatives than doing it this way?
Thanks!
I don't know if you would consider using Cairo. If so, I think the function showText may be what you are looking for. Use the cairo function moveTo to move to a specific location before writing the text. This is one of the simplest working examples I can produce:
import Graphics.UI.Gtk
import Graphics.Rendering.Cairo
main :: IO ()
main = do
initGUI
window <- windowNew
drawingArea <- drawingAreaNew
containerAdd window drawingArea
drawingArea `onExpose` (\_ -> renderScene drawingArea)
window `onDestroy` mainQuit
windowSetDefaultSize window 640 480
widgetShowAll window
mainGUI
renderScene :: DrawingArea -> IO Bool
renderScene da = do
dw <- widgetGetDrawWindow da
renderWithDrawable dw $ do setSourceRGBA 0.5 0.5 0.5 1.0
moveTo 100.0 100.0
showText "HelloWorld"
return True
I found the following to be an excellent guide, even though it was not for Haskell:
http://zetcode.com/tutorials/cairographicstutorial/cairotext/
I've found the way to do this with Pango.
layout <- widgetCreateLayout drawAreaWidget stringToDraw
Then you can use this newly-created layout with functions such as drawLayout and drawLayoutWithColors.