【问题标题】:Close separately two windows in Python GTK (Glade)在 Python GTK (Glade) 中分别关闭两个窗口
【发布时间】:2019-12-21 19:41:10
【问题描述】:

我最近开始使用 Python GTK 和 Glade 开发一些简单的 GUI。我创建了一个 GUI,它由一个带有按钮的主窗口组成,而按下按钮的动作是弹出第二个窗口,上面有一个 matplotlib 图。

我面临的问题是,当我关闭第二个窗口时,第一个窗口也关闭了,我希望能够单独终止它们。

下面是 python 代码,here 是带有 GUI 布局的 glade 文件。

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

import numpy as np
from matplotlib.figure import Figure
from matplotlib.axes import Subplot
from matplotlib.backends.backend_gtk3agg import (
    FigureCanvasGTK3Agg as FigureCanvas)

class PlotApp:

    def __init__(self):
        gladefile = 'GTK-two-windows.glade'

        self.builder = Gtk.Builder()
        self.builder.add_from_file(gladefile)

        main_win = self.builder.get_object("window1")
        main_win.connect("delete-event", Gtk.main_quit)

        button   = self.builder.get_object('button')
        button.connect('clicked', self.plot)

        main_win.show_all()

    def plot(self, widget):
        window2  = self.builder.get_object("window2")
        window2.connect("delete-event", Gtk.main_quit)

        scrolledwindow = self.builder.get_object("scrolledwindow")

        # ----- Start of Matplotlib specific code -----
        figure = Figure(figsize=(8, 6), dpi=71)
        axis = figure.add_subplot(111)
        t = np.arange(0.0, 3.0, 0.01)
        s = np.sin(2*np.pi*t)
        axis.plot(t, s)

        axis.set_xlabel('time [s]')
        axis.set_ylabel('voltage [V]')

        canvas = FigureCanvas(figure)     # a Gtk.DrawingArea
        canvas.set_size_request(800, 600)
        scrolledwindow.add_with_viewport(canvas)
        # ----- End of Matplotlib specific code -----

        window2.show_all()

if __name__ == "__main__":
    main = PlotApp()
    Gtk.main()

导入来自 vext.gi、numpy 和 matplotlib python 包,我使用的 Glade 版本是 3.22.1,我的操作系统是 Elementary Linux 5.1 Hera。

【问题讨论】:

    标签: python gtk glade


    【解决方案1】:

    您需要更改调用:

    .connect("delete-event", Gtk.main_quit)
    

    这样他们只有在这是最后一个打开的窗口时才调用Gtk.main_quit

    将直接调用Gtk.main_quit 替换为:

    def exit(window, event):    
        if not any(w.get_visible() for w in Gtk.Window.list_toplevels() if w is not window):
            Gtk.main_quit()
    

    这使得关闭窗口只会在它是最后一个可见窗口时退出应用程序。


    如果你只这样做:

    window.hide()
    return True
    

    在您的销毁事件处理程序中,您永远不会关闭应用程序。

    将我认为没有问题的问题与我建议的代码结合起来会导致main_quit 在所有窗口都关闭时被调用。

    【讨论】:

    • 感谢您的回复,这是一个合适的答案,但是,当我关闭第二个窗口并按下按钮重新绘制并重新打开它时,它显示为空(没有情节)和以下警告:Gtk -CRITICAL : gtk_widget_get_window:assertion 'GTK_IS_WIDGET (widget) failed
    • 我使用了您提供的功能,但我仍然得到相同的结果,即第二个窗口重新打开但内容为空白。这可能是合理的,因为据我了解,它仍然会破坏弹出窗口而不是隐藏 - 根据这篇文章 (stackoverflow.com/questions/40736476/…) 应该是这样。我将在下面提供一个似乎可以根据上述帖子解决问题的解决方案。
    • 确实命令 window.hide() 将隐藏第二个窗口而不是杀死它,但这是我的目标,因为 Gtk.main_quit() 将关闭两个窗口(主要,即第一个带有按钮,第二个,即单击按钮后的弹出窗口)。经过更多研究后,我发现我正在寻找的功能是隐藏第二个窗口但破坏包含的滚动条窗口,以便能够重新绘制。
    【解决方案2】:

    经过一些研究(Reopen window throws Gtk-CRITICAL **: gtk_widget_get_window: assertion 'GTK_IS_WIDGET (widget)' failed)和一些实验,我得出结论,由我的主窗口按钮触发的第二个弹出窗口不应该用事件函数 Gtk.main_quit() 关闭,但它应该使用事件函数 hide() 隐藏。因此,第一个主窗口将保持打开状态。

    但是,由于 matplotlib 绘图绘制在包含在我的第二个弹出窗口的子小部件(滚动条窗口)中的 FigureCanvas 上,因此需要弹出窗口的删除事件来销毁滚动条窗口为了能够重新绘制并避免现有子小部件的错误。

    所以在我原来的问题的代码中,以下修改添加了所需的功能:

    [1] 从 glade 文件中删除滚动条窗口部件

    [2]在弹窗顶部添加滚动条窗口:

    scrolledwindow = Gtk.ScrolledWindow()
    
        #----- Start of Matplotlib specific code -----
            figure = Figure(figsize=(8, 6), dpi=71)
            axis = figure.add_subplot(111)
            t = np.arange(0.0, 3.0, 0.01)
            s = np.sin(2*np.pi*t)
            axis.plot(t, s)
    
            axis.set_xlabel('time [s]')
            axis.set_ylabel('voltage [V]')
    
            canvas = FigureCanvas(figure)     # a Gtk.DrawingArea
            canvas.set_size_request(800, 600)
            scrolledwindow.add_with_viewport(canvas)
            #scrolledwindow.add(canvas)
        # ----- End of Matplotlib specific code -----
    
            window2.add(scrolledwindow)
            window2.show_all()
    

    [3]改变弹窗事件功能:

    window2.connect("delete-event", self.destroy)
    

    其中destroy函数定义为:

    def destroy(self, widget, event):
        scrolledwindow = widget.get_child()
        scrolledwindow.destroy()
        widget.hide()
        return True
    

    然后无论我关闭第二个窗口并按下主窗口中的按钮多少次,绘图都会绘制在(隐藏的)第二个窗口上。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多