pygtk 实现的无边框窗口的拖动 拖动时窗口抖动太厉害 不平滑

软件和网站开发以及相关技术探讨
niuminguo
帖子: 20
注册时间: 2009-11-25 19:40

pygtk 实现的无边框窗口的拖动 拖动时窗口抖动太厉害 不平滑

#1

帖子 niuminguo » 2013-10-12 14:07

代码如下:

代码: 全选


#!/usr/bin/env python

import gtk

class TitleBar(gtk.DrawingArea):
	def __init__(self,father):
		gtk.DrawingArea.__init__(self)

		self.motion=''
		self.begin_x=''
		self.begin_y=''
		self.end_x=''
		self.end_y=''
		
		self.father=father

		self.father_x,self.father_y=self.father.get_position()

		self.set_size_request(-1,80)
		self.connect('button-press-event',self.mouse_press)
		self.connect('button-release-event',self.mouse_release)
		self.set_events( gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.POINTER_MOTION_MASK)

	def mouse_press(self,widget,event):
		if event.button == 1:
			self.begin_x,self.begin_y = event.x,event.y
			self.motion=self.connect('motion-notify-event',self.mouse_motion)
	def mouse_release(self,widget,event):
		self.disconnect(self.motion)
	def mouse_motion(self,widget,event):
		self.end_x,self.end_y = event.x,event.y
		x=self.father_x + int(self.end_x - self.begin_x)
		y=self.father_y + int(self.end_y - self.begin_y)
		self.father.move(int(x),int(y))

[/size]


self.father是drawingarea所在的窗体。目的是通过鼠标在drawingarea上拖动来拖动窗体移动。效果是可以拖动窗体移动,但是抖动的比较厉害。
上次由 niuminguo 在 2013-10-12 22:58,总共编辑 1 次。
头像
eexpress
帖子: 58428
注册时间: 2005-08-14 21:55
来自: 长沙

Re: pygtk 实现的无边框窗口的拖动 拖动时窗口抖动太厉害 不平滑

#2

帖子 eexpress » 2013-10-12 18:25

py本来慢。
你搜索showpng的bin,试试效果。或者cairo-weather的deb包。
● 鸣学
头像
langyxxl
帖子: 443
注册时间: 2012-01-17 22:17

Re: pygtk 实现的无边框窗口的拖动 拖动时窗口抖动太厉害 不平滑

#3

帖子 langyxxl » 2013-10-12 18:48

归结到"py本来慢",......厉害啊,轻松加愉快

我用py也做了个拖动的,感觉没你说的这种情况....
肯定是你的程序写的不好,或者是什么其他原因

你这代码格式,太难看了,弄好点,可以帮你看看
头像
eexpress
帖子: 58428
注册时间: 2005-08-14 21:55
来自: 长沙

Re: pygtk 实现的无边框窗口的拖动 拖动时窗口抖动太厉害 不平滑

#4

帖子 eexpress » 2013-10-12 18:57

好吧。给bin,自己试试。
附件
showpng.tar
(30 KiB) 已下载 100 次
● 鸣学
niuminguo
帖子: 20
注册时间: 2009-11-25 19:40

Re: pygtk 实现的无边框窗口的拖动 拖动时窗口抖动太厉害 不平滑

#5

帖子 niuminguo » 2013-10-12 23:00

langyxxl 写了:归结到"py本来慢",......厉害啊,轻松加愉快

我用py也做了个拖动的,感觉没你说的这种情况....
肯定是你的程序写的不好,或者是什么其他原因

你这代码格式,太难看了,弄好点,可以帮你看看
代码已经编辑好了,麻烦帮忙看下。谢了。
头像
langyxxl
帖子: 443
注册时间: 2012-01-17 22:17

Re: pygtk 实现的无边框窗口的拖动 拖动时窗口抖动太厉害 不平滑

#6

帖子 langyxxl » 2013-10-13 9:33

http://rueiyuanlu.blogspot.com/2010/11/gtk.html
难道是这个原因

我记得我当时好像把,鼠标按下时,当做按到标题栏来处理,直接就能移动了
niuminguo
帖子: 20
注册时间: 2009-11-25 19:40

Re: pygtk 实现的无边框窗口的拖动 拖动时窗口抖动太厉害 不平滑

#7

帖子 niuminguo » 2013-10-13 10:04

langyxxl 写了:http://rueiyuanlu.blogspot.com/2010/11/gtk.html
难道是这个原因

我记得我当时好像把,鼠标按下时,当做按到标题栏来处理,直接就能移动了
链接咋打不开呢
头像
langyxxl
帖子: 443
注册时间: 2012-01-17 22:17

Re: pygtk 实现的无边框窗口的拖动 拖动时窗口抖动太厉害 不平滑

#8

帖子 langyxxl » 2013-10-13 11:05

要翻墙////
GTK視窗事件 "motion-notify-event"
GTK在視窗元件中定義了 motion-notify-event,這種訊號會在滑鼠游標在視窗元件上移動時觸發。由於滑鼠游標快速地在視窗元件上移動時會產生大量的視窗事件,使得系統來不及處理完佇列中的事件進而產生 lag 的現象。

為了解決這個問題,我們可以在這個視窗元件上加上 GDK_POINTER_MOTION_HINT。

使用motion-notify-event的視窗元件,需要執行下列指令來啟用motion-notify-event
gtk_widget_add_events(GTK_WIDGET(?) ,GDK_POINTER_MOTION_MASK);


加上 GDK_POINTER_MOTION_HINT
gtk_widget_add_events(GTK_WIDGET(?) ,GDK_POINTER_MOTION_HINT_MASK);
niuminguo
帖子: 20
注册时间: 2009-11-25 19:40

Re: pygtk 实现的无边框窗口的拖动 拖动时窗口抖动太厉害 不平滑

#9

帖子 niuminguo » 2013-10-13 11:15

langyxxl 写了:要翻墙////
GTK視窗事件 "motion-notify-event"
GTK在視窗元件中定義了 motion-notify-event,這種訊號會在滑鼠游標在視窗元件上移動時觸發。由於滑鼠游標快速地在視窗元件上移動時會產生大量的視窗事件,使得系統來不及處理完佇列中的事件進而產生 lag 的現象。

為了解決這個問題,我們可以在這個視窗元件上加上 GDK_POINTER_MOTION_HINT。

使用motion-notify-event的視窗元件,需要執行下列指令來啟用motion-notify-event
gtk_widget_add_events(GTK_WIDGET(?) ,GDK_POINTER_MOTION_MASK);


加上 GDK_POINTER_MOTION_HINT
gtk_widget_add_events(GTK_WIDGET(?) ,GDK_POINTER_MOTION_HINT_MASK);
加上之后还是抖动厉害,是不是真是因为pygtk慢来不及处理事件?
头像
langyxxl
帖子: 443
注册时间: 2012-01-17 22:17

Re: pygtk 实现的无边框窗口的拖动 拖动时窗口抖动太厉害 不平滑

#10

帖子 langyxxl » 2013-10-13 13:50

python调用gtk,也是调用gtk的动态链接库.....,归结到python慢上去,没道理...
深度的播放器好像都是用python写的,没听说过,来不及处理事件...


你用c写个和你现在一样的试试

我在想是不是显卡驱动没装好,胡乱猜的..
niuminguo
帖子: 20
注册时间: 2009-11-25 19:40

Re: pygtk 实现的无边框窗口的拖动 拖动时窗口抖动太厉害 不平滑

#11

帖子 niuminguo » 2013-10-13 15:19

langyxxl 写了:python调用gtk,也是调用gtk的动态链接库.....,归结到python慢上去,没道理...
深度的播放器好像都是用python写的,没听说过,来不及处理事件...


你用c写个和你现在一样的试试

我在想是不是显卡驱动没装好,胡乱猜的..
在windows下和Linux下跑都会抖动,不知道深度是如何实现的。
nae6taiyie0T
帖子: 482
注册时间: 2013-09-13 0:42
系统: Debian sid

Re: pygtk 实现的无边框窗口的拖动 拖动时窗口抖动太厉害 不平滑

#12

帖子 nae6taiyie0T » 2013-10-13 16:42

niuminguo 写了:代码如下:

代码: 全选


#!/usr/bin/env python

import gtk

class TitleBar(gtk.DrawingArea):
	def __init__(self,father):
		gtk.DrawingArea.__init__(self)

		self.motion=''
		self.begin_x=''
		self.begin_y=''
		self.end_x=''
		self.end_y=''
		
		self.father=father

		self.father_x,self.father_y=self.father.get_position()

		self.set_size_request(-1,80)
		self.connect('button-press-event',self.mouse_press)
		self.connect('button-release-event',self.mouse_release)
		self.set_events( gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.POINTER_MOTION_MASK)

	def mouse_press(self,widget,event):
		if event.button == 1:
			self.begin_x,self.begin_y = event.x,event.y
			self.motion=self.connect('motion-notify-event',self.mouse_motion)
	def mouse_release(self,widget,event):
		self.disconnect(self.motion)
	def mouse_motion(self,widget,event):
		self.end_x,self.end_y = event.x,event.y
		x=self.father_x + int(self.end_x - self.begin_x)
		y=self.father_y + int(self.end_y - self.begin_y)
		self.father.move(int(x),int(y))

[/size]


self.father是drawingarea所在的窗体。目的是通过鼠标在drawingarea上拖动来拖动窗体移动。效果是可以拖动窗体移动,但是抖动的比较厉害。
因为你用的是实时计算, 当窗口移动后, 你绑定的操作都会被计算一遍, 这样有很大的计算量, 表现出来就是卡, 抖.

我的解决方法是用延时计算, 原理很简单, 这也是做GUI设计的一个基本技巧.
当移动事件触发之后, 你用GLib.timeout_add()来调用你真正的处理函数. 看例子:

代码: 全选

def on_window_move(self, *args):
    def _on_window_move(local_timestamp):
        # 如果这两个时间标记不相同, 就什么都不做.
        if local_timestamp != self.window_move_timestamp:
            return
        # 超过250ms, 进行实际的计算操作
        # ....

    self.window_move_timestamp = time.time()
    GLib.timeout_add(250, _on_window_move, self.window_move_timestamp)
适用的场景包括, 播放器的进度条的滑动, 窗口移动; 还有, 输入框的自动补全(自动完成)等.
当然, 这个方法对于需要实时得到窗口状态的操作不适用, 比如实时显示当前窗口的位置.

恰当地处理好时间, 在GUI设计中是很有意思的, 比如, 在手机上, 如何才能取消刚刚要发送的一条短信呢? 最简单的方法就是延迟几秒钟才发送(这个地在老罗他们的android ROM里加入的功能).


多说两句无关的, Gtk中提供的另一个简单易用的函数是GLib.idle_add(), 用它可以将后台线程处理好的数据转到主线程上显示.

还有, 提醒一下, pygtk这个gtk的绑定早在2011年就没更新了, 最后一个版本是2.24. 现在都转到了PyGObject上来了, 总体来说, 后者的API更加规范一下, 并且更强大, 具体可以看一下GObject Introspection.
niuminguo
帖子: 20
注册时间: 2009-11-25 19:40

Re: pygtk 实现的无边框窗口的拖动 拖动时窗口抖动太厉害 不平滑

#13

帖子 niuminguo » 2013-10-13 17:36

nae6taiyie0T 写了:
niuminguo 写了:代码如下:

代码: 全选


#!/usr/bin/env python

import gtk

class TitleBar(gtk.DrawingArea):
	def __init__(self,father):
		gtk.DrawingArea.__init__(self)

		self.motion=''
		self.begin_x=''
		self.begin_y=''
		self.end_x=''
		self.end_y=''
		
		self.father=father

		self.father_x,self.father_y=self.father.get_position()

		self.set_size_request(-1,80)
		self.connect('button-press-event',self.mouse_press)
		self.connect('button-release-event',self.mouse_release)
		self.set_events( gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.POINTER_MOTION_MASK)

	def mouse_press(self,widget,event):
		if event.button == 1:
			self.begin_x,self.begin_y = event.x,event.y
			self.motion=self.connect('motion-notify-event',self.mouse_motion)
	def mouse_release(self,widget,event):
		self.disconnect(self.motion)
	def mouse_motion(self,widget,event):
		self.end_x,self.end_y = event.x,event.y
		x=self.father_x + int(self.end_x - self.begin_x)
		y=self.father_y + int(self.end_y - self.begin_y)
		self.father.move(int(x),int(y))

[/size]


self.father是drawingarea所在的窗体。目的是通过鼠标在drawingarea上拖动来拖动窗体移动。效果是可以拖动窗体移动,但是抖动的比较厉害。
因为你用的是实时计算, 当窗口移动后, 你绑定的操作都会被计算一遍, 这样有很大的计算量, 表现出来就是卡, 抖.

我的解决方法是用延时计算, 原理很简单, 这也是做GUI设计的一个基本技巧.
当移动事件触发之后, 你用GLib.timeout_add()来调用你真正的处理函数. 看例子:

代码: 全选

def on_window_move(self, *args):
    def _on_window_move(local_timestamp):
        # 如果这两个时间标记不相同, 就什么都不做.
        if local_timestamp != self.window_move_timestamp:
            return
        # 超过250ms, 进行实际的计算操作
        # ....

    self.window_move_timestamp = time.time()
    GLib.timeout_add(250, _on_window_move, self.window_move_timestamp)
适用的场景包括, 播放器的进度条的滑动, 窗口移动; 还有, 输入框的自动补全(自动完成)等.
当然, 这个方法对于需要实时得到窗口状态的操作不适用, 比如实时显示当前窗口的位置.

恰当地处理好时间, 在GUI设计中是很有意思的, 比如, 在手机上, 如何才能取消刚刚要发送的一条短信呢? 最简单的方法就是延迟几秒钟才发送(这个地在老罗他们的android ROM里加入的功能).


多说两句无关的, Gtk中提供的另一个简单易用的函数是GLib.idle_add(), 用它可以将后台线程处理好的数据转到主线程上显示.

还有, 提醒一下, pygtk这个gtk的绑定早在2011年就没更新了, 最后一个版本是2.24. 现在都转到了PyGObject上来了, 总体来说, 后者的API更加规范一下, 并且更强大, 具体可以看一下GObject Introspection.
非常感谢你的答复,你提到的方法我再试试看。另外,pygobject的东西我也会看看,非常重要的提醒,多谢!
头像
langyxxl
帖子: 443
注册时间: 2012-01-17 22:17

Re: pygtk 实现的无边框窗口的拖动 拖动时窗口抖动太厉害 不平滑

#14

帖子 langyxxl » 2013-10-13 18:33

没想到做gui里面有这么深的水啊!!..
niuminguo
帖子: 20
注册时间: 2009-11-25 19:40

Re: pygtk 实现的无边框窗口的拖动 拖动时窗口抖动太厉害 不平滑

#15

帖子 niuminguo » 2013-10-13 19:49

niuminguo 写了:
nae6taiyie0T 写了:
niuminguo 写了:代码如下:

代码: 全选


#!/usr/bin/env python

import gtk

class TitleBar(gtk.DrawingArea):
	def __init__(self,father):
		gtk.DrawingArea.__init__(self)

		self.motion=''
		self.begin_x=''
		self.begin_y=''
		self.end_x=''
		self.end_y=''
		
		self.father=father

		self.father_x,self.father_y=self.father.get_position()

		self.set_size_request(-1,80)
		self.connect('button-press-event',self.mouse_press)
		self.connect('button-release-event',self.mouse_release)
		self.set_events( gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.POINTER_MOTION_MASK)

	def mouse_press(self,widget,event):
		if event.button == 1:
			self.begin_x,self.begin_y = event.x,event.y
			self.motion=self.connect('motion-notify-event',self.mouse_motion)
	def mouse_release(self,widget,event):
		self.disconnect(self.motion)
	def mouse_motion(self,widget,event):
		self.end_x,self.end_y = event.x,event.y
		x=self.father_x + int(self.end_x - self.begin_x)
		y=self.father_y + int(self.end_y - self.begin_y)
		self.father.move(int(x),int(y))

[/size]


self.father是drawingarea所在的窗体。目的是通过鼠标在drawingarea上拖动来拖动窗体移动。效果是可以拖动窗体移动,但是抖动的比较厉害。
因为你用的是实时计算, 当窗口移动后, 你绑定的操作都会被计算一遍, 这样有很大的计算量, 表现出来就是卡, 抖.

我的解决方法是用延时计算, 原理很简单, 这也是做GUI设计的一个基本技巧.
当移动事件触发之后, 你用GLib.timeout_add()来调用你真正的处理函数. 看例子:

代码: 全选

def on_window_move(self, *args):
    def _on_window_move(local_timestamp):
        # 如果这两个时间标记不相同, 就什么都不做.
        if local_timestamp != self.window_move_timestamp:
            return
        # 超过250ms, 进行实际的计算操作
        # ....

    self.window_move_timestamp = time.time()
    GLib.timeout_add(250, _on_window_move, self.window_move_timestamp)
适用的场景包括, 播放器的进度条的滑动, 窗口移动; 还有, 输入框的自动补全(自动完成)等.
当然, 这个方法对于需要实时得到窗口状态的操作不适用, 比如实时显示当前窗口的位置.

恰当地处理好时间, 在GUI设计中是很有意思的, 比如, 在手机上, 如何才能取消刚刚要发送的一条短信呢? 最简单的方法就是延迟几秒钟才发送(这个地在老罗他们的android ROM里加入的功能).


多说两句无关的, Gtk中提供的另一个简单易用的函数是GLib.idle_add(), 用它可以将后台线程处理好的数据转到主线程上显示.

还有, 提醒一下, pygtk这个gtk的绑定早在2011年就没更新了, 最后一个版本是2.24. 现在都转到了PyGObject上来了, 总体来说, 后者的API更加规范一下, 并且更强大, 具体可以看一下GObject Introspection.
非常感谢你的答复,你提到的方法我再试试看。另外,pygobject的东西我也会看看,非常重要的提醒,多谢!
这个方法我试过了,虽然不抖动了,但是移动不平滑,像是鼠标指针先移动过去了,窗口才过去,不是同步移动的。改变timeout值也不行,改的太小的时候又会出现抖动。
回复