(Haskell) gi-gtk 在按钮回调中设置图像

(Haskell) gi-gtk setting image in button callback

我遵循了有关创建基本 Gi-Gtk 应用程序的教程。 现在我想通过将图像的源设置为我从常量字符串列表中随机选择的字符串来对按钮按下做出反应:

{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where

import Data.GI.Base
import System.Random 
import qualified GI.Gtk as Gtk
import Control.Monad.Random

akkorde = ["C11.png","C13.png","C69.png","C6.png","C7#11.png","C7#9.png","C7b13.png","C7b9.png","C7.png","C9.png","Cadd9.png","Cj7.png"]

selectAcc:: (MonadRandom m) => m [Char]
selectAcc = do 
  let n = length akkorde  
  i <- getRandomR (0, n-1)
  return (akkorde !! i)
main :: IO ()
main = do 
  name <- selectAcc
  Gtk.init Nothing
  win <- Gtk.windowNew Gtk.WindowTypeToplevel
  Gtk.windowSetTitle win "accordtrainer"
  Gtk.onWidgetDestroy win Gtk.mainQuit
  #resize win 640 480
  
  img <- Gtk.imageNewFromFile ("../" ++ name)   
  box <- new Gtk.Box [#orientation := Gtk.OrientationVertical ]
  #add box img
  #add win box
    
  msg <- new Gtk.Label[#label := ( "")]
  #packStart box msg True False 10
 
  btn <- new Gtk.Button [#label := "Click me!"]
  #packStart box btn False False 10
  on btn #clicked ( Gtk.imageSetFromFile img  (do { name <- selectAcc; return ("../" ++ name) }))
  Gtk.widgetShowAll win
  Gtk.main

问题出现在行

  on btn #clicked ( Gtk.imageSetFromFile img  (do { name <- selectAcc; return ("../" ++ name) }))

我认为 Gtk.imageSetFromFile 需要一个 Maybe[Char] 但目前它只能得到 [Char] ghc 说:

app/Main.hs:38:62: error:
    • No instance for (MonadRandom Maybe)
        arising from a use of ‘selectAcc’

Just 构造函数应该给我一个 maybe 类型

  on btn #clicked ( Gtk.imageSetFromFile img  (Just(do { name <- selectAcc; return ("../" ++ name) })))

但后来我发现类型不匹配

• Couldn't match type ‘[Char]’ with ‘Char’
  Expected type: Maybe [Char]
    Actual type: Maybe [[Char]]

错误/“修复”链仍在继续

我认为我做的事情从根本上是错误的,但我不知道如何以“正确”的方式做到这一点如果你知道我应该如何写这一行或更好的方法来获得随机选择的字符串,请回复

您已经在使用 mtl 风格的 monad 约束,它比普通的 monad 更高级,所以不用担心不理解所有内容。

imageSetFromFile的简化类型为:

imageSetFromFile :: Image -> Maybe [Char] -> IO ()

因此它希望您提供 Maybe [Char] 作为输入,其中 Nothing 可能会删除图像,而 Just someFile 会将图像设置到该文件位置。

您正在使用的函数具有以下类型:

selectAcc:: MonadRandom m => m [Char]

这意味着m这里需要一些可以生成随机数的monad。您可以通过在 GHCi 中键入以下内容来查找 MonadRandom 的实例:

ghci> import Control.Monad.Random
ghci> :i MonadRandom
type MonadRandom :: (* -> *) -> Constraint
class Monad m => MonadRandom m where
  getRandomR :: Random a => (a, a) -> m a
  getRandom :: Random a => m a
  getRandomRs :: Random a => (a, a) -> m [a]
  getRandoms :: Random a => m [a]
  {-# MINIMAL getRandomR, getRandom, getRandomRs, getRandoms #-}
    -- Defined in ‘Control.Monad.Random.Class’
instance (RandomGen g, Monad m) => MonadRandom (RandT g m)
  -- Defined in ‘Control.Monad.Trans.Random.Lazy’
instance [safe] MonadRandom IO
  -- Defined in ‘Control.Monad.Random.Class’

在底部你会看到两个实例:一个是 RandT(一个 monad 转换器),另一个是 IO。但是,imageSetFromFile 需要一个 Maybe [Char] 作为参数,因此两者都不会立即起作用。但是在这种情况下,您可以在 IO monad 中调用 imageSetFromFile 之前生成随机名称:

  on btn #clicked $ do
    name <- selectAcc
    Gtk.imageSetFromFile img (Just ("../" ++ name))