Winand

multithread ui

Jul 31st, 2015
337
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.78 KB | None | 0 0
  1. __author__ = 'МакаровАС'
  2.  
  3. from PyQt4 import QtCore, QtGui, uic
  4. import sys, queue, pythoncom
  5.  
  6. DEBUG = True
  7. print_def = lambda *args: not DEBUG or print(*args, file=sys.__stdout__)
  8.  
  9. #QtUtils
  10. #https://bitbucket.org/philipstarkey/qtutils
  11.  
  12. class Caller(QtCore.QObject):
  13.     """An event handler which calls the function held within a CallEvent."""
  14.     def event(self, event):
  15.         event.accept()
  16.         exception = None
  17.         try:
  18.             result = event.fn(*event.args, **event.kwargs)
  19.         except Exception:
  20.             # Store for re-raising the exception in the calling thread:
  21.             exception = sys.exc_info()
  22.             result = None
  23.             if event._exceptions_in_main:
  24.                 # Or, if nobody is listening for this exception,
  25.                 # better raise it here so it doesn't pass
  26.                 # silently:
  27.                 raise
  28.         finally:
  29.             event._returnval.put([result,exception])
  30.         return True
  31. caller = Caller()
  32.  
  33. def inmain(fn, *args, **kwargs):
  34.     class CallEvent(QtCore.QEvent):
  35.         """An event containing a request for a function call."""
  36.         EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
  37.         def __init__(self, queue, exceptions_in_main, fn, *args, **kwargs):
  38.             QtCore.QEvent.__init__(self, self.EVENT_TYPE)
  39.             self.fn = fn
  40.             self.args = args
  41.             self.kwargs = kwargs
  42.             self._returnval = queue
  43.             # Whether to raise exceptions in the main thread or store them
  44.             # for raising in the calling thread:
  45.             self._exceptions_in_main = exceptions_in_main
  46.  
  47.     def in_main_later(fn, exceptions_in_main, *args, **kwargs):
  48.         """Asks the mainloop to call a function when it has time. Immediately
  49.        returns the queue that was sent to the mainloop.  A call to queue.get()
  50.        will return a list of [result,exception] where exception=[type,value,traceback]
  51.        of the exception.  Functions are guaranteed to be called in the order
  52.        they were requested."""
  53.         q = queue.Queue()
  54.         QtCore.QCoreApplication.postEvent(caller, CallEvent(q, exceptions_in_main, fn, *args, **kwargs))
  55.         return q
  56.  
  57.     def get_inmain_result(queue):
  58.         result,exception = queue.get()
  59.         if exception is not None:
  60.             type, value, traceback = exception
  61.             raise value.with_traceback(traceback)
  62.         return result
  63.     print_def("inmain", fn, isMainThread())
  64.     return fn(*args, **kwargs) if isMainThread() else get_inmain_result(in_main_later(fn,False,*args,**kwargs))
  65.  
  66. # dontproxy=0
  67. # def noproxy(func):
  68. #     def wrap_func1(self, *args, **kwargs):
  69. #         return func(self, *args, **kwargs)
  70. #     global dontproxy
  71. #     dontproxy = wrap_func1
  72. #     print(dontproxy)
  73. #     return wrap_func1
  74.  
  75. class prx():
  76.     "Proxies object, automatically calls methods in GUI thread"
  77.     GETATTR, CALL, SETATTR = range(3)
  78.     builtin = str, bool, int, type(None), complex, bytes#, dontproxy
  79.     def __init__(self, client, *args, atts={}, **kwargs):
  80.         self.__dict__['client'] = client
  81.         for k in atts:
  82.             self.__dict__[k] = atts[k]
  83.     def proxy(self, t, *args, **kwargs):
  84.         if t == self.SETATTR:
  85.             setattr(self.client, args[0], args[1])
  86.         else:
  87.             if t == self.GETATTR:
  88.                 print_def("get", self.client, self.client.__class__)
  89.                 ret = getattr(self.client, args[0])
  90.             else:
  91.                 print_def("call", self.client.__name__, self.client.__self__ if hasattr(self.client, "__self__") else None)
  92.                 ret = inmain(self.client, *args, **kwargs)
  93. #            print_def("get/call", type(ret), ret, self.client)# in self.client.__class__.__dict__.keys())
  94.             return ret if type(ret) in self.builtin else prx(ret) #if type(ret) != types.MethodType else ret
  95.     def __getattr__(self, name): return self.proxy(self.GETATTR, name)
  96.     def __call__(self, *args, **kwargs): return self.proxy(self.CALL, *args, **kwargs)
  97.     def __setattr__(self, name, value): self.proxy(self.SETATTR, name, value)
  98.     def __str__(self): return "<Proxied %s>" % self.client
  99.     def __eq__(self, other): return self.client is other.client
  100.  
  101. threads = [] #stores thread refs
  102. class GenericThread(QtCore.QThread):
  103.     def __init__(self, client_self, function, *args, **kwargs):
  104.         QtCore.QThread.__init__(self)
  105.         self.client_self = prx(client_self, \
  106.                             atts={"sender": lambda s=client_self.sender(): s})
  107.         self.function, self.args, self.kwargs = function, args, kwargs
  108.         self.finished.connect(self.finished_)
  109.         threads.append(self)
  110.         self.start()
  111.     def __del__(self):
  112.         if self.isRunning():
  113.             print_def("Thread %s is still running. Waiting..." % self)
  114.             self.wait() #block until finished
  115.     def finished_(self):
  116.         del threads[threads.index(self)]
  117.     def run(self):
  118.         pythoncom.CoInitialize()
  119.         self.function(self.client_self, *self.args, **self.kwargs)
  120.  
  121. def isMainThread():
  122.     if not QtCore.QCoreApplication.instance():
  123.         print_def("isMainThread: app instance is None!")
  124.         return True
  125.     return QtCore.QThread.currentThread() is QtCore.QCoreApplication.instance().thread()
  126.  
  127. def pyqtThreadedSlot(*args, **kwargs):
  128.     def threaded_int(func):
  129.         @QtCore.pyqtSlot(*args, name=func.__name__, **kwargs)
  130.         def wrap_func(self, *args1, **kwargs1):
  131.             GenericThread(self, func, *args1, **kwargs1)
  132.         return wrap_func
  133.     return threaded_int
  134.  
  135. def QtApp(Form, *args, flags=None, ui=None, stdout=None, **kwargs):
  136.     "Create new QApplication and specified window"
  137.     app = QtGui.QApplication(sys.argv)
  138.     def __init__(self, flags, ui, __init__def=Form.__init__):
  139.         super(Form, self).__init__(flags=flags or QtCore.Qt.WindowType())
  140.         uic.loadUi((ui or Form.__name__.lower())+".ui", self)
  141.         if stdout: redirect_stdout(getattr(self, stdout))
  142.         __init__def(self, *args, **kwargs)
  143.     Form.__init__ = __init__
  144.     form = Form(flags, ui)
  145.     form.show()
  146.     sys.exit(app.exec_())
  147.  
  148. def redirect_stdout(wgt):
  149.     """Redirect standard output to the specified widget"""
  150.     classes = wgt.metaObject().className(), wgt.metaObject().superClass().className()
  151.     if "QPlainTextEdit" in classes:
  152.         def write(txt):
  153.             wgt.moveCursor(QtGui.QTextCursor.End)
  154.             wgt.insertPlainText(txt)
  155.         wgt.write = write
  156.         wgt.flush = lambda: None
  157.     else:
  158.         print_def("Cannot redirect output to unsupported "+classes[0])
  159.         return
  160.     parent = wgt.parent()
  161.     def closeEvent(e, orig_ce=parent.closeEvent):
  162.         sys.stdout = sys.__stdout__
  163.         orig_ce(e)
  164.     parent.closeEvent = closeEvent
  165.     sys.stdout = prx(wgt)
Advertisement
Add Comment
Please, Sign In to add comment