Changeset - dc1ed8ff56ef
[Not reviewed]
default
0 5 0
Laman - 6 years ago 2018-12-12 23:48:38

cleaned up and documented shutdown
5 files changed with 41 insertions and 46 deletions:
0 comments (0 inline, 0 general)
overview.md
Show inline comments
 
Processes and threads
 
=====================
 

	
 
Processes communicate through MsgQueues (a wrapper around multiprocessing.Queue). Each owns one incoming and holds any number of outcoming queues. The queue runs in one thread, reads messages and calls handlers on its owner.
 
Processes communicate through MsgQueues (a wrapper around multiprocessing.Queue).
 
Each owns one incoming and holds any number of outcoming queues.
 
The queue runs in one thread, reads messages and calls handlers on its owner.
 

	
 
A shutdown is initiated by closing the GUI.
 
It closes its MsgQueue and exits its mainloop.
 
End of GUI process is waited on by the Core.
 
It follows by sending VideoCapture process an "shutdown" order and then waits on its end.
 
The VideoCapture closes its MsgQueue and sets its main loop for a break with its _shutdown attribute.
 
Finally the Core closes its MsgQueue and exits. 
 

	
 
Core
 
----
src/core.py
Show inline comments
 
@@ -24,9 +24,10 @@ class Core:
 
		self._cache=FrameCache()
 
		self.states=StateBag()
 

	
 
		self._ownMessages=MsgQueue(self._handleEvent)
 
		self._guiMessages=MsgQueue()
 
		self._vidMessages=MsgQueue()
 
		self._ownMessages=MsgQueue("Core",self._handleEvent)
 
		self._guiMessages=MsgQueue("GUI")
 
		self._vidMessages=MsgQueue("Video")
 
		self._listenerThread=None
 

	
 
		self._frame=None
 

	
 
@@ -73,14 +74,16 @@ class Core:
 
				log.info("illegal position detected")
 

	
 
	def listen(self):
 
		listenerThread=threading.Thread(target=lambda: self._ownMessages.listen())
 
		listenerThread.start()
 
		self._listenerThread=threading.Thread(target=lambda: self._ownMessages.listen())
 
		self._listenerThread.start()
 

	
 
	def joinChildren(self):
 
		self._guiProc.join()
 
		self._vidMessages.send("shutDown")
 
		self._vidProc.join()
 
		self._ownMessages.send("!kill",("core",))
 
		self._listenerThread.join()
 
		log.info("Core exiting.")
 

	
 
	def _handleEvent(self,e):
 
		actions={
 
@@ -98,31 +101,4 @@ if __name__=="__main__":
 
	core.listen()
 
	log.info("OneEye started.")
 
	core.joinChildren()
 
	log.info("Exited correctly.")
 

	
 
"""
 
core
 
====
 
grid
 
go
 
imageAnalyzer
 

	
 

	
 
gui
 
===
 
corners
 

	
 
a) keeps references to important objects and uses them
 
b) gets and sets all relevant data through method calls with core
 

	
 
GUI
 
<- addCorner(corner)
 
-> redrawImgView(img,grid)
 
<- refreshTresholds(tresB,tresW)
 

	
 
BoardView
 
-> redrawState(go)
 

	
 

	
 
core-gui: just pass messages with relevant data (!! always pass object copies, don't share instances)
 
"""
 
\ No newline at end of file
 
	log.info("OneEye done.")
src/gui/__init__.py
Show inline comments
 
@@ -37,7 +37,7 @@ class GUI:
 
		self.root.bind("<<setUp>>", lambda e: self.setUp())
 
		self.root.bind("<<setRecording>>", lambda e: self.setRecording())
 
		self.root.bind("<F12>",lambda e: Settings(self))
 
		self.mainWindow.bind("<Destroy>",lambda e: self._ownMessages.send("!kill",("gui",)))
 
		self.mainWindow.bind("<Destroy>",lambda e: self._shutDown())
 

	
 
		self.setUp()
 

	
 
@@ -45,8 +45,8 @@ class GUI:
 
		self._ownMessages=ownMessages
 
		self._coreMessages=coreMessages
 

	
 
		self.listenerThread=threading.Thread(target=lambda: ownMessages.listen(self._handleEvent))
 
		self.listenerThread.start()
 
		self._listenerThread=threading.Thread(target=lambda: ownMessages.listen(self._handleEvent))
 
		self._listenerThread.start()
 

	
 
		self.mainWindow.mainloop()
 

	
 
@@ -70,6 +70,11 @@ class GUI:
 
	def sendParams(self):
 
		self.sendMsg("setParams",(self.detector.params.copy(),))
 

	
 
	def _shutDown(self):
 
		log.info("GUI proc exiting.")
 
		self._ownMessages.send("!kill",("gui",))
 
		self._listenerThread.join()
 

	
 
	def _handleEvent(self,e):
 
		actions={
 
			"setFrame": self._frameHandler,
src/util.py
Show inline comments
 
@@ -12,7 +12,8 @@ colorNames={BLACK:"B",WHITE:"W"}
 

	
 

	
 
class MsgQueue:
 
	def __init__(self,handler=None):
 
	def __init__(self,name,handler=None):
 
		self.name=name
 
		self._queue=multiprocessing.Queue()
 
		self._event=multiprocessing.Event()
 
		self._handleEvent=handler
 
@@ -30,11 +31,10 @@ class MsgQueue:
 
			msg=self._queue.get()
 
			if self._queue.empty():
 
				self._event.clear()
 
			log.info(msg)
 
			if msg[0]=="!kill":
 
				self._queue.cancel_join_thread()
 
				break
 
			log.info(msg if msg[0]!="putFrame" else "('putFrame', ..., {})")
 
			if msg[0]=="!kill": break
 
			self._handleEvent(msg)
 
		log.info("%s MsgQueue exiting.",self.name)
 

	
 
	def setHandler(self,handler):
 
		self._handleEvent=handler
src/video.py
Show inline comments
 
import time
 
import threading
 
import logging
 

	
 
import cv2 as cv
 

	
 
log=logging.getLogger(__name__)
 

	
 

	
 
class VideoCapture:
 
	def __init__(self, video_source=0):
 
@@ -27,7 +30,8 @@ class VideoCapture:
 
		else:
 
			return (False,None)
 

	
 
	def shutDown(self):
 
	def _shutDown(self):
 
		log.info("Video proc exiting.")
 
		self._ownMessages.send("!kill",("video",))
 
		self._shutdown=True
 

	
 
@@ -35,8 +39,8 @@ class VideoCapture:
 
		self._ownMessages=ownMessages
 
		self._coreMessages=coreMessages
 

	
 
		self.listenerThread=threading.Thread(target=lambda: ownMessages.listen(self._handleEvent))
 
		self.listenerThread.start()
 
		self._listenerThread=threading.Thread(target=lambda: ownMessages.listen(self._handleEvent))
 
		self._listenerThread.start()
 

	
 
		t=0
 
		while not self._shutdown:
 
@@ -45,6 +49,7 @@ class VideoCapture:
 
			if res: self._coreMessages.send("putFrame",(frame,))
 
			time.sleep(1)
 
			t+=1
 
		self._listenerThread.join()
 

	
 
	def __del__(self):
 
		if self._vid.isOpened():
 
@@ -52,7 +57,7 @@ class VideoCapture:
 
			
 
	def _handleEvent(self,e):
 
		actions={
 
			"shutDown": self.shutDown
 
			"shutDown": self._shutDown
 
		}
 
		(actionName,args,kwargs)=e
 

	
0 comments (0 inline, 0 general)