gtk3/gtk2hs:在滚动窗口中平移 "flickers"
gtk3/gtk2hs: panning in a scrolledWindow "flickers"
我试图在 gtk3/gtk2hs 中的 scrolledWindow 上获得平移行为,类似于像 google 地图这样的地图显示(有人告诉我这叫做平移,但标签描述将其定义为旋转)
(滚动窗口中的光标,M1 向下)=> 滚动窗口中的对象 "follows" 鼠标光标
大体思路是在buttonPressEvent上记录光标位置,在motionNotifyEvent上使用当前位置的偏移来更新视口的Adjustment。
我没有顺畅地跟随,而是出现闪烁行为,导致图像来回跳动并且没有跟随正确的 "speed"(比例偏移)。
下面的代码包括我的调试尝试。打印显示 onMotionNotify 部分是在 "incorrect" 个光标位置执行的。
module WidgetBehavior where
import Control.Monad
import "gtk3" Graphics.UI.Gtk
import "gtk3" Graphics.UI.Gtk.Buttons.Button
import "gtk3" Graphics.UI.Gtk.General.Enums
import "gtk3" Graphics.UI.Gtk.Gdk.EventM
import Control.Monad.IO.Class(liftIO, MonadIO)
import Control.Monad.State.Class
import Control.Monad.Trans.Reader
import Data.IORef
import Control.Applicative
import Control.Monad.Trans.Class
initViewportPanning :: (WidgetClass target, ViewportClass target) =>
target -> IO (ConnectId target)
initViewportPanning target = do
widgetAddEvents target [Button1MotionMask]
initialCursorPosition <-newIORef (0, 0)
initialAdjustment <-newIORef (0, 0)
on target buttonPressEvent $ do
newPos <- eventCoordinates
liftIO $ do
writeIORef initialCursorPosition newPos
hAdj <- viewportGetHAdjustment target
hVal <- adjustmentGetValue hAdj
vAdj <- viewportGetVAdjustment target
vVal <- adjustmentGetValue vAdj
writeIORef initialAdjustment (hVal, vVal)
liftIO $ putStrLn "pressed"
return True
on target motionNotifyEvent $ do
(newH, newV) <- eventCoordinates
liftIO $ do
putStrLn ("motion at " ++ show newH ++ " , "++ show newV)
hAdj <- viewportGetHAdjustment target
vAdj <- viewportGetVAdjustment target
(initAdjH, initAdjV) <- readIORef initialAdjustment
(initCH, initCV) <- readIORef initialCursorPosition
adjustmentSetValue hAdj (initAdjH - (newH - initCH) )
adjustmentSetValue vAdj (initAdjV - (newV - initCV) )
adjustmentValueChanged hAdj
adjustmentValueChanged vAdj
return False
其中 target 是滚动窗口中的视口,它本身包含带有图像的叠加层:
- 滚动窗口
- 视口
- 叠加
- 图片
按下并沿一个方向缓慢拖动时的典型输出如下所示:
pressed
motion at 629.247802734375 , 581.4336242675781
motion at 629.247802734375 , 582.4336242675781
motion at 629.247802734375 , 580.4336242675781 #
motion at 628.247802734375 , 582.4336242675781 #
motion at 629.247802734375 , 579.4336242675781 #
motion at 627.247802734375 , 582.4336242675781
motion at 629.247802734375 , 578.4336242675781
标记的行是我所说的示例 "jumps"
似乎事件被处理了两次,但这仍然不能解释为什么小部件移动明显慢于鼠标移动。
编辑:
XML: "main.ui"
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.16.1 -->
<interface>
<requires lib="gtk+" version="3.10"/>
<object class="GtkApplicationWindow" id="mainWindow">
<property name="width_request">600</property>
<property name="height_request">400</property>
<property name="can_focus">False</property>
<property name="window_position">center-on-parent</property>
<child>
<object class="GtkScrolledWindow" id="scrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkViewport" id="viewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkOverlay" id="overlay">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<object class="GtkSizeGroup" id="sizegroup1">
<property name="mode">both</property>
<widgets>
<widget name="mainWindow"/>
</widgets>
</object>
</interface>
.cabal
-- Initial panningMinimal.cabal generated by cabal init. For further
-- documentation, see http://haskell.org/cabal/users-guide/
name: panningMinimal
version: 0.1.0.0
-- synopsis:
-- description:
-- license:
license-file: LICENSE
author: .
maintainer: .
-- copyright:
-- category:
build-type: Simple
-- extra-source-files:
cabal-version: >=1.10
executable panningMinimal
main-is: Main.hs
-- other-modules:
other-extensions: PackageImports, MultiParamTypeClasses, GeneralizedNewtypeDeriving, FlexibleInstances
build-depends: base >=4.6 && <4.7, gtk3, cairo >= 0.13.0.2, transformers >= 0.4.2.0, mtl >= 2.2.1
-- hs-source-dirs:
default-language: Haskell2010
Main.hs
{-# LANGUAGE PackageImports #-}
module Main where
import Control.Monad
import "gtk3" Graphics.UI.Gtk
import "gtk3" Graphics.UI.Gtk.Buttons.Button
import "gtk3" Graphics.UI.Gtk.General.Enums
import Control.Monad.IO.Class(liftIO)
import WidgetBehavior
main = do
initGUI
builder <- builderNew
builderAddFromFile builder "main.ui"
window <- builderGetObject builder castToWindow "mainWindow"
overlay <- builderGetObject builder castToOverlay "overlay"
viewport <- builderGetObject builder castToViewport "viewport"
scrolledWindow <- builderGetObject builder castToScrolledWindow "scrolledWindow"
initViewportPanning viewport
image <- imageNewFromFile "redCat.jpg"
containerAdd overlay image
set overlay [widgetOpacity := 0.9]
window `on` deleteEvent $ liftIO mainQuit >> return False
-- Display the window
widgetShowAll window
mainGUI
http://pastebin.com/QKbPuNKe
这 3 个文件 + 一个名为 "redCat.jpg" 的大图像应该可以在 cabal 沙箱中使用:
cabal 沙箱初始化
cabal 安装 --dependencies-only
阴谋集团运行
您的问题是由于您在监听视口上的运动事件的同时不断拉动视口造成的。
我发现的简单解决方案是将 scrolledWindow
包装在 GtkEventBox
中(我在代码中调用了 eventbox
),然后将事件监听器附加到那。这样,您正在收听的小部件就不会四处移动(它是固定的 GtkEventBox
)。
eventbox <- builderGetObject builder castToEventBox "eventbox"
viewport <- builderGetObject builder castToViewport "viewport"
initViewportPanning eventbox viewport
with initViewportPanning
略有更改以允许与其目标不同的事件源:
initViewportPanning :: (WidgetClass src, ViewportClass target)
=> src -> target -> IO (ConnectId src)
initViewportPanning src target = do
widgetAddEvents src [Button1MotionMask]
-- ...
on src buttonPressEvent $ do
-- unchanged
on src motionNotifyEvent $ do
-- unchanged
通过这种方式,我的平移变得如丝般顺滑。
我试图在 gtk3/gtk2hs 中的 scrolledWindow 上获得平移行为,类似于像 google 地图这样的地图显示(有人告诉我这叫做平移,但标签描述将其定义为旋转)
(滚动窗口中的光标,M1 向下)=> 滚动窗口中的对象 "follows" 鼠标光标
大体思路是在buttonPressEvent上记录光标位置,在motionNotifyEvent上使用当前位置的偏移来更新视口的Adjustment。
我没有顺畅地跟随,而是出现闪烁行为,导致图像来回跳动并且没有跟随正确的 "speed"(比例偏移)。 下面的代码包括我的调试尝试。打印显示 onMotionNotify 部分是在 "incorrect" 个光标位置执行的。
module WidgetBehavior where
import Control.Monad
import "gtk3" Graphics.UI.Gtk
import "gtk3" Graphics.UI.Gtk.Buttons.Button
import "gtk3" Graphics.UI.Gtk.General.Enums
import "gtk3" Graphics.UI.Gtk.Gdk.EventM
import Control.Monad.IO.Class(liftIO, MonadIO)
import Control.Monad.State.Class
import Control.Monad.Trans.Reader
import Data.IORef
import Control.Applicative
import Control.Monad.Trans.Class
initViewportPanning :: (WidgetClass target, ViewportClass target) =>
target -> IO (ConnectId target)
initViewportPanning target = do
widgetAddEvents target [Button1MotionMask]
initialCursorPosition <-newIORef (0, 0)
initialAdjustment <-newIORef (0, 0)
on target buttonPressEvent $ do
newPos <- eventCoordinates
liftIO $ do
writeIORef initialCursorPosition newPos
hAdj <- viewportGetHAdjustment target
hVal <- adjustmentGetValue hAdj
vAdj <- viewportGetVAdjustment target
vVal <- adjustmentGetValue vAdj
writeIORef initialAdjustment (hVal, vVal)
liftIO $ putStrLn "pressed"
return True
on target motionNotifyEvent $ do
(newH, newV) <- eventCoordinates
liftIO $ do
putStrLn ("motion at " ++ show newH ++ " , "++ show newV)
hAdj <- viewportGetHAdjustment target
vAdj <- viewportGetVAdjustment target
(initAdjH, initAdjV) <- readIORef initialAdjustment
(initCH, initCV) <- readIORef initialCursorPosition
adjustmentSetValue hAdj (initAdjH - (newH - initCH) )
adjustmentSetValue vAdj (initAdjV - (newV - initCV) )
adjustmentValueChanged hAdj
adjustmentValueChanged vAdj
return False
其中 target 是滚动窗口中的视口,它本身包含带有图像的叠加层:
- 滚动窗口
- 视口
- 叠加
- 图片
按下并沿一个方向缓慢拖动时的典型输出如下所示:
pressed
motion at 629.247802734375 , 581.4336242675781
motion at 629.247802734375 , 582.4336242675781
motion at 629.247802734375 , 580.4336242675781 #
motion at 628.247802734375 , 582.4336242675781 #
motion at 629.247802734375 , 579.4336242675781 #
motion at 627.247802734375 , 582.4336242675781
motion at 629.247802734375 , 578.4336242675781
标记的行是我所说的示例 "jumps" 似乎事件被处理了两次,但这仍然不能解释为什么小部件移动明显慢于鼠标移动。
编辑:
XML: "main.ui"
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.16.1 -->
<interface>
<requires lib="gtk+" version="3.10"/>
<object class="GtkApplicationWindow" id="mainWindow">
<property name="width_request">600</property>
<property name="height_request">400</property>
<property name="can_focus">False</property>
<property name="window_position">center-on-parent</property>
<child>
<object class="GtkScrolledWindow" id="scrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkViewport" id="viewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkOverlay" id="overlay">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<object class="GtkSizeGroup" id="sizegroup1">
<property name="mode">both</property>
<widgets>
<widget name="mainWindow"/>
</widgets>
</object>
</interface>
.cabal
-- Initial panningMinimal.cabal generated by cabal init. For further
-- documentation, see http://haskell.org/cabal/users-guide/
name: panningMinimal
version: 0.1.0.0
-- synopsis:
-- description:
-- license:
license-file: LICENSE
author: .
maintainer: .
-- copyright:
-- category:
build-type: Simple
-- extra-source-files:
cabal-version: >=1.10
executable panningMinimal
main-is: Main.hs
-- other-modules:
other-extensions: PackageImports, MultiParamTypeClasses, GeneralizedNewtypeDeriving, FlexibleInstances
build-depends: base >=4.6 && <4.7, gtk3, cairo >= 0.13.0.2, transformers >= 0.4.2.0, mtl >= 2.2.1
-- hs-source-dirs:
default-language: Haskell2010
Main.hs
{-# LANGUAGE PackageImports #-}
module Main where
import Control.Monad
import "gtk3" Graphics.UI.Gtk
import "gtk3" Graphics.UI.Gtk.Buttons.Button
import "gtk3" Graphics.UI.Gtk.General.Enums
import Control.Monad.IO.Class(liftIO)
import WidgetBehavior
main = do
initGUI
builder <- builderNew
builderAddFromFile builder "main.ui"
window <- builderGetObject builder castToWindow "mainWindow"
overlay <- builderGetObject builder castToOverlay "overlay"
viewport <- builderGetObject builder castToViewport "viewport"
scrolledWindow <- builderGetObject builder castToScrolledWindow "scrolledWindow"
initViewportPanning viewport
image <- imageNewFromFile "redCat.jpg"
containerAdd overlay image
set overlay [widgetOpacity := 0.9]
window `on` deleteEvent $ liftIO mainQuit >> return False
-- Display the window
widgetShowAll window
mainGUI
http://pastebin.com/QKbPuNKe 这 3 个文件 + 一个名为 "redCat.jpg" 的大图像应该可以在 cabal 沙箱中使用:
cabal 沙箱初始化
cabal 安装 --dependencies-only
阴谋集团运行
您的问题是由于您在监听视口上的运动事件的同时不断拉动视口造成的。
我发现的简单解决方案是将 scrolledWindow
包装在 GtkEventBox
中(我在代码中调用了 eventbox
),然后将事件监听器附加到那。这样,您正在收听的小部件就不会四处移动(它是固定的 GtkEventBox
)。
eventbox <- builderGetObject builder castToEventBox "eventbox"
viewport <- builderGetObject builder castToViewport "viewport"
initViewportPanning eventbox viewport
with initViewportPanning
略有更改以允许与其目标不同的事件源:
initViewportPanning :: (WidgetClass src, ViewportClass target)
=> src -> target -> IO (ConnectId src)
initViewportPanning src target = do
widgetAddEvents src [Button1MotionMask]
-- ...
on src buttonPressEvent $ do
-- unchanged
on src motionNotifyEvent $ do
-- unchanged
通过这种方式,我的平移变得如丝般顺滑。