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

これはどうしようもなく部分的です。例外をスローする可能性のある部分的なパターン マッチが 2 つあります。しかし、それは大丈夫です。それが目的だからtryEventです。執筆時点では、他のすべての回答には多くのイベントハンドラーの登録が含まれますが、これは1つしか登録しません。これにより、(わずかな) パフォーマンス上の利点が得られるはずです。

于 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]

mapMmapモナドを除いて、のようです。のタイプmapは次のとおりです。

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

つまり、関数を受け取り、それをリストのすべての要素に適用して、結果を返します。のタイプmapMは次のとおりです。

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

つまり、モナド関数 (キー押下イベントを登録する副作用を作成するモナドregisterKeyPressEvent内の関数など) を取ることを意味します。次に、この関数をリスト内のすべての要素に対して 1 回実行し、結果をリストに収集するだけでなく、モナド アクションを結果のモナドに収集します。つまり、 11 回実行することによる副作用が順番に実行されます。IOmapMregisterKeyPressEvent

パズルの最後のピースは、 を使用すると型エラーが発生する可能性があるということです。mapMこれは、結果のリストに関心があると想定し、 を返すためですm [b]。ただし、この場合、メインのタイプは でありIO ()()まで一致しません[b]。したがってmapM、モナドアクションのみを収集して、結果のリストを破棄するわずかなバリエーションが必要です。

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

これには、探している戻り値の型があります。

于 2014-05-18T16:10:03.387 に答える