メインフレームの閉じるボタンをクリックすると、アプリケーションが閉じることになっています。しかし、それを実装した方法では、ボタンをクリックすると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__()
フレームを閉じる通常の方法は次のとおりです。
_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にバインドする主な理由は、closeイベントをキャッチしてユーザーに情報の保存を要求できるようにするためです。それを行う場合は、「self.Close」の代わりに_self.Destroy
_を使用して、そのイベントを発生させないようにする必要があります。
すでに述べたように、クローズハンドラーでは、スレッドを終了し、タスクバーアイコンを破棄し、開いているファイルを閉じて、何もハングしないようにする必要があります。参照: http://wxpython.org/docs/api/wx.CloseEvent-class.html
私は2つの問題に直面していました。それらを見つけるのに役立つ3つすべての回答に感謝します。
まず、フレームは、実行中のスレッドを持つ属性_self.stuff
_を持っているため、self.Close()
またはself.Destroy()
に応答しません。このスレッドは最初に閉じる必要があります。
次に、self.Close()
は、クローズイベントに応答し、呼び出されたときに無限再帰を引き起こすハンドラー内にあります。これにより、ランタイムエラーが発生します(セグメンテーション違反、再帰の深さを超えました)。解決策は、代わりにself.Destroy()
を使用することです。
アプリケーションを閉じるためにコールバックが必要なのはなぜですか? MainFrame
に何もバインドしない場合、閉じるボタンをクリックすると、アプリケーションが自動的に閉じられます。プロセスの終了を妨げる他のスレッドが実行されている場合を除きます。それでもcloseイベントにバインドし、閉じる前に何かを実行したい場合は、ウィンドウを閉じたい場合は、イベントハンドラーで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
が呼び出されないため、ウィンドウは閉じられません。
前の回答の1つは、アプリケーションを終了する前にユーザー情報を保存するために、Close()ではなくDestroy()を使用することについて説明しています。終了するかどうかをユーザーに尋ねるサンプルコードを次に示します。彼らが肯定的に応答すると、フレームを「破棄」してアプリケーションを終了します。
# 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()
アプリのすべてのトップレベルフレームを閉じると、閉じるなどにバインドする必要なく、アプリケーションが終了します。複数ある場合、または一部が非表示になっている場合は、.Close()
を使用して自分で閉じることができます。
また、TaskBarIconがある場合は、アプリを終了する前に破棄する必要があります。
あなたのセグメントの欠陥は、フレームがどのように破壊されて子供を破壊するかと関係がある別の理由が原因であると私は思う。私はコードをステップ実行してみて、セグフォールトが発生しているポイントを見つけます。