0

我正在使用 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

是否有更好和/或简洁的方法来做到这一点?我试图从“主要”中处理它,但只有“F1”有效。我不知道如何管理这个样板。

4

3 回答 3

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
于 2014-05-18T16:10:36.583 回答
3

这个怎么样:

window `on` keyPressEvent $ tryEvent $ do
    'F':n_ <- eventKeyName
    let (n, ""):_ = reads n_
    liftIO . notebookSetCurrentPage notebook $ n - 1

这是无可救药的部分:有两个部分模式匹配可以引发异常。但这没关系,因为这就是tryEvent目的。在撰写本文时,所有其他答案都涉及注册许多事件处理程序,而这个仅注册一个。这应该具有(轻微的)性能优势。

于 2014-05-18T16:24:40.010 回答
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,除了单子。的类型map是:

map :: (a -> b) -> [a] -> [b]

这意味着它接受一个函数并将其应用于列表的每个元素,并返回结果。的类型mapM是:

mapM :: Monad m => (a -> m b) -> [a] -> m [b]

意味着它需要一个单子函数(例如registerKeyPressEvent,单子中的函数IO会产生注册按键事件的副作用)。mapM然后对列表中的每个元素执行一次该函数,不仅将结果收集到一个列表中,而且将单子动作收集到结果单子中,这意味着运行 11 次的副作用registerKeyPressEvent是按顺序执行的。

难题的最后一部分是,如果使用 ,您可能会收到类型错误mapM,因为它假定您关心结果列表,因此返回m [b]。但是,在这种情况下, main 的类型是IO (),并且()不会匹配到[b]。因此,您需要稍微mapM改变一下结果列表,只收集单子动作:

mapM_ :: Monad m => (a -> m b) -> [a] -> m ()

这具有您正在寻找的返回类型。

于 2014-05-18T16:10:03.387 回答