2014-10-16 40 views
1

我已經使用wx python編寫了一個相當複雜的腳本,類等等的python應用程序。該應用程序從外部源獲取xml文件並操縱它們,這有時會在腳本的各個不同區域引起意外的異常。在IDLE中運行應用程序時這很好,我可以在IDLE shell中看到任何異常,但我需要將其作爲獨立應用程序運行,我使用py2app和pyinstaller。所以切入追逐,我的問題是...作爲獨立應用程序運行時,我想打印任何python異常到wx.stc.StyledTextCtrl窗口或類似的東西,所以我至少可以看到任何奇怪的事情發生。我曾看過使用tryexcept Exception來捕捉異常,但我必須重新編寫我的應用程序以包含這些類型的異常。我有什麼選擇? 這裏是我的應用程序的一個非常小的例子,顯然與膽量丟失:Python輕鬆捕獲所有在wx python應用程序中的traceback(最近調用最後一個)

import wx 
import os 
import os.path 
import subprocess 
import pipes 
import platform 
import shlex 
from subprocess import Popen, PIPE 
import sys 
import threading 
import wx.stc as stc 
import traceback 

class MainWindow(wx.Frame): 
    def __init__(self, parent, id, title): 
     wx.Frame.__init__(self, parent, id, title, size=(600, 550), style=wx.DEFAULT_FRAME_STYLE & ~ (wx.RESIZE_BORDER | 
               wx.RESIZE_BOX | 
               wx.MAXIMIZE_BOX)) 

     self.run_params = {} 
     self.run_params["refresh"] = False 
     systemType = platform.system() 
......................... 

app = wx.App() 
MainWindow(None, -1, 'My Application') 
app.MainLoop() 
+0

您是否正在尋找一種方法來定義一個「未處理的異常處理程序」,將被調用任何異常中的任何異常發生'塊? – abarnert 2014-10-16 21:39:54

+0

是的,這聽起來正是我想要的,只要我只能使用1次嘗試。 – speedyrazor 2014-10-16 21:46:16

回答

2

快速谷歌爲「wxPython的回溯」在wxPython的維基止跌回升文章CustomExceptionHandling

維基頁面提供的代碼樣本比您要求的要多得多,比如通過電子郵件提交錯誤報告。我只是展示如何做最符合問題的最簡單的事情,但請閱讀頁面以獲取更多的建議。

class MyApp(wx.App): 
    def OnInit(self): 
     sys.excepthook = self.OnException 
     # your existing OnInit code 

    def OnException(self, type, value, tb): 
     lines = traceback.format_exception(type, value, tb) 
     msg = '\n'.join(lines) 
     # Now create your dialog and display msg 

請注意,如果您的應用程序是沒有考慮異常安全寫的,有一個非常好的機會,未處理的異常意味着你在可能不可恢復的狀態是,有沒有簡單的方法來判斷自己是否是否。所以,你的對話框應該只有一個「退出」按鈕,你應該以模態方式顯示它,然後一旦點擊就退出。


如果你有一個多線程應用程序,這可能無法工作,因爲threading有每個線程自身的異常處理程序。有關此問題的各種解決方案,請參見#1230540。但幾個注意事項:

  • 你要麼需要使主線程調用sys.excepthook對你來說,與其說這是直接在工作線程,或寫你的excepthook是工作者線程安全(特別是,讓它讓主線程代表它創建對話框)。我將在下面展示如何做到這一點,雖然它是未經測試的代碼,並且我在一段時間內沒有使用wx,所以它可能需要一些工作。
  • 如果您使用的是Thread子類,而不是像target函數那樣使用該類,則只有Ian Beaver's solution將適用於您;我不會表明這一點,但它並不太棘手。
  • 如果你的GUI的主循環沒有在主線程上運行(這種情況並不常見,但不是不可能,或者是非法的),如果沒有一些大的改變,這可能無法正常工作。我不記得如何處理這些。
  • 早期版本的Python中存在一些錯誤,導致其中一些解決方案無法正常工作;我不知道哪些版本或者它們是什麼。

所以,這裏的一個簡化版本的Jonathan Ellis's solution我的未經檢驗的WX-量身定製的版本:

import sys 
import threading 
import wx 

ID_EXCEPTHOOK = wx.NewId() 

def EVT_EXCEPTHOOK(win, func): 
    win.Connect(-1, -1, ID_EXCEPTHOOK, func) 

class ExceptionEvent(wx.PyEvent): 
    def __init__(self, type, value, tb): 
     super(ExceptionEvent, self).__init__() 
     self.SetEventType(ID_EXCEPTHOOK) 
     self.type, self.value, self.tb = type, value, tb 

class WorkerThread(threading.Thread): 
    def __init__(self, main_window, *args, **kwargs): 
     super(WorkerThread, self).__init__(*args, **kwargs) 
     self.main_window = main_window 
    def run(*args, **kwargs): 
     try: 
      super(WorkerThread, self).run(*args, **kwargs) 
     except (KeyboardInterrupt, SystemExit): 
      raise 
     except: 
      wx.PostEvent(self.main_window, ExceptionEvent(*sys.exc_info())) 

class MainFrame(wx.Frame): 
    def __init__(self, parent, id): 
     # existing code 
     EVT_EXCEPTHOOK(self, self.OnException) 
    def OnException(self, event): 
     sys.excepthook(event.type, event.value, event.tb) 

class MyApp(wx.App): 
    # same as before 

現在,只要您創建一個線程,你必須使用WorkerThread代替Thread,並通過它你MainFrame實例作爲額外的第一個參數。但大概你創建的線程比你編寫函數的次數少得多,所以希望這不是問題(如果它的問題,你可以添加代碼到WorkerThread獲得單例應用程序,詢問它爲它的主窗口,並使用它,然後monkeypatch threading.Thread = WorkerThread。)

+0

正是我在找什麼,謝謝。 – speedyrazor 2014-10-17 04:57:52

+0

剛發現一個問題,它沒有捕獲到線程中的異常? – speedyrazor 2014-10-17 05:36:08

+0

@speedyrazor:我會編輯答案。 – abarnert 2014-10-17 17:56:42

相關問題