Haskell 是否可以使用gtk2hs清除某些代码?

Haskell 是否可以使用gtk2hs清除某些代码?,haskell,boilerplate,gtk2hs,Haskell,Boilerplate,Gtk2hs,我正在用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 bui

我正在用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起作用。
我不知道如何管理这个样板文件。

尝试将重复的部分拆分为一个函数,如下所示:

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的类型为:

这意味着它采用了一个单精度函数,如registerKeyPressEvent,IO单精度函数中的函数,该函数会产生注册按键事件的副作用。然后,mapM对列表中的每个元素执行此函数一次,不仅将结果收集到列表中,而且将一元操作收集到生成的monad中,这意味着按顺序执行11次registerKeyPressEvent的副作用

最后一个难题是,如果使用mapM,可能会出现类型错误,因为它假定您关心结果列表,因此返回m[b]。但是,在这种情况下,main的类型是IO,并且不会匹配到[b]。因此,您希望在mapM上稍作改动,丢弃结果列表,只收集一元操作:

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

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

我对gtk2hs了解不多,但使用forM_uu循环关键索引应该会有很大帮助。而且,事件似乎是一个MonadPlus,所以模式匹配失败可以用guard来代替

这个怎么样:

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

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

很好的解释,但这是一个错误:F++show n+1不是一个模式,您不能将它放在Oops的左侧!给我一分钟,修好了,谢谢。我冒昧借用了你的版本。我希望你不介意。如果我介意我的代码被借用,我不会把它放在StackOverflow上:非常感谢!解释很清楚,我忘记了mapM_uu魔法,曾经使用过,有时…:但是“窗口”和“笔记本”必须是registerKeyPressEvent函数的参数,因为它们只是主要参数。非常感谢!我选择将此解决方案用于注册事实,但您能否提供一些关于部分模式匹配的文档?我还不明白他们…:@grwp是肯定的。你想知道什么?也许你想知道什么东西它们不匹配,或者…?非常感谢,这就是我尝试的解决方案!非常有效!
mapM_ :: Monad m => (a -> m b) -> [a] -> m ()
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
window `on` keyPressEvent $ tryEvent $ do
    'F':n_ <- eventKeyName
    let (n, ""):_ = reads n_
    liftIO . notebookSetCurrentPage notebook $ n - 1