Как я могу избежать продолжения работы моей программы при закрытии основного фрейма? - PullRequest
1 голос
/ 19 июня 2019

Я сейчас изучаю библиотеку wxPython самостоятельно. У меня появилась идея создать графический интерфейс с одним основным фреймом, который может открыть дочерний.

Я знаю, что могу сделать это, скомпилировав оба кадра в один и тот же код, но для моего проекта мне нужно разделить их.

Мне удалось управлять открытием дочернего фрейма и его закрытием, но, к сожалению, это создает новую проблему в моем родительском фрейме.

Вот мои коды:

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)

Так что моя главная проблема в том, что когда я нажимаю кнопку «Выход» или поле «x» в родительском фрейме (wx_practicing.py), фрейм закрывается, но программа не закрывается. После нескольких попыток я заметил, что это вызвано объявлением self.child в MainWindow.

Однако мне нужно это объявление, чтобы MainWindow открывал MainWindow_child. Я пытался добавить self.child.Close () в мой метод Onquit () в моем классе MainWindow, но это не удалось.

После некоторых исследований я понял, что, возможно, могу использовать обработчик CloseEvent, но я не совсем понял его назначение и принципы его работы.

Надеюсь, я был достаточно ясен.

ПРИМЕЧАНИЕ. Обе программы находятся в одной папке.

Ответы [ 2 ]

1 голос
/ 19 июня 2019

Вызовите дочернее окно с помощью 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

1 голос
/ 19 июня 2019

Добро пожаловать в StackOverflow

Проблема с вашим кодом в том, что вы создаете два экземпляра MainWindow_child. Это можно увидеть, добавив print(self.child) под каждой строкой, например self.child = wx_Practicing_child.MainWindow_child() в файле wx_Practicing.py. Если вы сейчас запустите свой код и создадите дочерний фрейм, вы получите что-то вроде:

<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 указывает на другой объект. Живой навсегда дочерний фрейм - причина, по которой ваша программа никогда не закрывается.

Для большей ясности, если вы измените методы OnQuit() и OnClose() в wx_Practicing.py на:

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 указывает на единственный экземпляр дочернего фрейма. После нажатия кнопки у вас есть два экземпляра, один из которых не отображается и без указателя, и программа больше не закрывается.

...