Reputation: 2459
I have a decorator that is supposed to try / except any method given to the wrapper so that it can log on the GUI any errors encountered in order to give the user a chance to understand what is going on if the application is not working
The decorator is used on multiple methods of different classes and encapsulates slots and is therefore declared outside of the classes ( outside the only class in this simplified example)
Currently the following code does not work as there is never the right amount of arguments given ( always one too many)
I tried different ways of writing the decorator but my understanding of them is limited and it turned out into a guesswork, witch also means that even if I stumbled onto the solution by chance I would not know why it works and not all of the previous errors
Here is a simplified code that illustrates my issue, there are different spin boxes that connect to different slots each taking a different amount of arguments and the same decorator is used on all of them
import sys
from functools import partial
from PyQt5.QtWidgets import QSpinBox, QWidget, QHBoxLayout, QApplication
def except_hook(cls, exception, error_traceback):
sys.__excepthook__(cls, exception, error_traceback)
def my_decorator(method_reference):
def wrapper(*args, **kwargs):
try:
return method_reference(*args, **kwargs)
except Exception as e:
print("saved from " + str(e))
return wrapper
class Foo(QWidget):
def __init__(self):
super(Foo, self).__init__()
layout = QHBoxLayout()
sb1 = QSpinBox()
sb2 = QSpinBox()
sb3 = QSpinBox()
sb1.valueChanged.connect(partial(self.__pressed, sb1))
sb2.valueChanged.connect(partial(self.__pressed_two, sb2, "arg1"))
sb3.valueChanged.connect(partial(self.__pressed_three, sb3, "arg1", "arg2"))
layout.addWidget(sb1, 0)
layout.addWidget(sb2, 1)
layout.addWidget(sb3, 2)
self.setLayout(layout)
@my_decorator
def __pressed(self, spin_box):
print("spin box now has a value of " + str(spin_box.value))
@my_decorator
def __pressed_two(self, spin_box, arg1):
print("spin box now has a value of " + str(spin_box.value))
print("this is arg 1 : " + arg1)
@my_decorator
def __pressed_three(self, spin_box, arg1, arg2):
print("spin box now has a value of " + str(spin_box.value))
print("this is arg 1 : " + arg1)
print("this is arg 2 : " + arg2)
sys.excepthook = except_hook # by default QT will hide all errors behind a code, this is used to expose the issue
app = QApplication(sys.argv)
foo = Foo()
foo.show()
sys.exit(app.exec_())
Could it be possible to point out why my solution is not working? I did try to find examples and explanations and am successful in making decorators that would work for methods that are not used as pyqt slots but I cannot work this one out.
Upvotes: 0
Views: 380
Reputation: 7509
The issue doesn't seem to be with your decorator, but rather with connecting a method to the valueChanged
signal. Since this signal emits data (the new value being set), you need to add an additional argument to the __pressed
method that represent this data.
This is a simplified version of your code - you can see that the exception is properly handled by the decorator when the value in the spinbox gets too high.
import sys
from PyQt5.QtWidgets import QSpinBox, QWidget, QHBoxLayout, QApplication
def my_decorator(method_reference):
def wrapper(*args, **kwargs):
try:
return method_reference(*args, **kwargs)
except Exception as e:
print("saved from " + str(e))
return wrapper
class Foo(QWidget):
def __init__(self):
super(Foo, self).__init__()
layout = QHBoxLayout()
self.setLayout(layout)
self.spinbox = QSpinBox()
self.spinbox.valueChanged.connect(self.pressed)
layout.addWidget(self.spinbox)
@my_decorator
def pressed(self, new_value):
print(f"spin box now has a value of {new_value}")
if new_value > 5:
raise ValueError(f"Value {new_value} is too high!")
if __name__ == '__main__':
app = QApplication(sys.argv)
foo = Foo()
foo.show()
sys.exit(app.exec_())
Now, in my experience with PyQt, you don't always need to add the extra argument representing the data being sent by the signal. But in this case I'm assuming it is (somehow) mandatory due to the wrapping of the slot method. People more experienced with the library are free to correct me :-)
Upvotes: 2