我正在使用haskell和gtk2hs启动GUI。 我有一个笔记本小部件,我想用“F1,F2 ... F11”键切换页面。
我的工作代码是:
import Control.Monad.Trans (liftIO)
import Graphics.UI.Gtk
main = do
initGUI
builder <- builderNew
builderAddFromFile builder "M62.glade"
window <- builderGetObject builder castToWindow "window1"
notebook <- builderGetObject builder castToNotebook "notebook1"
window `on` keyPressEvent $ tryEvent $ do "F1" <- eventKeyName
liftIO $ notebookSetCurrentPage notebook 0
window `on` keyPressEvent $ tryEvent $ do "F2" <- eventKeyName
liftIO $ notebookSetCurrentPage notebook 1
window `on` keyPressEvent $ tryEvent $ do "F3" <- eventKeyName
liftIO $ notebookSetCurrentPage notebook 2
window `on` keyPressEvent $ tryEvent $ do "F4" <- eventKeyName
liftIO $ notebookSetCurrentPage notebook 3
window `on` keyPressEvent $ tryEvent $ do "F5" <- eventKeyName
liftIO $ notebookSetCurrentPage notebook 4
window `on` keyPressEvent $ tryEvent $ do "F6" <- eventKeyName
liftIO $ notebookSetCurrentPage notebook 5
window `on` keyPressEvent $ tryEvent $ do "F7" <- eventKeyName
liftIO $ notebookSetCurrentPage notebook 6
window `on` keyPressEvent $ tryEvent $ do "F8" <- eventKeyName
liftIO $ notebookSetCurrentPage notebook 7
window `on` keyPressEvent $ tryEvent $ do "F9" <- eventKeyName
liftIO $ notebookSetCurrentPage notebook 8
window `on` keyPressEvent $ tryEvent $ do "F10" <- eventKeyName
liftIO $ notebookSetCurrentPage notebook 9
window `on` keyPressEvent $ tryEvent $ do "F11" <- eventKeyName
liftIO $ notebookSetCurrentPage notebook 10
onDestroy window mainQuit
widgetShowAll window
mainGUI
是否有更好的方法和/或简明的方法来做到这一点? 我试图用'main'来处理它,但只有“F1”才有效。 我不知道如何管理这个样板。
答案 0 :(得分:7)
我不太了解gtk2hs,但使用forM_
循环关键索引应该会有很长的路要走。此外,似乎事件是MonadPlus
,因此模式匹配失败可以有利地替换为guard
。
forM_ [0..10] $ \i -> do
let key = "F" ++ show (i + 1)
window `on` keyPressEvent $ tryEvent $ do
pressed <- eventKeyName
guard (pressed == key)
liftIO $ notebookSetCurrentPage notebook i
答案 1 :(得分:3)
这个怎么样:
window `on` keyPressEvent $ tryEvent $ do
'F':n_ <- eventKeyName
let (n, ""):_ = reads n_
liftIO . notebookSetCurrentPage notebook $ n - 1
这是无可救药的部分:有两个部分模式匹配可以抛出异常。但那没关系,因为这就是tryEvent
的用途。在撰写本文时,所有其他答案都涉及注册许多事件处理程序,而这一个只注册一个。这应该具有(轻微)性能优势。
答案 2 :(得分:2)
尝试将重复的部分拆分成一个函数,如下所示:
import Control.Monad
import Graphics.UI.Gtk
main = do
initGUI
builder <- builderNew
builderAddFromFile builder "M62.glade"
window <- builderGetObject builder castToWindow "window1"
notebook <- builderGetObject builder castToNotebook "notebook1"
-- Split the repeated code into a reusable function, like this
let registerKeyPressEvent n =
window `on` keyPressEvent $ tryEvent $ do
pressed <- eventKeyName
guard (pressed == ("F" ++ show (n + 1)))
liftIO $ notebookSetCurrentPage notebook n
-- Thanks to Tarmil for catching a bug in the code that used to be above.
-- Tarmil got it right, so I'm borrowing his/her version.
-- Then you can call it more than once
registerKeyPressEvent 0
registerKeyPressEvent 1
registerKeyPressEvent 2
registerKeyPressEvent 3
registerKeyPressEvent 4
registerKeyPressEvent 5
registerKeyPressEvent 6
registerKeyPressEvent 7
registerKeyPressEvent 8
registerKeyPressEvent 9
registerKeyPressEvent 10
-- But even that is too verbose.
-- You can shorten it even further like this:
mapM_ registerKeyPressEvent [0..10]
mapM
与map
类似,但monad除外。 map
的类型为:
map :: (a -> b) -> [a] -> [b]
意味着它接受一个函数并将其应用于列表的每个元素,并返回结果。 mapM
的类型为:
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
意味着它需要一个monadic函数(例如registerKeyPressEvent
,IO
monad中的函数会产生注册按键事件的副作用)。 mapM
然后对列表中的每个元素执行一次此函数,不仅将结果收集到列表中,还将monadic动作收集到生成的monad中,这意味着运行registerKeyPressEvent
11的副作用时间按顺序进行。
最后一个难题是,如果使用mapM
,可能会出现类型错误,因为它假设您关心结果列表,因此返回m [b]
。但是,在这种情况下,主要类型为IO ()
,而()
不会与[b]
匹配。因此,您希望mapM
稍微改变一下,抛弃结果列表,只收集monadic动作:
mapM_ :: Monad m => (a -> m b) -> [a] -> m ()
这具有您正在寻找的返回类型。