关闭主机时如何避免程序继续运行?

时间:2019-06-19 09:06:01

标签: python-3.x button wxpython frame

我目前正在独自学习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处理程序,但我并不真正了解它的用途和工作原理。

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

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

2 个答案:

答案 0 :(得分:1)

欢迎使用StackOverflow

您的代码存在问题,因为您正在创建两个MainWindow_child实例。您可以通过在文件wx_Practicing.py中的print(self.child)之类的每一行下方添加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指向子框架的唯一实例。按下按钮后,您将拥有两个实例,其中一个未显示且没有指针,并且程序不再关闭。

答案 1 :(得分:1)

通过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)

另一种方法是对子框架执行初始调用,并将子打开标志移到父框架中。
为此,孩子需要记录父项,并确保不仅在退出按钮上而且在右上角关闭的窗口中将子项打开标志设置为false。

这使您的主程序如下:

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()

enter image description here