1

我无法从标签和操作列表中生成按钮。我很确定这与 IO 未得到评估有关('on button buttonActivated action'),但我不确定如何修复它。

我有一个(标签,命令)元组列表,我用它来生成一个按钮,它是关联的 IO () 操作。

调用的函数:'on button buttonActivated action'被调用,但按钮仍然没有注册点击。

module GtkTest where

import qualified Data.Map as M
import Graphics.UI.Gtk
import Graphics.UI.Gtk.Buttons.Button
import Graphics.UI.Gtk.Windows.Window
import Graphics.UI.Gtk.Layout.VBox
import Control.DeepSeq

type Command = String
type ButtonLabel = String
type ButtonAction = IO ()
type ButtonDesc = (ButtonLabel, Command)
data ButtonInfo = ButtonInfo {
    buttonLabel :: ButtonLabel
  , buttonAction :: ButtonAction
  , buttonIo :: IO Button
  }

genAction :: Command -> ButtonAction
genAction command = putStrLn ("Running: " ++ command)

genButton :: ButtonDesc -> ButtonInfo
genButton info = let (label, command) = info
                 in ButtonInfo label (genAction command) (buttonNewWithLabel label)

getButtonDescs :: IO [ButtonDesc]
getButtonDescs = return [("Ok", "ok"),
                         ("Foo", "foo"),
                         ("Bar", "bar")]

applyAction (ButtonInfo _ action io) = do
--This gets called
  putStrLn "applying click handler"
  button <- io
--But apparently not this
  on button buttonActivated action

addIoToContainer container io = do
  widget <- io
  containerAdd container widget

main = do
  initGUI
  window <- windowNew
--These buttons do not work
  buttonDescs <- getButtonDescs
  buttons <- return $ map genButton buttonDescs
  vbox <- vBoxNew True 0
  _ <- sequence $ map ((addIoToContainer vbox) . buttonIo) buttons
  _ <- sequence $ map applyAction buttons
  _ <- containerAdd window vbox
-- This button works
  button <- buttonNewWithLabel "Manually made"
  on button buttonActivated $ genAction "Manual action"
  containerAdd vbox button
  onDestroy window mainQuit
  widgetShowAll window
  mainGUI

任何帮助将非常感激。谢谢你。

编辑:

根据 chi 的回答,我将 ButtonInfo 数据类型更新为不执行 IO,并最终得到一个 IO [ButtonInfo],它允许我绑定操作。

修订来源(工作):

module GtkTest where

import qualified Data.Map as M
import Graphics.UI.Gtk
import Graphics.UI.Gtk.Buttons.Button
import Graphics.UI.Gtk.Windows.Window
import Graphics.UI.Gtk.Layout.VBox
import Control.DeepSeq

type Command = String
type ButtonLabel = String
type ButtonAction = IO ()
type ButtonDesc = (ButtonLabel, Command)
data ButtonInfo = ButtonInfo {
    buttonLabel :: ButtonLabel
  , buttonAction :: ButtonAction
  , buttonWidget :: Button
  }

genAction :: Command -> ButtonAction
genAction command = putStrLn ("Running: " ++ command)

genButton :: ButtonDesc -> IO ButtonInfo
genButton info = let (label, command) = info
                 in do
                   button <- (buttonNewWithLabel label)
                   return $ ButtonInfo label (genAction command) button

getButtonDescs :: IO [ButtonDesc]
getButtonDescs = return [("Ok", "ok"),
                         ("Foo", "foo"),
                         ("Bar", "bar")]

applyAction (ButtonInfo _ action widget) = do
  putStrLn "applying click handler"
  on widget buttonActivated action

addIoToContainer container io = do
  widget <- io
  containerAdd container widget

main = do
  initGUI
  window <- windowNew
  buttonDescs <- getButtonDescs
  buttons <- sequence $ map genButton buttonDescs
  vbox <- vBoxNew True 0
  _ <- sequence $ map ((containerAdd vbox) . buttonWidget) buttons
  _ <- sequence $ map applyAction buttons
  _ <- containerAdd window vbox
  button <- buttonNewWithLabel "Manually made"
  on button buttonActivated $ genAction "Manual action"
  containerAdd vbox button
  onDestroy window mainQuit
  widgetShowAll window
  mainGUI
4

1 回答 1

1

据我所知,该buttonIo字段ButtonInfo存储了一个每次运行时都会创建一个新按钮的操作。正因为如此,代码

applyAction (ButtonInfo _ action io) = do
  putStrLn "applying click handler"
  button <- io   -- (1)
  on button buttonActivated action

addIoToContainer container io = do
  widget <- io   -- (2)
  containerAdd container widget

看起来不对,因为addIoToContainer创建了一个新按钮 (line (2)) 并将其添加到容器中,同时applyAction创建了另一个按钮 (line (1)) 并附加了一个buttonActivated动作。因此,您会在屏幕上看到一个无响应的按钮,并在屏幕外看到一个响应式按钮。

我的建议是使用

data ButtonInfo = ButtonInfo {
    buttonLabel :: ButtonLabel
  , buttonAction :: ButtonAction
  , buttonIo :: Button   -- no IO here!
  }

IO Button由于类型不同,这将立即导致许多类型错误Button。这很好,因为修复这些类型错误将迫使您只创建一次按钮,因此使它们具有响应性并显示在屏幕上。

于 2014-05-17T21:34:35.247 回答