Reputation: 31
I'm trying to repeatedly call a decorated function from within another class, such that the decorator is executed everytime the function is called.
The original question is below but does not explicitly relate to pyqt as pointed out correctly.
I'm trying to use decorators within a pyqt thread. From how I understand decorators, the decoration should be executed every time the function is called. (Or at least that is what I want.) However, calling a decorated function from within a pyqt thread leads to execution of the decorator only once.
This is my tested example:
import time, sys
import numpy as np
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class Decor:
def deco(self, *args, **kwargs):
print(kwargs['txt'])
print("In decorator")
def inner(func):
return func
return inner
dec = Decor()
class Window(QWidget):
def __init__(self, parent = None):
QWidget.__init__(self, parent)
self.thread = Worker()
label = QLabel(self.tr("random number"))
self.thread.output[str].connect(label.setText)
layout = QGridLayout()
layout.addWidget(label, 0, 0)
self.setLayout(layout)
self.thread.start()
class Worker(QThread):
output = pyqtSignal(str)
def run(self):
# Note: This is never called directly. It is called by Qt once the
# thread environment has been set up.
while True:
time.sleep(1)
number = self.random()
self.output.emit('random number {}'.format(number))
@dec.deco(txt='kw_argument')
def random(self):
return np.random.rand(1)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
I expected to get the prints of 'kw_argument' and 'in decorator' as often as self.normal()
is called, but get it only once. What am I doing wrong?
Upvotes: 2
Views: 639
Reputation: 18106
You could use function decorator instead:
import time, sys
from functools import wraps
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
def dec2(*args, **kwargs):
def real_decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
print(args, kwargs)
print("In decorator")
return fn(*args, **kwargs)
return wrapper
return real_decorator
class Window(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.thread = Worker()
label = QLabel(self.tr("random number"))
self.thread.output[str].connect(label.setText)
layout = QGridLayout()
layout.addWidget(label, 0, 0)
self.setLayout(layout)
self.thread.start()
class Worker(QThread):
output = pyqtSignal(str)
def run(self):
# Note: This is never called directly. It is called by Qt once the
# thread environment has been set up.
while True:
time.sleep(1)
number = self.random()
self.output.emit('random number {}'.format(number))
@dec2(txt='kw_argument')
def random(self):
return np.random.rand(1)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
Out:
(<__main__.Worker object at 0x10edc37d0>,) {}
In decorator
(<__main__.Worker object at 0x10edc37d0>,) {}
In decorator
(<__main__.Worker object at 0x10edc37d0>,) {}
In decorator
(<__main__.Worker object at 0x10edc37d0>,) {}
In decorator
(<__main__.Worker object at 0x10edc37d0>,) {}
In decorator
...
If you really need to print txt always, stick with a class decorator:
class dec2(object):
def __init__(self, *args, **kwargs):
self.deco_args = args
self.deco_kwargs = kwargs
def __call__(self, f):
def wrapped_f(*args):
print(self.deco_kwargs['txt'])
print('in decorator')
return f(*args)
return wrapped_f
Out:
w_argument
in decorator
kw_argument
in decorator
...
Upvotes: 1