import threading import tkinter as tk from PIL import ImageTk import PIL import config from corners import Corners import image_analyzer from go import Go class MainWindow(tk.Frame): def __init__(self,parent,master=None): self.parent=parent self.corners=Corners() self.currentFrame=None self.boardGrid=None self.gameState=None self.img=None tk.Frame.__init__(self, master) self.grid(column=0,row=0) self._createWidgets() def setCurrentFrame(self,frame,grid): self.currentFrame=frame self.boardGrid=grid w=int(self.imgView['width']) h=int(self.imgView['height']) self.img=ImageTk.PhotoImage(frame.resize((w,h),resample=PIL.Image.BILINEAR)) def setGameState(self,gameState): pass def setCallbacks(self,setCorners,setTresholds): self.cornersCallback=setCorners self.tresholdsCallback=setTresholds def _createWidgets(self): # a captured frame with overlay graphics self.imgView=tk.Canvas(self) self.imgView.configure(width=480,height=360) self.imgView.bind('<1>',lambda e: self.addCorner(e.x,e.y)) self.imgView.grid(column=0,row=0) # board with detected stones self.boardView=BoardView(self) self.boardView.grid(column=1,row=0) # more controls below the board self.scaleTresB=tk.Scale(self, orient=tk.HORIZONTAL, length=200, from_=0.0, to=100.0, command=self.refreshTresholds) self.scaleTresW=tk.Scale(self, 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) self.scaleTresB.grid(column=0,row=1,columnspan=2) self.scaleTresW.grid(column=0,row=2,columnspan=2) # render everything self.redrawImgView() ## Stores a grid corner located at x,y coordinates. def addCorner(self,x,y): self.corners.add(x,y) if self.corners.canonizeOrder(): self.parent.sendMsg("setCorners",(self.corners,)) # self.boardGrid=Grid(self.corners) # self.boardView.setBoardGrid(self.boardGrid) # self.redrawImgView() ## Redraws the current image and its overlay. def redrawImgView(self): if self.currentFrame and self.img: self.imgView.create_image(2,2,anchor="nw",image=self.img) origWidth,origHeight=self.currentFrame.size widthRatio=origWidth/self.img.width() heightRatio=origHeight/self.img.height() sizeCoef=max(widthRatio,heightRatio) # shift=EPoint(origWidth-self.img.width()*sizeCoef,origHeight-self.img.height()*sizeCoef)/2 for corner in self.corners.corners: self.markPoint(corner.x,corner.y) if self.boardGrid!=None and config.gui.showGrid: for r in range(19): a=self.boardGrid.intersections[r][0] b=self.boardGrid.intersections[r][-1] self.imgView.create_line(a.x,a.y,b.x,b.y,fill='#00ff00') for c in range(19): a=self.boardGrid.intersections[0][c] b=self.boardGrid.intersections[-1][c] self.imgView.create_line(a.x,a.y,b.x,b.y,fill='#00ff00') if self.boardGrid!=None and config.gui.showBigPoints: for r in range(19): for c in range(19): ((r1,c1),(r2,c2))=image_analyzer.relevantRect(self.boardGrid.intersections[r][c],*(self.boardGrid.stoneSizeAt(r,c,sizeCoef))) self.imgView.create_rectangle(r1,c1,r2,c2,outline="#00ffff") self.imgView.grid() ## Marks a point at the image with a green cross. Used for corners. def markPoint(self,x,y): self.imgView.create_line(x-3,y-3,x+4,y+4,fill="#00ff00") self.imgView.create_line(x-3,y+3,x+4,y-4,fill="#00ff00") self.imgView.grid() def refreshTresholds(self,_): self.parent.sendMsg("setTresholds",tuple(),{"tresB":self.scaleTresB.get(), "tresW":self.scaleTresW.get()}) ## Handles and presents the game state as detected by the program. class BoardView(tk.Canvas): def __init__(self, master=None): # self.detector=ImageAnalyzer() self.boardGrid=None tk.Canvas.__init__(self, master) self.configure(width=360,height=360,background="#ffcc00") self.drawGrid() self.grid() def redrawState(self,gameState): for r,row in enumerate(gameState): for c,point in enumerate(row): self.drawStone(r,c,point) self.grid() ## Redraws and reananalyzes the board view. # def redrawState(self,img,sizeCoef,shift): # self.create_rectangle(0,0,360,360,fill="#ffcc00") # self.drawGrid() # # self.detector.analyze(img,sizeCoef,shift) # # for r,row in enumerate(self.detector.board): # for c,point in enumerate(row): # self.drawStone(r,c,point) # # self.grid() def drawGrid(self): for i in range(19): self.create_line(18,18*(i+1),360-18,18*(i+1),fill="#000000") # rows self.create_line(18*(i+1),18,18*(i+1),360-18,fill="#000000") # cols self.drawStars() def drawStars(self): for r in range(4,19,6): for c in range(4,19,6): self.create_oval(r*18-2,c*18-2,r*18+2,c*18+2,fill='#000000') ## Draws a stone at provided coordinates. # # For an unknown color draws nothing and returns False. # # @param r row coordinate, [0-18], counted from top # @param c column coordinate, [0-18], counted from left # @param color color indicator, Go.BLACK or Go.WHITE def drawStone(self,r,c,color): if color==Go.BLACK: hexCode='#000000' elif color==Go.WHITE: hexCode='#ffffff' else: return False r+=1 c+=1 self.create_oval(c*18-9,r*18-9,c*18+9,r*18+9,fill=hexCode) class GUI: def __init__(self): self.root = tk.Tk() self.root.title("OneEye {0}.{1}.{2}".format(*config.misc.version)) self.outcomingQueue=None self.outcomingEvent=None self.mainWindow = MainWindow(self,master=self.root) self.root.bind("<>", lambda e: self.mainWindow.redrawImgView()) self.root.bind("<>", lambda e: print("fired receiveState")) def __call__(self,incomingQueue,incomingEvent,outcomingQueue,outcomingEvent): self.outcomingQueue=outcomingQueue self.outcomingEvent=outcomingEvent self.listenerThread=threading.Thread(target=lambda: self._listen(incomingQueue,incomingEvent)) self.listenerThread.start() self.mainWindow.mainloop() def sendMsg(self,actionName,args=tuple(),kwargs=dict()): self.outcomingQueue.put((actionName,args,kwargs)) self.outcomingEvent.set() def _listen(self,incomingQueue,incomingEvent): while True: incomingEvent.wait() msg=incomingQueue.get() if incomingQueue.empty(): incomingEvent.clear() print(msg) self._handleEvent(msg) def _handleEvent(self,e): actions={"setCurrentFrame": self._frameHandler} (actionName,args,kwargs)=e return actions[actionName](*args,**kwargs) def _frameHandler(self,newFrame,grid): self.mainWindow.setCurrentFrame(newFrame,grid) self.root.event_generate("<>") def _stateHandler(self,e): pass gui=GUI()