diff --git a/woman/qt_util.py b/woman/qt_util.py
index 895eff932e8ca009e6ec875103404c1794468600..6a4440d04b85509f6093d66bd792c8938744eb36 100644
--- a/woman/qt_util.py
+++ b/woman/qt_util.py
@@ -4,6 +4,8 @@ from PyQt5.QtWidgets import *
 
 from dataclasses import dataclass
 import re
+import types
+import traceback
 
 
 qtoverride = lambda x: x  # only documentation of code
@@ -356,3 +358,38 @@ class QLabelOpeningBracket(QLabel):
             painter.drawLine(line_x, padding, line_x, y//2-text_h//2)
             painter.drawLine(line_x, y-1-padding, line_x, y//2+text_h//2)
             painter.drawLine(line_x, y-1-padding, x-2, y-1-padding)
+
+_qt_jobs = set()
+
+class QtJob:
+    def __init__(self, f):
+        _qt_jobs.add(self)
+        self.f = f
+        self.timer = QTimer()
+        self.timer.setSingleShot(True)
+        self.timer.timeout.connect(self._run)
+        self.timer.start(0)
+
+    def _clean(self):
+        _qt_jobs.remove(self)
+
+    def _run(self):
+        try:
+            r = self.f.send(None)
+            print(r)
+        except (StopIteration, GeneratorExit):
+            self._clean()
+        except Exception:
+            traceback.print_exc()
+            self._clean()
+        else:
+            self.timer.start(r)
+
+@types.coroutine
+def qt_job_wait_ms(time_ms):
+    yield time_ms
+
+def QtJobDecorator(f):
+    def l(*arg, **kvarg):
+        return QtJob(f(*arg, **kvarg))
+    return l