如何处理 Haskell+GTK 中的大量小部件?
How to deal with lots of widgets in Haskell+GTK?
我有一个描述我的界面的 Glade 文件。它的目标是通过本地网络控制 Nec 显示器。因此有很多小部件控制监视器的工作方式:
我需要在 get/set from/to 监视器和小部件之间创建 link。这意味着 getting/setting 值并将函数附加到小部件。
GTK 库中的 Builder
需要使用 IO () 并强制转换每个小部件,以便调用它们的特定函数。这有点无聊并添加(不必要的)代码行。
module GUI where
import Graphics.UI.Gtk
data NecControlGUI = NecControlGUI
{ winNecControl :: Window
, entIPAddress :: Entry
, btnConnection :: Button
, cbtVideoInput :: ComboBox
, cbtPIPInput :: ComboBox
, sclSharpness :: Scale
, sclContrast :: Scale
, sclBrightness :: Scale
, sclBlackLevel :: Scale
, sclColorTemperature :: Scale
, cbtGamma :: ComboBox
, sclBalance :: Scale
, sclTreble :: Scale
, sclBass :: Scale
, sclVolume :: Scale
, cbtLanguage :: ComboBox
, sclMenuDisplayTime :: Scale
}
loadGUI :: String -> IO NecControlGUI
loadGUI guiPath = do
bdr <- builderNew
builderAddFromFile bdr guiPath
window <- builderGetObject bdr castToWindow "winNecControl"
ipaddress <- builderGetObject bdr castToEntry "entIPAddress"
connection <- builderGetObject bdr castToButton "btnConnection"
videoinput <- builderGetObject bdr castToComboBox "cbtVideoInput"
pipinput <- builderGetObject bdr castToComboBox "cbtPIPInput"
sharpness <- builderGetObject bdr castToScale "sclSharpness"
contrast <- builderGetObject bdr castToScale "sclContrast"
brightness <- builderGetObject bdr castToScale "sclBrightness"
blacklevel <- builderGetObject bdr castToScale "sclBlackLevel"
colortemperature <- builderGetObject bdr castToScale "sclColorTemparature"
gamma <- builderGetObject bdr castToComboBox "cbtGamma"
balance <- builderGetObject bdr castToScale "sclBalance"
treble <- builderGetObject bdr castToScale "sclTreble"
bass <- builderGetObject bdr castToScale "sclBass"
volume <- builderGetObject bdr castToScale "sclVolume"
language <- builderGetObject bdr castToComboBox "cbtLanguage"
menudisplaytime <- builderGetObject bdr castToScale "sclMenuDisplayTime"
return NecControlGUI
{ winNecControl = window
, entIPAddress = ipaddress
, btnConnection = connection
, cbtVideoInput = videoinput
, cbtPIPInput = pipinput
, sclSharpness = sharpness
, sclContrast = contrast
, sclBrightness = brightness
, sclBlackLevel = blacklevel
, sclColorTemperature = colortemperature
, cbtGamma = gamma
, sclBalance = balance
, sclTreble = treble
, sclBass = bass
, sclVolume = volume
, cbtLanguage = language
, sclMenuDisplayTime = menudisplaytime
}
有没有办法自动化这部分?
我听说过 LGtk,但这是唯一的方法吗?
编辑 2016-09-20 1
我尝试采用应用方式,但 Haskell 似乎不喜欢组合应用 IO? (但可能是我)
module NecControlGUI where
import Graphics.UI.Gtk
data NecControlGUI = NecControlGUI
{ adjBalance :: Adjustment
, winNecControl :: Window
}
loadNecControlGUI :: String -> IO NecControlGUI
loadNecControlGUI guiPath = do
bdr <- builderNew
builderAddFromFile bdr guiPath
let bget = builderGetObject bdr
NecControlGUI <$> bget castToAdjustment "adjBalance"
<*> bget castToWindow "winNecControl"
编译输出如下错误:
.../src/NecControlGUI.hs:16:23: error:
• Couldn't match type ‘Adjustment’ with ‘Window’
Expected type: IO Window
Actual type: IO Adjustment
• In the second argument of ‘(<*>)’, namely
‘bget castToWindow "winNecControl"’
In a stmt of a 'do' block:
NecControlGUI <$> bget castToAdjustment "adjBalance"
<*> bget castToWindow "winNecControl"
In the expression:
do { bdr <- builderNew;
builderAddFromFile bdr guiPath;
let bget = builderGetObject bdr;
NecControlGUI <$> bget castToAdjustment "adjBalance"
<*> bget castToWindow "winNecControl" }
.../src/NecControlGUI.hs:16:28: error:
• Couldn't match type ‘Window’ with ‘Adjustment’
Expected type: GObject -> Adjustment
Actual type: GObject -> Window
• In the first argument of ‘bget’, namely ‘castToWindow’
In the second argument of ‘(<*>)’, namely
‘bget castToWindow "winNecControl"’
In a stmt of a 'do' block:
NecControlGUI <$> bget castToAdjustment "adjBalance"
<*> bget castToWindow "winNecControl"
编辑 2016-09-20 2
如果我去掉 let bdr = ...
语句,一切都会按预期进行。
这不是您要查找的内容,但它更短。使用 Applicative
实例将对 builderGetObject
的调用与数据构造函数结合起来。
module GUI where
import Graphics.UI.Gtk
data NecControlGUI = NecControlGUI
{ ...
}
loadGUI :: String -> IO NecControlGUI
loadGUI guiPath = do
bdr <- builderNew
builderAddFromFile bdr guiPath
let bget = builderGetObject bdr
NecControlGUI <$> bget castToWindow "winNecControl"
<*> bget castToEntry "entIPAddress"
<*> bget castToButton "btnConnection"
<*> bget castToComboBox "cbtVideoInput"
<*> bget castToComboBox "cbtPIPInput"
<*> bget castToScale "sclSharpness"
<*> bget castToScale "sclContrast"
<*> bget castToScale "sclBrightness"
<*> bget castToScale "sclBlackLevel"
<*> bget castToScale "sclColorTemparature"
<*> bget castToComboBox "cbtGamma"
<*> bget castToScale "sclBalance"
<*> bget castToScale "sclTreble"
<*> bget castToScale "sclBass"
<*> bget castToScale "sclVolume"
<*> bget castToComboBox "cbtLanguage"
<*> bget castToScale "sclMenuDisplayTime"
我有一个描述我的界面的 Glade 文件。它的目标是通过本地网络控制 Nec 显示器。因此有很多小部件控制监视器的工作方式:
我需要在 get/set from/to 监视器和小部件之间创建 link。这意味着 getting/setting 值并将函数附加到小部件。
GTK 库中的 Builder
需要使用 IO () 并强制转换每个小部件,以便调用它们的特定函数。这有点无聊并添加(不必要的)代码行。
module GUI where
import Graphics.UI.Gtk
data NecControlGUI = NecControlGUI
{ winNecControl :: Window
, entIPAddress :: Entry
, btnConnection :: Button
, cbtVideoInput :: ComboBox
, cbtPIPInput :: ComboBox
, sclSharpness :: Scale
, sclContrast :: Scale
, sclBrightness :: Scale
, sclBlackLevel :: Scale
, sclColorTemperature :: Scale
, cbtGamma :: ComboBox
, sclBalance :: Scale
, sclTreble :: Scale
, sclBass :: Scale
, sclVolume :: Scale
, cbtLanguage :: ComboBox
, sclMenuDisplayTime :: Scale
}
loadGUI :: String -> IO NecControlGUI
loadGUI guiPath = do
bdr <- builderNew
builderAddFromFile bdr guiPath
window <- builderGetObject bdr castToWindow "winNecControl"
ipaddress <- builderGetObject bdr castToEntry "entIPAddress"
connection <- builderGetObject bdr castToButton "btnConnection"
videoinput <- builderGetObject bdr castToComboBox "cbtVideoInput"
pipinput <- builderGetObject bdr castToComboBox "cbtPIPInput"
sharpness <- builderGetObject bdr castToScale "sclSharpness"
contrast <- builderGetObject bdr castToScale "sclContrast"
brightness <- builderGetObject bdr castToScale "sclBrightness"
blacklevel <- builderGetObject bdr castToScale "sclBlackLevel"
colortemperature <- builderGetObject bdr castToScale "sclColorTemparature"
gamma <- builderGetObject bdr castToComboBox "cbtGamma"
balance <- builderGetObject bdr castToScale "sclBalance"
treble <- builderGetObject bdr castToScale "sclTreble"
bass <- builderGetObject bdr castToScale "sclBass"
volume <- builderGetObject bdr castToScale "sclVolume"
language <- builderGetObject bdr castToComboBox "cbtLanguage"
menudisplaytime <- builderGetObject bdr castToScale "sclMenuDisplayTime"
return NecControlGUI
{ winNecControl = window
, entIPAddress = ipaddress
, btnConnection = connection
, cbtVideoInput = videoinput
, cbtPIPInput = pipinput
, sclSharpness = sharpness
, sclContrast = contrast
, sclBrightness = brightness
, sclBlackLevel = blacklevel
, sclColorTemperature = colortemperature
, cbtGamma = gamma
, sclBalance = balance
, sclTreble = treble
, sclBass = bass
, sclVolume = volume
, cbtLanguage = language
, sclMenuDisplayTime = menudisplaytime
}
有没有办法自动化这部分?
我听说过 LGtk,但这是唯一的方法吗?
编辑 2016-09-20 1
我尝试采用应用方式,但 Haskell 似乎不喜欢组合应用 IO? (但可能是我)
module NecControlGUI where
import Graphics.UI.Gtk
data NecControlGUI = NecControlGUI
{ adjBalance :: Adjustment
, winNecControl :: Window
}
loadNecControlGUI :: String -> IO NecControlGUI
loadNecControlGUI guiPath = do
bdr <- builderNew
builderAddFromFile bdr guiPath
let bget = builderGetObject bdr
NecControlGUI <$> bget castToAdjustment "adjBalance"
<*> bget castToWindow "winNecControl"
编译输出如下错误:
.../src/NecControlGUI.hs:16:23: error:
• Couldn't match type ‘Adjustment’ with ‘Window’
Expected type: IO Window
Actual type: IO Adjustment
• In the second argument of ‘(<*>)’, namely
‘bget castToWindow "winNecControl"’
In a stmt of a 'do' block:
NecControlGUI <$> bget castToAdjustment "adjBalance"
<*> bget castToWindow "winNecControl"
In the expression:
do { bdr <- builderNew;
builderAddFromFile bdr guiPath;
let bget = builderGetObject bdr;
NecControlGUI <$> bget castToAdjustment "adjBalance"
<*> bget castToWindow "winNecControl" }
.../src/NecControlGUI.hs:16:28: error:
• Couldn't match type ‘Window’ with ‘Adjustment’
Expected type: GObject -> Adjustment
Actual type: GObject -> Window
• In the first argument of ‘bget’, namely ‘castToWindow’
In the second argument of ‘(<*>)’, namely
‘bget castToWindow "winNecControl"’
In a stmt of a 'do' block:
NecControlGUI <$> bget castToAdjustment "adjBalance"
<*> bget castToWindow "winNecControl"
编辑 2016-09-20 2
如果我去掉 let bdr = ...
语句,一切都会按预期进行。
这不是您要查找的内容,但它更短。使用 Applicative
实例将对 builderGetObject
的调用与数据构造函数结合起来。
module GUI where
import Graphics.UI.Gtk
data NecControlGUI = NecControlGUI
{ ...
}
loadGUI :: String -> IO NecControlGUI
loadGUI guiPath = do
bdr <- builderNew
builderAddFromFile bdr guiPath
let bget = builderGetObject bdr
NecControlGUI <$> bget castToWindow "winNecControl"
<*> bget castToEntry "entIPAddress"
<*> bget castToButton "btnConnection"
<*> bget castToComboBox "cbtVideoInput"
<*> bget castToComboBox "cbtPIPInput"
<*> bget castToScale "sclSharpness"
<*> bget castToScale "sclContrast"
<*> bget castToScale "sclBrightness"
<*> bget castToScale "sclBlackLevel"
<*> bget castToScale "sclColorTemparature"
<*> bget castToComboBox "cbtGamma"
<*> bget castToScale "sclBalance"
<*> bget castToScale "sclTreble"
<*> bget castToScale "sclBass"
<*> bget castToScale "sclVolume"
<*> bget castToComboBox "cbtLanguage"
<*> bget castToScale "sclMenuDisplayTime"