# HG changeset patch # User Laman # Date 2018-12-12 23:48:38 # Node ID dc1ed8ff56ef3d22f3928efdd5b894a1bbf45b76 # Parent 52d1a214c0323390202113f09cfc4eecfc5b60ad cleaned up and documented shutdown diff --git a/overview.md b/overview.md --- a/overview.md +++ b/overview.md @@ -1,7 +1,16 @@ 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 ---- diff --git a/src/core.py b/src/core.py --- a/src/core.py +++ b/src/core.py @@ -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.") diff --git a/src/gui/__init__.py b/src/gui/__init__.py --- a/src/gui/__init__.py +++ b/src/gui/__init__.py @@ -37,7 +37,7 @@ class GUI: self.root.bind("<>", lambda e: self.setUp()) self.root.bind("<>", lambda e: self.setRecording()) self.root.bind("",lambda e: Settings(self)) - self.mainWindow.bind("",lambda e: self._ownMessages.send("!kill",("gui",))) + self.mainWindow.bind("",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, diff --git a/src/util.py b/src/util.py --- a/src/util.py +++ b/src/util.py @@ -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 diff --git a/src/video.py b/src/video.py --- a/src/video.py +++ b/src/video.py @@ -1,8 +1,11 @@ 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