在pygtk中处理双击和单击事件

3 投票
4 回答
3860 浏览
提问于 2025-04-18 01:28

我正在尝试用 glib.timeout_add()glib.source_remove() 来区分单击事件和双击事件。以下是我的做法:

class Exchange:

'''

some code

'''

    def __init__(self):

        self.timeoutID_1 = 0
        self.timeoutID_2 = 0

        self.startTime1 = 0.0
        self.stopTime1 = 0.0
        self.startTime2 = 0.0
        self.stopTime2 = 0.0

        '''

        some code

        '''

        ## THE DOUBLE CLICK SIGNAL - item-activated
        iconView1.connect("item-activated", self.on_item_activated_1,upButton1, store1)
        sw1.add(iconView1)

        iconView2.connect("item-activated", self.on_item_activated_2,upButton2, store2)
        sw2.add(iconView2)

        ## THE SINGLE CLICK SIGNAL - selection-changed

        iconView1.connect("selection-changed", self.on_selection_changed_1, copyButton1, cutButton1, pasteButton1, deleteButton1)
       iconView2.connect("selection-changed", self.on_selection_changed_2, copyButton2, cutButton2, pasteButton2, deleteButton2)

    def on_selection_changed_1(self, iconView1, copyButton1, cutButton1, pasteButton1, deleteButton1) :


        self.startTime1 = time.time()

        self.timeoutID_1 = glib.timeout_add(2000, self.selectIcon_1, iconView1, copyButton1, cutButton1, pasteButton1, deleteButton1)


    def on_selection_changed_2(self, iconView2, copyButton2, cutButton2, pasteButton2, deleteButton2) :

        self.startTime2 = time.time()

        self.timeoutID_2 = glib.timeout_add(2000, self.selectIcon_2, iconView2, copyButton2, cutButton2, pasteButton2, deleteButton2)


    def selectIcon_1(self, iconView1, copyButton1, cutButton1, pasteButton1, deleteButton1) :

        copyButton1.set_sensitive(True)
        cutButton1.set_sensitive(True)
        pasteButton1.set_sensitive(True)
        deleteButton1.set_sensitive(True)


    def selectIcon_2(self, iconView2, copyButton2, cutButton2, pasteButton2, deleteButton2) :


        copyButton2.set_sensitive(True)
        cutButton2.set_sensitive(True)
        pasteButton2.set_sensitive(True)
        deleteButton2.set_sensitive(True)

    def on_item_activated_1(self, iconView1, item, upButton1, store1) :

        self.stopTime1 = time.time()

        if self.stopTime1 - self.startTime1 < 1.50 :
            glib.source_remove(self.timeoutID_1)

        '''

        some code

        '''

    def on_item_activated_2(self, iconView2, item, upButton2, store2) :

        self.stopTime2 = time.time()

        if self.stopTime2 - self.startTime2 < 1.50 :
            glib.source_remove(self.timeoutID_2)

        '''

        some code

        '''

尽管 self.stopTime - self.startTime <1.50 的结果是 True(这表示是有效的双击),但单击事件还是被执行了,而且每次单击后会在2秒后只执行一次。我该如何完全取消有效双击时的 selectIcon 方法的执行呢?

更新

根据 mtwebster 的回答,我尝试使用 button_press_event。可惜的是,我又回到了最初的状态。

def on_button_press_event(self, widget, event) :
    if event.button == 1 :
        data = widget.get_path_at_pos(int(event.x), int(event.y))
        if data :
            if event.type == gtk.gdk._2BUTTON_PRESS :
                print " double click "

            elif event.type == gtk.gdk.BUTTON_PRESS :
                print " single click "

输出 ::

vineet@vineet:~/Documents/Project$ python draft6.py
 single click 
 single click 
 double click 

更糟糕的是,双击时竟然执行了三个点击事件:两个单击和一个双击!有没有其他的计时机制可以使用,而不需要处理重复调用带来的不确定性呢?

4 个回答

0

在Python Gtk3中处理双击和单击事件

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk, GLib

class Test:
    def __init__(self):
        # Create a new window
        self.window = Gtk.Window()
        self.window.set_title("Test")

        # It's a good idea to do this for all windows.
        self.window.connect("destroy", lambda wid: Gtk.main_quit())
        self.window.connect("delete_event", lambda a1,a2:Gtk.main_quit())

        # Sets the border width of the window.
        self.window.set_border_width(20)

        # Create a new button
        button = Gtk.Button(label = "Click")

        # Two lists for append the click events
        self.single_click_events = []
        self.double_click_events = []

        # Connect the "clicked" signal of the button to our callback
        button.connect("button-press-event", self.on_button_press_event)
        button.show()

        self.window.add(button)
        self.window.show()

    def on_button_press_event(self, widget, event):
        if event.button == 1:
            if event.type == Gdk.EventType._2BUTTON_PRESS:
                # Remove all single click events
                while self.single_click_events:
                    GLib.source_remove(self.single_click_events.pop()) 
                GLib.idle_add(self.handle_double_click,widget,event)
            elif event.type == Gdk.EventType.BUTTON_PRESS:
                # Schedule the callback 'self.handle_single_click' after double click can occur (after 300ms)
                self.single_click_events.append(GLib.timeout_add(300,self.handle_single_click,widget,event))


    def handle_single_click(self,widget,event):
        # TODO: Handle single click
        print("TODO: Handle single click")
        # Remove all single click events
        while self.single_click_events:
            GLib.source_remove(self.single_click_events.pop())


    def handle_double_click(self,widget,event):
        # TODO: Handle double click
        print("TODO: Handle double click")
        # Remove all double click events
        while self.double_click_events:
            GLib.source_remove(self.double_click_events.pop())

def main():
    Test()
    Gtk.main()
    return 0     

if __name__ == "__main__":
    main()
0

我觉得在这里移除处理程序不是个好主意。

我之前做过类似的事情——给一个点击(比如选择)和双击(比如执行)同一行的操作添加不同的逻辑,这个是在TreeView中实现的。此外,你可能还想支持键盘导航和图片。

当时我在使用的pygtk版本中成功实现了这个功能。不过,过了一段时间,gtk有了几个小版本更新,我的代码就坏掉了。结果发现,想用同一段代码支持多个版本的gtk几乎是不可能的。

最实用的方法是跟踪两个项目:

  • 点击的是不是已经选中的对象(在我这里是行)
  • 这行是怎么选中的,以及选中是什么时候的
    • 行是默认选中的(比如显示或切换视图时的第一行)——忽略
    • 行是通过键盘选中的——忽略
    • 行选中的时间太久了(比如超过300毫秒)——忽略
    • 否则(通过点击选中,且少于300毫秒)——执行
0

你可以把单击事件安排在双击的时间之后,这样如果发生了双击,就可以在处理单击事件之前把安排好的单击事件取消掉。举个例子:

import pygtk
pygtk.require('2.0')
import gtk
import glib
class Test:
    def __init__(self):
        # Create a new window
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_title("Test")

        # It's a good idea to do this for all windows.
        self.window.connect("destroy", lambda wid: gtk.main_quit())
        self.window.connect("delete_event", lambda a1,a2:gtk.main_quit())

        # Sets the border width of the window.
        self.window.set_border_width(20)

        # Create a new button
        button = gtk.Button("Click")
        self.click_events = []

        # Connect the "clicked" signal of the button to our callback
        button.connect("button-press-event", self.on_button_press_event)
        button.show()

        self.window.add(button)
        self.window.show()

    def on_button_press_event(self, widget, event):
        if event.button == 1:
            if event.type == gtk.gdk._2BUTTON_PRESS:
                # Remove all single click events
                while self.click_events:
                    glib.source_remove(self.click_events.pop()) 
                glib.idle_add(self.handle_double_click,widget,event)
            elif event.type == gtk.gdk.BUTTON_PRESS:
                # Schedule after double click can occur
                self.click_events.append(glib.timeout_add(300,self.handle_single_click,widget,event))


    def handle_single_click(self,widget,event):
        # TODO: Handle single click
        print "TODO: Handle single click"


    def handle_double_click(self,widget,event):
        # TODO: Handle double click
        print "TODO: Handle double click"

def main():
    Test()
    gtk.main()
    return 0     

if __name__ == "__main__":
    main()
0

我想这不算是一个有效的答案……不过你有没有考虑过直接连接到IconView的“按下按钮”信号呢?

在事件回调中,你可以让Gdk来判断这是双击事件还是单击事件,只需要检查event->type就可以了(具体可以参考这个链接)。

撰写回答