0

I need to do some action while button is pressed. How can I do it?

I have version 0.12.4.

P. S.: For some reason, onButtonActivate in

import Graphics.UI.Gtk
import Control.Concurrent
main = do
    initGUI
    window <- windowNew

    but <-buttonNewWithLabel "Write A"

    onButtonActivate  but $ do
        putStr "A"
        threadDelay 1000000
        return()

    containerAdd window but

    widgetShowAll window
    onDestroy window mainQuit
    mainGUI

do not do anything.


Also, it's good to go, if action will be done repeatedly while pressed some key on keyboard.

Anon Imous
  • 415
  • 2
  • 11
  • I suppose you should spawn a thread on 'pressed' event and kill it on 'released' one. I don't think gtk is firing some kind of event continuously while button is pressed. – user3974391 Feb 27 '14 at 07:14
  • Wouldn't it be better to use a ToggleButton? It supports two states (active, i.e. pushed down, and inactive) – Alfonso Villén Feb 27 '14 at 22:15

2 Answers2

1

According to the docs onButtonActivate is depreciated so you probably shouldn't use it. Im having trouble finding the correct way though, there probably are some generics signals somewhere that you should use. You can try my solution that uses onPressed and onRelease (these are also noted as depreciated). You could do as suggested in the comment and fork a thread:

import Control.Concurrent (forkIO, killThread, threadDelay)
import Control.Monad (void, when)

whilePressed button action = do
    onPressed button $ do
        threadId <- forkIO go
        onReleased button (killThread threadId)
    where
        go = do
            action
            threadDelay 1000000
            go

Then rewrite your main to do:

whilePressed but (putStr "A")

Im not sure if this is safe though, as it seems it could be possible for the buttonReleased event to be fired before killThread is registered. It might be safer to use an IORef:

import Data.IORef


whilePressed button action = do
    isPressed <- newIORef False

    onPressed button $ do
        writeIORef isPressed True
        void $ forkIO $ go isPressed

    onReleased button (writeIORef isPressed False)

    where
        go isPressed = do
            c <- readIORef isPressed
            when c $ do
                action
                threadDelay 1000000
                void $ forkIO $ go isPressed

Unfortunately I haven't compiled or tested the code sine I cannot install GTK on this computer, but let me know if you have any issues.

Reite
  • 1,677
  • 10
  • 12
1

This uses a ToggleButton, a "timeout" object (see timeoutAdd in the module System.Glib.MainLoop in the docs) and an IORef. A timer is started when the ToggleButton is pressed down, but only if no other timer is currently running (that's the purpose of the IORef). If the button is released, the timer is stopped. The timer's callback function returns IO False to stop and destroy the timer object.

import Graphics.UI.Gtk
import Data.IORef
import Control.Monad(void, when)

action but idle = do
    butDown <- get but toggleButtonActive
    if butDown
        then do
            putStrLn "A"
            writeIORef idle False
            return True
        else do
            writeIORef idle True
            return False

main = do
    initGUI
    window <- windowNew

    but <-toggleButtonNewWithLabel "Write A"

    idle <- newIORef True

    on but toggled $ do
        butDown <- get but toggleButtonActive
        isIdle  <- readIORef idle
        when (butDown && isIdle) $ void $ timeoutAdd (action but idle) 1000

    containerAdd window but

    widgetShowAll window
    on window objectDestroy mainQuit
    mainGUI

The preferred way of registering signal callbacks is using "on". Also note that "on window objectDestroy mainQuit" correctly destroys the window and stops the Gtk main loop (you version didn't destroy the timers in GHCi, they kept running after calling "main" again).

Alfonso Villén
  • 373
  • 1
  • 9