【问题标题】:How can I avoid my program to keep running when I close my main frame?当我关闭主框架时,如何避免程序继续运行?
【发布时间】:2019-11-01 23:22:23
【问题描述】:

我目前正在自学 wxPython 库。我的想法是创建一个带有一个可以打开一个子框架的主框架的 GUI。

我知道我可以通过将两个框架编译成相同的代码来做到这一点,但对于我的项目,我需要将它们分开。

我成功管理了子框架的打开和关闭,但不幸的是,它在我的父框架中产生了一个新问题。

这是我的代码:

wx_Practicing.py

import wx
import time
import wx_Practicing_child
import threading
import os
import sys

"""Class which defines my main frame."""

class MainWindow(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Test", wx.DefaultPosition,
        (1000,850), wx.DEFAULT_FRAME_STYLE, wx.FrameNameStr)

        # Click counter
        self.click = 0

        # Init of the opening variable which is set to 1 when a child frame is opened
        self.OpenButtonFlag = 0

        # Init of the frame child invoked by the parent frame
        self.child = wx_Practicing_child.MainWindow_child()
        self.child.label = "child"

        # Sizers
        sizer_hori = wx.BoxSizer(wx.HORIZONTAL)
        sizer_verti = wx.BoxSizer(wx.VERTICAL)

        # Init of the panel

        test_panel = PanelMainWindow(self)
        test_panel.SetSizer(sizer_verti)

        # Buttons declaration
        # Button to quit the main frame
        btn_quit = wx.Button(test_panel, label ="Quit")
        btn_quit.Bind(wx.EVT_BUTTON, self.OnQuit)
        sizer_verti.Add(btn_quit)

        # Button counting number of time you trigger it

        btn_counter = wx.Button(test_panel, label="Click counter")
        sizer_verti.Add(btn_counter)
        btn_counter.Bind(wx.EVT_LEFT_DOWN, self.OnCount)

        # Button opening the child frame

        btn_new_frame = wx.Button(test_panel, label = "Open new frame",
                                    pos=(100,100))

        btn_new_frame.Bind(wx.EVT_LEFT_DOWN, self.OnNewFrame)


        self.Bind(wx.EVT_CLOSE, self.OnClose)

        # Frame displaying
        self.Show()

    def OnClose(self, event):

        self.Destroy(True)

    # Method used to close the parent frame
    def OnQuit(self, event):
        self.Destroy()
        print("closed")


    # Method used to count number of click
    def OnCount(self, event):
        self.click +=1
        print(self.click)

    # Method calling wx_Practicing_child.py to open a child frame
    def OnNewFrame(self, event):
        if self.child.OpenButtonFlag == 0 :
            self.child = wx_Practicing_child.MainWindow_child()
            self.child.label = "child"
            print("Flag before",self.child.OpenButtonFlag)
            self.child.Show()
            print("new Frame opened")
            self.child.OpenButtonFlag = 1
        else :
            print("Frame already launched, close the previous one and retry")
        print("Flag after", self.child.OpenButtonFlag)


"""Class of the panel"""

class PanelMainWindow(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

test = wx.App(False)
frame = MainWindow()

test.MainLoop()

和 wx_Practicing_child.py

import wx
import time


"""Classe définissant une frame (i.e la zone globale parente). Elle permet
de faire exister le panel et ses attributs."""

class MainWindow_child(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Test", wx.DefaultPosition,
        (1000,850), wx.DEFAULT_FRAME_STYLE, wx.FrameNameStr)

        self.OpenButtonFlag = 0
        self.label = "Child"

        # Sizers
        sizer_hori = wx.BoxSizer(wx.HORIZONTAL)
        sizer_verti = wx.BoxSizer(wx.VERTICAL)

        # Init of the panel

        test_panel_child = PanelMainWindow_child(self)
        test_panel_child.SetSizer(sizer_verti)

        # Buttons declaration
        # Button to quit the frame
        btn_quit = wx.Button(test_panel_child, label ="Quit")
        btn_quit.Bind(wx.EVT_LEFT_DOWN, self.OnQuit)
        sizer_verti.Add(btn_quit)

    # Method used to quit the frame
    def OnQuit(self, event):
        self.OpenButtonFlag = 0
        self.Destroy()
        print("child print", self.OpenButtonFlag)



"""Class which defines a panel for the child frame"""

class PanelMainWindow_child(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

所以我的主要问题是当我点击父框架(wx_practicing.py)上的“退出”按钮或“x”框时,框架关闭,但程序没有关闭。经过几次尝试,我注意到它似乎是由 MainWindow 中的 self.child 声明引起的。

但是,我需要此声明以允许 MainWindow 打开 MainWindow_child。 我曾尝试在 MainWindow 类的 Onquit() 方法中添加 self.child.Close(),但没有成功。

经过一些研究,我发现我也许可以使用 CloseEvent 处理程序,但我并不真正了解它的用途和工作原理。

我希望我已经足够清楚了。

注意:两个程序都在同一个文件夹中。

【问题讨论】:

    标签: python-3.x button wxpython frame


    【解决方案1】:

    欢迎来到 StackOverflow

    您的代码的问题是您正在创建两个 MainWindow_child 实例。您可以通过在每行下方添加print(self.child) 来查看这一点,例如文件 wx_Practicing.py 中的self.child = wx_Practicing_child.MainWindow_child()。如果你现在运行你的代码并创建一个子框架,你会得到类似的东西:

    <wx_Practicing_child.MainWindow_child object at 0x102e078b8>
    <wx_Practicing_child.MainWindow_child object at 0x1073373a8>
    Flag before 0
    new Frame opened
    Flag after 1
    

    上面的前两行表示您有两个 MainWindow_child 实例,并且指针 self.child 仅指向其中一个。因此,当您使用self.child.Destroy() 或以正常方式关闭子框架(红色 X 按钮)时,您只会破坏一个实例。另一个实例永远存在,因为您从不显示它,因此您不能按关闭按钮来销毁它或使用self.child.Destroy(),因为 self.child 指向不同的对象。永远活着的子框架是您的程序永远不会关闭的原因。

    为了更清楚起见,如果您将 wx_Practicing.py 中的 OnQuit()OnClose() 方法更改为:

    def OnClose(self, event):
        try:
            self.child.Destroy()
        except Exception:
            pass
        self.Destroy()
    
    # Method used to close the parent frame
    def OnQuit(self, event):
        try:
            self.child.Destroy()
        except Exception:
            pass
        self.Destroy()
        print("closed")
    

    如果您不按Open new frame 按钮,程序将关闭,因为self.child 指向子框架的唯一实例。按下按钮后,您将获得两个实例,其中一个未显示且没有指针,并且程序不再关闭。

    【讨论】:

    • 谢谢,现在可以正常使用了。但是我还是有一些误解。 “异常”是 Python 处理的特定类型的异常,还是 object 的默认异常?您还提到了@Rolf 帖子下有关父母和孩子之间信息共享的问题。它是否与我创建子框架的 2 个引用这一事实有关,因此父框架可能会丢失它必须与哪个共享数据?
    • 1- 处理尝试销毁不再存在的子框架时可能出现的任何错误除外。 2-是的,如果您不跟踪哪个子框架是指针 self.child 指向您,则可以将部分信息发送到一个实例,将另一部分信息发送到第二个、第三个子框架。最好控制它以避免奇怪的行为。为什么你说:我需要这个声明来允许 MainWindow 打开一个 MainWindow_child?
    • @ClemZer Exception 在这种情况下是任何例外,即如果有任何问题只是 pass 不要担心。
    • 我做了上一个主题,因为我遇到了打开一个且只有一个子框架的问题(顺便说一下你的帮助,再次感谢)。如果我删除 self.child = wx_Practicing_child.MainWindow_child(self),由于我的 OpenNewFrame() 方法缺少对 self.child 的引用,这将导致我的程序出错。或者也许我以错误的方式创建了这个方法,我需要继续努力。
    【解决方案2】:

    使用parent 即调用子窗口

    self.child = wx_Practicing_child.MainWindow_child(self)
    

    在孩子中声明父母,即

    class MainWindow_child(wx.Frame):
        def __init__(self, parent):
            wx.Frame.__init__(self, parent, wx.ID_ANY, "Test", wx.DefaultPosition,
            (1000,850), wx.DEFAULT_FRAME_STYLE, wx.FrameNameStr)
    

    现在当父级被销毁时,子级也将被销毁,程序将干净地退出。

    我认为test_panel_child = PanelMainWindow_child(self) 是某种编辑错误,实际上应该是test_panel_child = wx.Panel(self)
    使用self.Destroy() self.Destroy(True)

    另一种方法是对子框架执行初始调用并将子框架打开标志移动到父框架中。
    为了实现这一点,孩子需要记下父母并确保孩子打开标志不仅在退出按钮上设置为假,而且在右上角的窗口关闭上也设置为假。

    这会让你的主程序变成这样:

    import wx
    import time
    import wx_Practicing_child
    import threading
    import os
    import sys
    
    """Class which defines my main frame."""
    
    class MainWindow(wx.Frame):
        def __init__(self):
            wx.Frame.__init__(self, None, wx.ID_ANY, "Test", wx.DefaultPosition,
            (400,350), wx.DEFAULT_FRAME_STYLE, wx.FrameNameStr)
    
            # Click counter
            self.click = 0
    
            # Init of the opening variable which is set to 1 when a child frame is opened
            self.OpenButtonFlag = 0
    
            # Init of the frame child invoked by the parent frame
            #self.child = wx_Practicing_child.MainWindow_child(self)
            #self.child.label = "child"
            self.child_open_flag = False
    
            # Sizers
            sizer_hori = wx.BoxSizer(wx.HORIZONTAL)
            sizer_verti = wx.BoxSizer(wx.VERTICAL)
    
            # Init of the panel
    
            test_panel = PanelMainWindow(self)
            test_panel.SetSizer(sizer_verti)
    
            # Buttons declaration
            # Button to quit the main frame
            btn_quit = wx.Button(test_panel, label ="Quit")
            btn_quit.Bind(wx.EVT_BUTTON, self.OnQuit)
            sizer_verti.Add(btn_quit)
    
            # Button counting number of time you trigger it
    
            btn_counter = wx.Button(test_panel, label="Click counter")
            sizer_verti.Add(btn_counter)
            btn_counter.Bind(wx.EVT_LEFT_DOWN, self.OnCount)
    
            # Button opening the child frame
    
            btn_new_frame = wx.Button(test_panel, label = "Open new frame",
                                        pos=(100,100))
    
            btn_new_frame.Bind(wx.EVT_LEFT_DOWN, self.OnNewFrame)
    
    
            self.Bind(wx.EVT_CLOSE, self.OnClose)
    
            # Frame displaying
            self.Show()
    
        def OnClose(self, event):
    
            self.Destroy()
    
        # Method used to close the parent frame
        def OnQuit(self, event):
            self.Destroy()
            print("closed")
    
        # Method used to count number of click
        def OnCount(self, event):
            self.click +=1
            print(self.click)
    
        # Method calling wx_Practicing_child.py to open a child frame
        def OnNewFrame(self, event):
            if self.child_open_flag:
                    wx.MessageBox("Frame already launched, close the previous one and retry",'Error', wx.OK | wx.ICON_INFORMATION)
            else:
                self.child = wx_Practicing_child.MainWindow_child(self)
                self.child.label = "child"
                self.child.Show()
                self.child_open_flag = True
    
    """Class of the panel"""
    
    class PanelMainWindow(wx.Panel):
        def __init__(self, parent):
            wx.Panel.__init__(self, parent)
    
    test = wx.App(False)
    frame = MainWindow()
    
    test.MainLoop()
    

    和您的孩子计划

    import wx
    import time
    
    """Classe définissant une frame (i.e la zone globale parente). Elle permet
    de faire exister le panel et ses attributs."""
    
    class MainWindow_child(wx.Frame):
        def __init__(self, parent):
            wx.Frame.__init__(self, parent, wx.ID_ANY, "Test", wx.DefaultPosition,
            (200,250), wx.DEFAULT_FRAME_STYLE, wx.FrameNameStr)
            # make parent accessible
            self.parent = parent
            self.label = "Child"
    
            # Sizers
            sizer_hori = wx.BoxSizer(wx.HORIZONTAL)
            sizer_verti = wx.BoxSizer(wx.VERTICAL)
    
            # Init of the panel
    
            test_panel_child = wx.Panel(self)
            test_panel_child.SetSizer(sizer_verti)
    
            # Buttons declaration
            # Button to quit the frame
            btn_quit = wx.Button(test_panel_child, label ="Quit")
            btn_quit.Bind(wx.EVT_LEFT_DOWN, self.OnQuit)
            #Reset the open flag if the child is closed not using the button
            self.Bind(wx.EVT_CLOSE, self.OnQuit)
            sizer_verti.Add(btn_quit)
    
        # Method used to quit the frame
        def OnQuit(self, event):
            # clear the child open flag in the parent
            self.parent.child_open_flag = False
            self.Destroy()
    

    【讨论】:

    • 这将负责销毁所有子框架,但它不能解决无缘无故创建多个子框架实例的问题。如果 OP 需要在子框架之间传递信息,这可能是一个问题。
    • 谢谢,我什至没有想到我必须创建一个地方来在子构造函数中添加指向子的链接。
    • 如果有帮助,我已经添加了一个更完整的答案。
    • @kbr85 点数。我并没有真正看到创建它两次的目的,除了设置打开标志,可以在主程序中定义和保存。
    • @ClemZer 我更改了OnNewFrame 以考虑到self.child 之前没有被调用过。但是,如果您对自己的方法感到满意,那很好。欢迎使用 StackOverflow!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-27
    • 1970-01-01
    相关资源
    最近更新 更多