Подтвердить что ты не робот

Как убить приложение WxPython, когда пользователь нажимает на рамку закрытия

Приложение должно закрываться, когда я нажимаю кнопку закрытия основного фрейма. Но как я его реализовал, он завершает работу с помощью Segmentation fault, когда я нажимаю кнопку.

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

Что такое правильный ненасильственный способ, чтобы завершить приложение WxPython, нажав кнопку закрытия?


Вот "основной" цикл реализованной мной программы:

if __name__ == "__main__":
    app = wx.App(False)
    mf = MainFrame(None, title='Spectrum Checker') #subclasses frame
    mf.register_close_callback( app.Destroy) #what is the apt func?
    app.MainLoop()

Вот как реализуется обратный вызов в MainFrame:

def __init__(self, parent, title):
    ...
    self.Bind(wx.EVT_CLOSE, self._when_closed)

...

def _when_closed(self, event):
if self.__close_callback__:
    self.__close_callback__()
4b9b3361

Ответ 1

Здесь нормальный метод закрытия кадра:

import wx

########################################################################
class MyFrame(wx.Frame):
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title="Close Me")
        panel = wx.Panel(self)

        closeBtn = wx.Button(panel, label="Close")
        closeBtn.Bind(wx.EVT_BUTTON, self.onClose)

    #----------------------------------------------------------------------
    def onClose(self, event):
        """"""
        self.Close()

if __name__ == "__main__":
    app = wx.App(False)
    frame = MyFrame()
    frame.Show()
    app.MainLoop()

Теперь, если у вас есть привязка к wx.EVT_CLOSE, вы попадете в бесконечный цикл, пока не получите segfault. Основной причиной привязки к EVT_CLOSE является то, что вы можете поймать событие закрытия и попросить пользователя сохранить их информацию. Если вы хотите это сделать, вам нужно использовать self.Destroy вместо "self.Close", чтобы вы не стреляли в это событие.

Как уже упоминалось, в вашем закрытом обработчике вам нужно убедиться, что вы завершаете потоки, уничтожаете значки на панели задач, закрываете открытые файлы и т.д., поэтому ничего не висит. См. Также: http://wxpython.org/docs/api/wx.CloseEvent-class.html


Добавление OP

У меня возникли два вопроса: я благодарю все три ответа, помогая мне найти их:

Во-первых, кадр не отвечает на self.Close() или self.Destroy(), потому что у него есть атрибут self.stuff, у которого есть работающий поток. Сначала этот поток должен быть закрыт.

Во-вторых, self.Close() в обработчике, который реагирует на закрытие событий и вызывает бесконечную рекурсию при вызове. Это вызывает ошибку времени выполнения (ошибка сегментации, глубина рекурсии). Решение состоит в том, чтобы вместо этого использовать self.Destroy().

Ответ 2

Я не уверен, зачем вам нужен обратный вызов, чтобы закрыть приложение? Если вы не привязываете ничего к MainFrame, приложение автоматически закрывается, когда вы нажимаете кнопку закрытия. Если у вас нет другого потока, который предотвращает завершение процесса. Если вы все еще хотите привязываться к событию закрытия и делать что-либо перед закрытием, вы должны использовать event.Skip() в обработчике событий, если вы все еще хотите закрыть окно. В противном случае событие не будет продолжено, а обработчик по умолчанию не будет выполнен. Пример:

import wx

class MainFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        self.__close_callback = None
        self.Bind(wx.EVT_CLOSE, self._when_closed)

    def register_close_callback(self, callback):
        self.__close_callback = callback

    def _when_closed(self, event):
        doClose = True if not self.__close_callback else self.__close_callback()
        if doClose:
            event.Skip()

if __name__ == "__main__":
    app = wx.App(False)
    mf = MainFrame(None, title='Test')
    mf.Show()
    mf.register_close_callback(lambda: True)
    app.MainLoop()

Когда вы выполняете код и закрываете его, приложение закрывается. Когда вы меняете функцию обратного вызова на lambda: False, окно не будет закрыто, так как Skip не вызывается.

Ответ 3

В одном из предыдущих ответов обсуждается использование Destroy(), а не Close(), чтобы сохранить информацию о пользователе перед выходом из приложения. Вот пример кода, запрашивающего пользователя, если он намерен выйти. Если они отвечают утвердительно, это "уничтожает" кадр и завершает работу приложения.

# Bind our events from our menu item and from the close dialog 'x' on the frame
def SetupEvents(self):
    self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
    self.Bind(wx.EVT_MENU, self.OnCloseFrame, self.fileMenuExitItem)


# Destroys the main frame which quits the wxPython application
def OnExitApp(self, event):
    self.Destroy()


# Makes sure the user was intending to quit the application
def OnCloseFrame(self, event):
    dialog = wx.MessageDialog(self, message = "Are you sure you want to quit?", caption = "Caption", style = wx.YES_NO, pos = wx.DefaultPosition)
    response = dialog.ShowModal()

    if (response == wx.ID_YES):
        self.OnExitApp(event)
    else:
        event.StopPropagation()

Ответ 4

Закрытие всех фреймов верхнего уровня в приложении должно заканчивать приложение без необходимости привязки к закрытию или чем-либо еще. Вы можете закрыть их самостоятельно с помощью .Close(), если у вас несколько или скрытые.

Также, если у вас есть TaskBarIcon, его нужно будет уничтожить до того, как приложение закончится.

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