Python:两个GTK小部件如何相互交互?

时间:2013-08-09 16:43:37

标签: python gtk

在没有实际点击按钮的情况下与按钮交互的正确方法是什么?

我有一个按钮“按钮”,可以点击:

  • 调用方法“the_method”,它将打印传递给它的参数(此处为“filename”)
  • 切换自己的属性,这里是它的图标。

我有一个树视图,其行必须双击:

  • 调用方法“the_method”,它将打印传递给它的参数(此处为“filename”)
  • 切换“按钮”的属性,此处为其图标。

只有第一部分有效。调用“foo”函数(通过按钮的回调,直接用于树视图项),并且检索参数(“filename”),但是如何执行作业的第2部分(更改“按钮”的属性) ,这是它的图标)?


import gtk

class Lister(object):

    def __init__(self):
        self.hbox = gtk.HBox()

        liststore = gtk.ListStore(str)
        liststore.append(["foo"])
        liststore.append(["bar"])
        treeview = gtk.TreeView(liststore)
        self.hbox.pack_start(treeview, False)
        cell = gtk.CellRendererText()
        col = gtk.TreeViewColumn("Column 1")
        col.pack_start(cell, True)
        col.set_attributes(cell,text=0)
        treeview.connect('row-activated', self.open_file)
        treeview.append_column(col)

    def open_file(self, button, *args):
        Buttons().the_method(self, "foo")

class Buttons(object):

    OPEN_IMAGE = gtk.image_new_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_BUTTON)
    CLOSED_IMAGE = gtk.image_new_from_stock(gtk.STOCK_REFRESH, gtk.ICON_SIZE_BUTTON)

    def __init__(self):

        self.button = gtk.Button() # THIS is the button to modify
        self.hbox = gtk.HBox()
        self.hbox.pack_start(self.button, False)
        self.button.set_image(self.OPEN_IMAGE)

        self.button.connect('clicked', self.the_method, "plop")
        self.toggled = True

    def the_method(self, button, filename):
        print filename
        print vars(self)

        if self.toggled:
            self.button.set_image(self.CLOSED_IMAGE)
            self.toggled = False
        else:
            self.button.set_image(self.OPEN_IMAGE)
            self.toggled = True

class GUI(object):

    def delete_event(self, widget, event, data=None):
        gtk.main_quit()
        return False

    def __init__(self):
        self.window = gtk.Window()
        self.window.set_size_request(100, 150)
        self.window.connect("delete_event", self.delete_event)

        vbox = gtk.VBox()
        vbox.pack_start(Buttons().hbox, False, False, 1)
        vbox.pack_start(Lister().hbox)

        self.window.add(vbox)
        self.window.show_all()
        return

def main():
    gtk.main()

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

2 个答案:

答案 0 :(得分:4)

我非常不同意user1146332的回答。这不是一个GTK +问题,也不是一个强大的设计问题,只是一个面向对象的编程问题。您的错误的原因是您像这样致电the_method

Buttons().the_method(self, "foo")

这不起作用,因为你混合了两个不同的基本内容:一个类和一个类的实例。当您致电Buttons()时,您正在创建Buttons课程的新实例。因此,由于此类不是单例,您实际上是使用新的GtkButton创建一个新实例,并且最终不会与您之前创建的按钮进行交互。

这里的解决方案是让Lister对象知道它需要修改的内容,这意味着存储您之前创建的Buttons实例,例如在self.button中,并调用the_method就可以了。

self.button.the_method("foo")

以下是您的代码的略微修改版本。重要的是Lister实例现在知道它需要修改的Buttons实例。

import gtk

class Lister(object):

    def __init__(self, button):
        self.hbox = gtk.HBox()
        self.button = button

        liststore = gtk.ListStore(str)
        liststore.append(["foo"])
        liststore.append(["bar"])
        treeview = gtk.TreeView(liststore)
        self.hbox.pack_start(treeview, False)
        cell = gtk.CellRendererText()
        col = gtk.TreeViewColumn("Column 1")
        col.pack_start(cell, True)
        col.set_attributes(cell,text=0)
        treeview.connect('row-activated', self.open_file)
        treeview.append_column(col)

    def open_file(self, button, *args):
        self.button.the_method("foo")

class Buttons(object):

    OPEN_IMAGE = gtk.image_new_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_BUTTON)
    CLOSED_IMAGE = gtk.image_new_from_stock(gtk.STOCK_REFRESH, gtk.ICON_SIZE_BUTTON)

    def __init__(self):

        self.button = gtk.Button() # THIS is the button to modify
        self.hbox = gtk.HBox()
        self.hbox.pack_start(self.button, False)
        self.button.set_image(self.OPEN_IMAGE)

        self.button.connect('clicked', self.the_method, "plop")
        self.toggled = True

    def the_method(self, filename):
        print filename
        print vars(self)

        if self.toggled:
            self.button.set_image(self.CLOSED_IMAGE)
            self.toggled = False
        else:
            self.button.set_image(self.OPEN_IMAGE)
            self.toggled = True

class GUI(object):

    def delete_event(self, widget, event, data=None):
        gtk.main_quit()
        return False

    def __init__(self):
        self.window = gtk.Window()
        self.window.set_size_request(100, 150)
        self.window.connect("delete_event", self.delete_event)

        vbox = gtk.VBox()
        buttons = Buttons()
        vbox.pack_start(buttons.hbox, False, False, 1)
        vbox.pack_start(Lister(buttons).hbox)

        self.window.add(vbox)
        self.window.show_all()
        return

def main():
    gtk.main()

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

然而,仍有很大的改进空间。我建议您不要使用__init__函数来创建窗口小部件,而是使用create方法返回窗口小部件树的顶层窗口小部件。这是因为您无法在__init__中返回任何内容,因此使用其他方法更容易,而不是在那里引发异常。

b = Buttons()
vbox.pack_start(b.create(), False, False, 1)
l = Lister(b)
vbox.pack_start(l.create(), False, False, 1)

其他改进可能是(对不起,我在这里使用C命名为GTK类/函数,我比python更好):

  • 使用GtkToggleButton而不是自己跟踪按钮状态
  • 使用gtk_button_set_use_stock告诉按钮将您在按钮中设置的标签解释为按钮的库存ID(这可能会打印相关文本,对此不确定)
  • 切换到GTK 3(使用pyGObject),因为这是GTK 2代码(使用pyGTK),除非你想要Windows兼容性

在linuxfr上见到你: - )

答案 1 :(得分:0)

首先,我对python一无所知,但我对gtk+有一些经验,而且我或多或少熟悉它的概念。

我注意到的第一件事是你定义了一个名为GUI的类和两个名为ButtonsLister的独立类。对我来说,这样的方法只有在你设计最后提到的两个类时才有意义,因为它们是一种(复合)小部件。这样您就可以在更高级别实例化它们,例如在GUI类中。这将是一种通用方法,如果您想重用这些新小部件,则非常有意义。

你这样做的方式对我来说没有意义。根据我迄今为止收集的内容ButtonsLister的实际目的是使用小部件填充主应用程序窗口,将回调连接到这些小部件的信号,并将这些回调定义为方法。

我认为如果你这样做的话,你会限制gtk的灵活性。例如,您将信号连接到回调,原则上您无法访问界面的所有小部件。相比之下,我更喜欢代码中的一个常见位置,在该位置我将信号连接到回调,并且我可以主要将所有感兴趣的小部件传递给特定的回调。

事实上,人们经常需要在回调中对几个小部件进行操作。因此,您必须考虑将回调实现为GUI类的方法,它们可以主要访问用户界面的所有元素。

另外,您应该考虑使用glade设计界面。这样你的代码就会更清晰。


补充(在推送一些代码之后):

import gtk

class GUI(object):

    OPEN_IMAGE = gtk.image_new_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_BUTTON)
    CLOSED_IMAGE = gtk.image_new_from_stock(gtk.STOCK_REFRESH, gtk.ICON_SIZE_BUTTON)
    toggled = True

    def __init__(self):
        self.window = gtk.Window()
        self.window.set_size_request(100, 150)
        self.window.connect("delete_event", gtk.main_quit)

        vbox = gtk.VBox()

        self.button = gtk.Button() # THIS is the button to modify
        self.button.set_image(self.OPEN_IMAGE)

        liststore = gtk.ListStore(str)
        liststore.append(["foo"])
        liststore.append(["bar"])
        self.treeview = gtk.TreeView(liststore)
        cell = gtk.CellRendererText()
        col = gtk.TreeViewColumn("Column 1")
        col.pack_start(cell, True)
        col.set_attributes(cell,text=0)
        self.treeview.append_column(col)

        vbox.pack_start(self.button, False, False, 1)
        vbox.pack_start(self.treeview, False, False, 1)

        self.treeview.connect('row-activated', self.the_method_wrapper, "plop")
        self.button.connect('clicked', self.the_method, "plop")

        self.window.add(vbox)
        self.window.show_all()
        return

    def the_method_wrapper(self, button, *args):
        self.the_method(self, "foo")

    def the_method(self, button, filename):
        print filename
        print vars(self)

        if self.toggled:
            self.button.set_image(self.CLOSED_IMAGE)
            self.toggled = False
        else:
            self.button.set_image(self.OPEN_IMAGE)
            self.toggled = True


def main():
    gtk.main()

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