gthread.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. """
  2. @package core.gthread
  3. @brief Threading
  4. Classes:
  5. - gthread::gThread
  6. (C) 2013-2014 by the GRASS Development Team
  7. This program is free software under the GNU General Public License
  8. (>=v2). Read the file COPYING that comes with GRASS for details.
  9. @author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
  10. """
  11. import threading
  12. import time
  13. import wx
  14. from wx.lib.newevent import NewEvent
  15. import sys
  16. if sys.version_info.major == 2:
  17. import Queue
  18. else:
  19. import queue as Queue
  20. from core.gconsole import EVT_CMD_DONE, wxCmdDone
  21. wxThdTerminate, EVT_THD_TERMINATE = NewEvent()
  22. class gThread(threading.Thread, wx.EvtHandler):
  23. """Thread for various backends
  24. terminating thread:
  25. https://www.geeksforgeeks.org/python-different-ways-to-kill-a-thread/
  26. """
  27. requestId = 0
  28. def __init__(self, requestQ=None, resultQ=None, **kwds):
  29. wx.EvtHandler.__init__(self)
  30. self.terminate = False
  31. self._terminate_evt = None
  32. threading.Thread.__init__(self, **kwds)
  33. if requestQ is None:
  34. self.requestQ = Queue.Queue()
  35. else:
  36. self.requestQ = requestQ
  37. if resultQ is None:
  38. self.resultQ = Queue.Queue()
  39. else:
  40. self.resultQ = resultQ
  41. self.setDaemon(True)
  42. self.Bind(EVT_CMD_DONE, self.OnDone)
  43. self.Bind(EVT_THD_TERMINATE, self.OnTerminate)
  44. self.start()
  45. def Run(self, *args, **kwds):
  46. """Run command in queue
  47. :param args: unnamed command arguments
  48. :param kwds: named command arguments, keyword 'callable'
  49. represents function to be run, keyword 'ondone'
  50. represents function to be called after the
  51. callable is done
  52. :return: request id in queue
  53. """
  54. gThread.requestId += 1
  55. self.requestQ.put((gThread.requestId, args, kwds))
  56. return gThread.requestId
  57. def GetId(self):
  58. """Get id for next command"""
  59. return gThread.requestId + 1
  60. def SetId(self, id):
  61. """Set starting id"""
  62. gThread.requestId = id
  63. def run(self):
  64. while True:
  65. requestId, args, kwds = self.requestQ.get()
  66. for key in ("callable", "ondone", "userdata", "onterminate"):
  67. if key in kwds:
  68. vars()[key] = kwds[key]
  69. del kwds[key]
  70. else:
  71. vars()[key] = None
  72. requestTime = time.time()
  73. ret = None
  74. exception = None
  75. time.sleep(0.01)
  76. self._terminate_evt = wxThdTerminate(
  77. onterminate=vars()["onterminate"],
  78. kwds=kwds,
  79. args=args,
  80. pid=requestId,
  81. )
  82. if self.terminate:
  83. return
  84. ret = vars()["callable"](*args, **kwds)
  85. if self.terminate:
  86. return
  87. # except Exception as e:
  88. # exception = e;
  89. self.resultQ.put((requestId, ret))
  90. event = wxCmdDone(
  91. ondone=vars()["ondone"],
  92. kwds=kwds,
  93. args=args, # TODO expand args to kwds
  94. ret=ret,
  95. exception=exception,
  96. userdata=vars()["userdata"],
  97. pid=requestId,
  98. )
  99. # send event
  100. wx.PostEvent(self, event)
  101. def OnDone(self, event):
  102. if event.ondone:
  103. event.ondone(event)
  104. def Terminate(self, terminate=True):
  105. """Abort command(s)"""
  106. self.terminate = terminate
  107. def start(self):
  108. self.__run_backup = self.run
  109. self.run = self.__run
  110. threading.Thread.start(self)
  111. def __run(self):
  112. sys.settrace(self.globaltrace)
  113. self.__run_backup()
  114. self.run = self.__run_backup
  115. def globaltrace(self, frame, event, arg):
  116. if event == "call":
  117. return self.localtrace
  118. else:
  119. return None
  120. def localtrace(self, frame, event, arg):
  121. if self.terminate:
  122. if event == "line":
  123. # Send event
  124. wx.PostEvent(self, self._terminate_evt)
  125. raise SystemExit()
  126. return self.localtrace
  127. def OnTerminate(self, event):
  128. if event.onterminate:
  129. event.onterminate(event)