PyGTK运行两个窗口,弹出和主窗口。

6
我正在使用Python编写脚本,与Bash配合使用。当我设置了所有搜索选项并点击查找按钮时,我希望弹出一个带有“进度条”的弹出窗口,在搜索完成后消失。我通过“popup.show()”打开“弹出窗口”,直到我关闭弹出窗口之前,没有任何功能被执行。那么如何解决这个问题?
在控制器类中:
def search(self, widget):
    cmd = "find " + self.model.directory + " -name \"" + self.model.name + "\"" + " -perm -" + str(self.model.mode)
    if self.model.type is not None and self.model.type != '':
        cmd += " -type " + self.model.type
    if self.model.owner is not None:
        cmd += " -user " + self.model.owner
    if self.model.days is not None:
        cmd += " -mtime -" + str(self.model.days)

    self.progress = SearcherProgressBar()

    output = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
    out = output.stdout.read().strip()
    array = string.split(out, '\n')
    self.list = list()
    self.progress.label = "sdsds"
    for value in array:
        self.add_to_list(value)

    #self.progress.popup.destroy() # when I uncomment, popup never appears

    self.view.treestore.clear()
    self.add_to_tree(self.list, None)
    self.view.expand_item.set_sensitive(True)

在进度条类中:

class SearcherProgressBar:

def __init__(self):
    self.popup = gtk.Window(gtk.WINDOW_POPUP)
    vbox = gtk.VBox()
    self.popup.add(vbox)
    self.popup.set_size_request(500,100)
    self.label = gtk.Label("Searching...")
    vbox.pack_start(self.label, True, True, 0)
    self.popup.connect("destroy", self.dest)
    self.popup.show_all()


def dest(self, widget, data=None):
    self.popup.destroy()
    return False

3
请将您的代码制作成SSCCE - 简短,自包含,正确(可编译)示例。我们无法运行您在问题中提供的代码,因为它们只是应用程序某部分的片段。 - Marwan Alsabbagh
2个回答

2

无论如何,这个问题似乎非常有趣,尽管从外部看很难理解你想要什么或者你的程序如何工作以及预期如何工作。我编写了最小化的代码来演示如何制作弹出窗口。

我结合使用了 subprocessingthreading 两种技术实现了这个功能。

在这段代码中需要注意一些重要的部分:

  • 由于使用了 threadinggtk 的组合,我们必须使用 gobject.threads_init()

  • 那些不在线程内执行的函数会快速执行并将控制权返回给 gtk.main 循环

  • 搜索按钮在被调用时被禁用,以免堆积子进程。

下面是代码:

#!/usr/bin/env python

import gtk
import threading
import gobject 
from subprocess import Popen, PIPE

class SearcherProgressBar(object):
    """This is the popup with only progress-bar that pulses"""
    def __init__(self):
        self._popup = gtk.Window(gtk.WINDOW_POPUP)
        self._progress = gtk.ProgressBar()
        self._progress.set_text = gtk.Label("Searching...")
        self._popup.add(self._progress)

    def run(self, search, target):
        """Run spawns a thread so that it can return the process to the
        main app and so that we can do a little more than just execute
        a subprocess"""
        self._popup.show_all()
        self._thread = threading.Thread(target=self._search, args=(search, target))
        self._thread.start()

        #Adding a callback here makes gtk check every 0.42s if thread is done
        gobject.timeout_add(420, self._callback)

    def _search(self, cmd, target):
        """This is the thread, it makes a subprocess that it communicates with
        when it is done it calls the target with stdout and stderr as arguments"""
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        target(*p.communicate())

    def _callback(self, *args):
        """The callback checks if thread is still alive, if so, it pulses
        return true makes callback continue, while false makes it stop"""
        if self._thread.is_alive():
            self._progress.pulse()
            return True
        else:
            self._popup.destroy()
            return False

class App(object):

    def __init__(self):

        self._window = gtk.Window()
        self._window.connect("destroy", self._destroy)

        vbox = gtk.VBox()
        self._window.add(vbox)

        self.button = gtk.Button()
        self.button.set_label('Pretend to search...')
        self.button.connect('clicked', self._search)
        vbox.pack_start(self.button, False, False, 0)

        entry = gtk.Entry()
        entry.set_text("This is here to show this gui doesn't freeze...")
        entry.connect("changed", self._gui_dont_freeze)
        vbox.pack_start(entry, False, False, 0)

        self._results = gtk.Label()
        vbox.pack_start(self._results, False, False, 0)

        self._gui_never_freeze = gtk.Label("Write in entry while searching...")
        vbox.pack_start(self._gui_never_freeze, False, False, 0)

        self._window.show_all()

    def run(self):

        gtk.main()

    def _gui_dont_freeze(self, widget):

        self._gui_never_freeze.set_text(
            "You've typed: '{0}'".format(widget.get_text()))

    def _search(self, widget):
        """Makes sure you can't stack searches by making button
        insensitive, gets a new popup and runs it. Note that the run
        function returns quickly and thus this _search function too."""
        self.button.set_sensitive(False)
        self._results.set_text("")
        popup = SearcherProgressBar()
        popup.run(['sleep', '10'], self._catch_results)

    def _catch_results(self, stdout, stderr):
        """This is where we do something with the output from the subprocess
        and allow button to be pressed again."""
        self.button.set_sensitive(True)
        self._results.set_text("out: {0}\terr: {1}".format(stdout, stderr))

    def _destroy(self, *args):

        self._window.destroy()         
        gtk.main_quit()

if __name__ == "__main__":

        #This is vital for threading in gtk to work correctly
        gobject.threads_init()
        a = App()
        a.run()

非常感谢您的回答。我已经用您帖子上方的简短代码完成了我的程序,但我一定会分析您的代码,并学习一些有关Python线程的知识。 - dragon7

1
【已解决】 此代码应在每个小部件更新后添加。
while gtk.events_pending():
        gtk.main_iteration()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接