gthread.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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(.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(ondone=vars()['ondone'],
  91. kwds=kwds,
  92. args=args, # TODO expand args to kwds
  93. ret=ret,
  94. exception=exception,
  95. userdata=vars()['userdata'],
  96. pid=requestId)
  97. # send event
  98. wx.PostEvent(self, event)
  99. def OnDone(self, event):
  100. if event.ondone:
  101. event.ondone(event)
  102. def Terminate(self, terminate=True):
  103. """Abort command(s)"""
  104. self.terminate = terminate
  105. def start(self):
  106. self.__run_backup = self.run
  107. self.run = self.__run
  108. threading.Thread.start(self)
  109. def __run(self):
  110. sys.settrace(self.globaltrace)
  111. self.__run_backup()
  112. self.run = self.__run_backup
  113. def globaltrace(self, frame, event, arg):
  114. if event == 'call':
  115. return self.localtrace
  116. else:
  117. return None
  118. def localtrace(self, frame, event, arg):
  119. if self.terminate:
  120. if event == 'line':
  121. # Send event
  122. wx.PostEvent(self, self._terminate_evt)
  123. raise SystemExit()
  124. return self.localtrace
  125. def OnTerminate(self, event):
  126. if event.onterminate:
  127. event.onterminate(event)