Pytanie Zapobieganie PyQt do wyciszania wyjątków występujących w gniazdach


O ile widzę, jeśli wyjątek występuje w gnieździe w PyQt, wyjątek jest drukowany na ekranie, ale nie jest bełkotany. Powoduje to problem w mojej strategii testowania, ponieważ jeśli wystąpi wyjątek w gnieździe, nie zobaczę, że test się nie udał.

Oto przykład:

import sys
from PyQt4 import QtGui, QtCore

class Test(QtGui.QPushButton):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.setText("hello")
        self.connect(self, QtCore.SIGNAL("clicked()"), self.buttonClicked)

    def buttonClicked(self):
        print "clicked"
        raise Exception("wow")

app=QtGui.QApplication(sys.argv)
t=Test()
t.show()
try:
    app.exec_()
except:
    print "exiting"

Zwróć uwagę, że wyjątek nigdy nie opuszcza programu.

Czy jest jakiś sposób obejścia tego problemu?


21
2017-09-11 12:13


pochodzenie


Czy możesz wyjaśnić tę kwestię nieco bardziej? Sloty są tylko wyznaczonymi słuchaczami, które wywołują inny kod. Kod, który jest wywoływany, gdy występują poważne błędy, można obsłużyć jak każdy inny.
@Lego: tak, ale jeśli błąd jest propagowany w górę i opuszcza gniazdo, błąd ten jest wyciszany. Napiszę dziś przykład - Stefano Borini


Odpowiedzi:


Potrafi stworzyć dekorator, który otoczy nowe konstruktory sygnału / slotu PyQt i zapewni obsługę wyjątków dla wszystkich slotów. Może również przesłonić QApplication :: notify, aby wychwycić nieprzechwycone wyjątki C ++.

import sys
import traceback
import types
from functools import wraps
from PyQt4 import QtGui, QtCore

def MyPyQtSlot(*args):
    if len(args) == 0 or isinstance(args[0], types.FunctionType):
        args = []
    @QtCore.pyqtSlot(*args)
    def slotdecorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            try:
                func(*args)
            except:
                print "Uncaught Exception in slot"
                traceback.print_exc()
        return wrapper

    return slotdecorator

class Test(QtGui.QPushButton):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.setText("hello")
        self.clicked.connect(self.buttonClicked)

    @MyPyQtSlot("bool")
    def buttonClicked(self, checked):
        print "clicked"
        raise Exception("wow")

class MyApp(QtGui.QApplication):
    def notify(self, obj, event):
        isex = False
        try:
            return QtGui.QApplication.notify(self, obj, event)
        except Exception:
            isex = True
            print "Unexpected Error"
            print traceback.format_exception(*sys.exc_info())
            return False
        finally:
            if isex:
                self.quit()

app = MyApp(sys.argv)

t=Test()
t.show()
try:
    app.exec_()
except:
    print "exiting"

19
2017-09-25 21:41



Ponadto PyQt przechwytuje wyjątki Pythona i zapisuje do standardowego błędu. Jeśli zamkniesz wszystkie aplikacje, może się wydawać, że aplikacja się zawiesza. W procedurach obsługi wyjątków można z kolei wywołać ogólny sygnał, aby wykonać rejestrowanie lub dialog. - jlujan
Aby wyjść z aplikacji z wartością zwrotną inną niż 0, zadzwoń QtCore.QCoreApplication.exit(1) z obsługi wyjątków. - jlujan
Ta odpowiedź ma sens tylko w aplikacjach na krawędziach, dla których ustawienie sys.excepthook jest niewykonalne. Od ustawienia sys.excepthook jest zazwyczaj wykonalny, aukaost„s rozwiązanie jest prawie na pewno to, co każdy powinien robić zamiast tego: zainstaluj globalny hak wyjątku, przypisując żądaną wartość do wywołania sys.excepthook. - Cecil Curry


Możesz wyjść z aplikacji z niezerowym kodem powrotu, aby wskazać, że wystąpił wyjątek.
Możesz przechwycić wszystkie wyjątki, instalując globalny przechwytujący wyjątek. Dodałem przykład poniżej, ale prawdopodobnie będziesz chciał dostosować go do swoich potrzeb.

import sys
from PyQt4 import QtGui, QtCore

class Test(QtGui.QPushButton):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.setText("hello")
        self.connect(self, QtCore.SIGNAL("clicked()"), self.buttonClicked)

    def buttonClicked(self):
        print "clicked"
        raise Exception("wow")

sys._excepthook = sys.excepthook
def exception_hook(exctype, value, traceback):
    sys._excepthook(exctype, value, traceback)
    sys.exit(1)
sys.excepthook = exception_hook

app=QtGui.QApplication(sys.argv)
t=Test()
t.show()
try:
    app.exec_()
except:
    print "exiting"

9
2017-09-24 11:15



zapomniałem dodać. Nie mogę przesłonić excepthook ... z powodów. - Stefano Borini
Hah, więc. - aukaost
to skomplikowane. :( - Stefano Borini
To rozsądne rozwiązanie. Demonstracja sposobu zachowania i odroczenie standardu sys.excepthook wdrożenie jest szczególnie pomocne. - Cecil Curry