Changeset - 18d23ce9a53f
[Not reviewed]
default
0 3 0
Laman - 6 years ago 2018-12-08 16:30:50

settings:
setting and getting parameters
entering setup state
closing on setRecording
3 files changed with 62 insertions and 8 deletions:
0 comments (0 inline, 0 general)
src/core.py
Show inline comments
 
import os
 
import multiprocessing
 
import threading
 
import logging
 
import PIL
 
from util import MsgQueue
 
from gui import gui
 
from analyzer import ImageAnalyzer
 
from analyzer.framecache import FrameCache
 
from go.core import Go, isLegalPosition
 
from statebag import StateBag
 
import config as cfg
 

	
 
log=logging.getLogger(__name__)
 

	
 

	
 
class Core:
 
	def __init__(self):
 
		self.grid=None
 
		self.go=Go()
 
		self.detector=ImageAnalyzer()
 
		self._cache=FrameCache()
 
		self.states=StateBag()
 

	
 
		self._ownMessages=MsgQueue(self._handleEvent)
 
		self._guiMessages=MsgQueue()
 

	
 
		self._imgs=sorted(os.listdir(cfg.misc.imgDir))
 
		self._imgIndex=cfg.misc.defaultImage
 
		imgPath=os.path.join(cfg.misc.imgDir,self._imgs[self._imgIndex])
 
		self._frame=PIL.Image.open(imgPath)
 

	
 
		self._guiProc=multiprocessing.Process(name="gui", target=gui, args=(self._guiMessages,self._ownMessages))
 
		self._guiProc.start()
 
		self.relativeFrame(0)
 

	
 
	def setCorners(self,corners):
 
		self.detector.setGridCorners(corners)
 
		self.analyze()
 

	
 
	def setTresholds(self,tresB=None,tresW=None):
 
		if tresB is not None: self.detector.tresB=tresB
 
		if tresW is not None: self.detector.tresW=tresW
 
		self.analyze()
 
		self.preview()
 

	
 
	def relativeFrame(self,step):
 
		self._imgIndex=(self._imgIndex+step)%len(self._imgs)
 
		imgPath=os.path.join(cfg.misc.imgDir,self._imgs[self._imgIndex])
 
		self._frame=PIL.Image.open(imgPath)
 
		self._guiMessages.send("setCurrentFrame",(self._frame.copy(),gui.PREVIEW))
 
		self.preview()
 

	
 
	def sendParams(self):
 
		params={
 
			"tresB": self.detector.tresB,
 
			"tresW": self.detector.tresW
 
		}
 
		self._guiMessages.send("setParams",tuple(),params)
 

	
 
	def setParams(self,tresB=0,tresW=0):
 
		self.detector.tresB=tresB
 
		self.detector.tresW=tresW
 

	
 
	def preview(self):
 
		if self.detector.analyze(self._frame):
 
			self._guiMessages.send("setGameState", (self.detector.board,[]))
 

	
 
	def analyze(self):
 
		if self.detector.analyze(self._frame):
 
			self._cache.put(self._frame)
 
			if isLegalPosition(self.detector.board):
 
				state=self.states.pushState(self.detector.board)
 
				rec=[]
 
				if state:
 
					rec=state.exportRecord()
 
					log.debug("progressive game record: %s",rec)
 
				self._guiMessages.send("setGameState", (self.detector.board,rec))
 

	
 
				self.go.transitionMove(self.detector.board)
 
				log.debug("conservative game record: %s",self.go._record)
 
			else:
 
				log.info("illegal position detected")
 

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

	
 
	def joinGui(self):
 
		self._guiProc.join()
 
		self._ownMessages.send("!kill",("core",))
 

	
 
	def _handleEvent(self,e):
 
		actions={
 
			"setCorners": self.setCorners,
 
			"setTresholds": self.setTresholds,
 
			"prevFrame": lambda: self.relativeFrame(-1),
 
			"nextFrame": lambda: self.relativeFrame(1)
 
			"nextFrame": lambda: self.relativeFrame(1),
 
			"fetchParams": self.sendParams,
 
			"setParams": self.setParams
 
		}
 
		(actionName,args,kwargs)=e
 

	
 
		return actions[actionName](*args,**kwargs)
 

	
 
core=Core()
 
core.listen()
 
core.joinGui()
 

	
 
"""
 
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
src/gui/__init__.py
Show inline comments
 
import threading
 
import logging
 
import threading
 
import tkinter as tk
 

	
 
import config
 
from .mainwindow import MainWindow
 
from .boardview import BoardView
 
from .settings import Settings
 

	
 
log=logging.getLogger(__name__)
 

	
 

	
 
class GUI:
 
	SETUP=PREVIEW=0
 
	RECORDING=REAL=1
 

	
 
	def __init__(self):
 
		self.root = tk.Tk()
 
		self.root.title("OneEye {0}.{1}.{2}".format(*config.misc.version))
 
		self.root.option_add('*tearOff',False) # for menu
 

	
 
		self._ownMessages=None
 
		self._coreMessages=None
 

	
 
		self._state=GUI.SETUP
 

	
 
		self.mainWindow = MainWindow(self, master=self.root)
 
		self.settings=None
 
		self.root.columnconfigure(0,weight=1)
 
		self.root.rowconfigure(0,weight=1)
 

	
 
		self.root.bind("<<redrawImgView>>", lambda e: self.mainWindow.redrawImgView())
 
		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.setUp()
 

	
 
	def __call__(self,ownMessages,coreMessages):
 
		self._ownMessages=ownMessages
 
		self._coreMessages=coreMessages
 

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

	
 
		self.mainWindow.mainloop()
 

	
 
	def sendMsg(self,actionName,args=tuple(),kwargs=None):
 
		self._coreMessages.send(actionName,args,kwargs)
 

	
 
	def setUp(self):
 
		self.mainWindow.setUp()
 
		self.root.bind("<Left>",lambda e: self.sendMsg("prevFrame"))
 
		self.root.bind("<Right>",lambda e: self.sendMsg("nextFrame"))
 

	
 
	def setRecording(self):
 
		self.mainWindow.setRecording()
 
		self.root.bind("<Left>",lambda e: None)
 
		self.root.bind("<Right>",lambda e: None)
 
		if self.settings:
 
			self.settings.destroy()
 
			self.settings=None
 

	
 
	def _handleEvent(self,e):
 
		actions={"setCurrentFrame":self._frameHandler, "setGameState":self._stateHandler}
 
		actions={
 
			"setCurrentFrame": self._frameHandler,
 
			"setGameState": self._stateHandler,
 
			"setParams": self._paramsHandler
 
		}
 
		(actionName,args,kwargs)=e
 

	
 
		return actions[actionName](*args,**kwargs)
 

	
 
	def _frameHandler(self,newFrame,type):
 
		if self._state!=type: return
 
		self.mainWindow.setCurrentFrame(newFrame)
 
		self.root.event_generate("<<redrawImgView>>")
 

	
 
	def _stateHandler(self,gameState,moves):
 
		labels={(row,col):(i+1) for (i,(c,row,col)) in enumerate(moves)}
 
		self.mainWindow.boardView.redrawState(gameState,labels)
 

	
 
	def _paramsHandler(self,**params):
 
		if not self.settings:
 
			log.warning("received 'setParams' message while settings is '%s'",str(self.settings))
 
			return
 
		self.settings.setParams(**params)
 

	
 

	
 
gui=GUI()
 

	
 
"""
 
# setup #
 
* we can click around the ImgView
 
* we can walk through the frames back and forth
 
* BoardView is showing what the reading of ImgView _would_ be
 
* core is reading and analyzing frames, pushing results to StateBag, but not showing them
 

	
 
# recording #
 
* ImgView is showing the current picture, is not clickable
 
* BoardView is showing last detected position
 
* on switch to recording (if parameters have changed):
 
	* feed analyzer new parameters and start using them
 
	* in the background reanalyze cached frames with the new parameters and merge them into StateBag
 
"""
src/gui/settings.py
Show inline comments
 
import tkinter as tk
 
from tkinter import N,S,E,W
 
from tkinter import N,S,E,W,LEFT
 

	
 
from .util import MsgMixin
 

	
 

	
 
class Settings(tk.Toplevel,MsgMixin):
 
	def __init__(self,parent):
 
		self.parent=parent
 

	
 
		tk.Toplevel.__init__(self, parent.root)
 

	
 
		self.title("Settings | OneEye")
 

	
 
		self.columnconfigure(0,weight=1)
 
		self.content=tk.Frame(self)
 
		self.content.grid(column=0,row=0,sticky=(N,S,E,W))
 
		self.content.columnconfigure(0,weight=1)
 
		self._create()
 
		self.parent.settings=self
 
		self.parent.root.event_generate("<<setUp>>")
 
		self.parent.sendMsg("fetchParams")
 

	
 
	def _create(self):
 
		self.scaleTresB=tk.Scale(self.content, orient=tk.HORIZONTAL, length=200, from_=0.0, to=100.0, command=self.refreshTresholds)
 
		self.scaleTresW=tk.Scale(self.content, orient=tk.HORIZONTAL, length=200, from_=0.0, to=100.0, command=self.refreshTresholds)
 
		self.scaleTresB.set(30.0) # !! proper defaults
 
		self.scaleTresW.set(60.0)
 

	
 
		blackLabel=tk.Label(self.content,text="Black stone treshold (intensity)")
 
		whiteLabel=tk.Label(self.content,text="White stone treshold (intensity)")
 
		blackLabel.grid(column=0,row=0)
 
		self.scaleTresB.grid(column=0,row=1,sticky=(E,W))
 
		whiteLabel.grid(column=0,row=2)
 
		self.scaleTresW.grid(column=0,row=3,sticky=(E,W))
 

	
 
		self.buttonFrame=tk.Frame(self.content)
 
		self.buttonFrame.grid(column=0,row=4,sticky=(W,))
 
		self.confirmButton=tk.Button(self.buttonFrame,text="OK",command=self.sendParams)
 
		self.cancelButton=tk.Button(self.buttonFrame,text="Cancel",command=lambda: self.destroy())
 
		self.confirmButton.pack(side=LEFT)
 
		self.cancelButton.pack(side=LEFT)
 

	
 
	def refreshTresholds(self,_):
 
		self.parent.sendMsg("setTresholds",tuple(),{"tresB":self.scaleTresB.get(), "tresW":self.scaleTresW.get()})
 
		self.parent.sendMsg("setTresholds",tuple(),self._compileParams())
 

	
 
	def setParams(self,tresB=0,tresW=0):
 
		self.scaleTresB.set(tresB)
 
		self.scaleTresW.set(tresW)
 

	
 
	def sendParams(self):
 
		self.parent.sendMsg("setParams",tuple(),self._compileParams())
 
		self.destroy()
 

	
 
	def _compileParams(self):
 
		params={
 
			"tresB": self.scaleTresB.get(),
 
			"tresW": self.scaleTresW.get()
 
		}
 
		return params
0 comments (0 inline, 0 general)