AnalogClock

```
from ast import Lambda
import math
from pickle import FALSE, TRUE
import sys
import threading
import serial.tools.list_ports
from serial import Serial
import argparse
import tkinter
import tkinter.messagebox as messagebox
from PIL import Image,ImageTk,ImageOps
import cv2
import time

BAUDRATES=[115200,38400,19200,9600]
# 時計のキャンバスのサイズの指定
CANVAS_WIDTH = 450
#時計の前面と背景の色の設定
BG_COLOR = "white"
FG_COLOR = "gray"
# 時計の数字の位置の設定
DISTANCE_NUMBER = CANVAS_WIDTH / 2 * 0.8

# 投球数とかのフォントサイズ
FONTSIZE = 30



class SerialPort:
	"""シリアル通信に関するクラス"""
	def __init__(self):
		self.var = ""
		self.menu = ""
	def getVar(self):
		return self.var

class Baudrate:
	"""ボーレートに関するクラス"""
	def __init__(self):
		self.var=""
		self.menu=""

class DrawerClock:
	"""時計を描画するためのクラス"""
	def __init__(self,master):
		#各種設定後に時計の盤面を描画する
		self.initSetting(master)
		self.createClock()
	def initSetting(self,master):
		"""時計の描画に必要な設定を行う"""
		# ウィジェットの作成先を設定
		self.master = master
		# 針のオブジェクトの格納器
		self.hands = []
		self.texts = []
		# 針の描画の初回判定
		self.first = 1
		# 針の色の指定
		self.color = "red"
		# 針の太さの指定
		self.width = 6
		# 針の長さの指定
		self.length = CANVAS_WIDTH/2 * 0.8
		#キャンバスの中心点座標を定義しておく
		self.center = [CANVAS_WIDTH/2, CANVAS_WIDTH/2]
	def createClock(self):
		"""時計の盤面を作成する"""
		# キャンバスを作成して配置する
		self.canvas = tkinter.Canvas(
			self.master,
			width = CANVAS_WIDTH,
			height = CANVAS_WIDTH,
			highlightthickness = 0,
		)
		self.canvas.place(x=1000,y=50)

		# 時計の盤面の円を描画する
		x1 = self.center[0]-CANVAS_WIDTH*0.95/2
		y1 = self.center[1]-CANVAS_WIDTH*0.95/2
		x2 = self.center[0]+CANVAS_WIDTH*0.95/2
		y2 = self.center[1]+CANVAS_WIDTH*0.95/2

		self.canvas.create_oval(
			x1,y1,x2,y2,
			fill = BG_COLOR,
			width = 4,
			outline = FG_COLOR)

		# 盤面上に数字を描画する
		for second in range(1,13):
			#角度を計算する
			angle = 360/12 * second + (-360/12-90)
			# 描画位置を計算
			x1 = self.center[0]
			y1 = self.center[1]
			dx = DISTANCE_NUMBER*math.cos(math.radians(angle))
			dy = DISTANCE_NUMBER*math.sin(math.radians(angle))
			x2 = x1+dx
			y2 = y1+dy
			self.canvas.create_text(
				x2, y2,
				font = ("",20),
				fill = FG_COLOR,
				text = str((13-second)*5))
			self.canvas.create_rectangle(
				-40+self.center[0],14+self.center[1],
				40+self.center[0],-14+self.center[1],
				fill = "white"
				)
			text = self.canvas.create_text(
				self.center[0],self.center[1],
				font = ("",20),
				text = "60.00",
				tag = "text")
			self.texts = text
	def drawHands(self,received_second):
		angle = -6*received_second-90
		x1 = self.center[0]
		y1 = self.center[1]
		x2 = x1 + self.length*math.cos(math.radians(angle))
		y2 = y1 + self.length*math.sin(math.radians(angle))
		if (self.first==1):
			hand = self.canvas.create_line(
				x1,y1,x2,y2,
				fill = self.color,
				width = self.width)
			self.canvas.create_rectangle(
				-40+self.center[0],14+self.center[1],
				40+self.center[0],-14+self.center[1],
				fill = "white"
				)
			self.canvas.delete("text")
			text = self.canvas.create_text(
				x1,y1,
				font = ("",20),
				text = str(received_second),
				tag = "text")
			self.first = 0
			self.hands = hand
			self.texts = text
		else:
			#coordsメソッドにより描画済みの座標を変更
			hand = self.canvas.coords(
				self.hands,
				x1,y1,x2,y2)
			self.canvas.delete("text")
			text = self.canvas.create_text(
				x1,y1,
				font = ("",20),
				text = str(received_second),
				tag = "text")
		

class DrawerFig:
	"""画像とテキストを描画するためのクラス"""
	def __init__(self,master,labelName,imageName,place,offset):
		#各種設定後に画像>テキストを描画する
		self.initSetting(master,offset)
		self.createFig(imageName,place)
		self.createText(labelName[0],labelName[1])
	def initSetting(self,master,offset):
		"""画像とテキストの描画に必要な設定を行う"""
		# ウィジェットの作成先を設定
		self.master = master
		# テキストのオブジェクトの格納器
		self.texts = []
		# テキストの描画の初回判定
		self.first = 1
		self.offset = offset#[5,0]
	def createFig(self,imageName,place):
		# 画像ファイルを開く
		self.photo_image = Image.open(imageName)
		self.scale = 500/self.photo_image.width
		self.numpixel = [self.photo_image.width,self.photo_image.height]
		self.photo_image = self.photo_image.resize((int(self.numpixel[0]*self.scale),
											  int(self.numpixel[1]*self.scale)))
		self.photo_image = ImageTk.PhotoImage(self.photo_image)
		#canvasの作成
		self.canvas = tkinter.Canvas(
			self.master,
			width = self.numpixel[0]*self.scale,
			height = self.numpixel[1]*self.scale,
			highlightthickness = 0)
		self.canvas.place(x=place[0],y=place[1],
					anchor=tkinter.CENTER)
		#　画像の描画
		self.canvas.create_image(
			self.numpixel[0]*self.scale/2,self.numpixel[1]*self.scale/2,
			image = self.photo_image)
	def createText(self,labelName,received_text):
		offsetValue = self.offset
		if(self.first==1):
			label = self.canvas.create_text(
				int(self.numpixel[0]*self.scale/2)+offsetValue[0],
				int(self.numpixel[1]*self.scale/2)+offsetValue[1]-FONTSIZE*0.75,
				font = ("",FONTSIZE),
				text = labelName)
			text = self.canvas.create_text(
				self.numpixel[0]*self.scale/2+offsetValue[0],
				self.numpixel[1]*self.scale/2+offsetValue[1]+FONTSIZE*0.75,
				font = ("",FONTSIZE),
				text =received_text,
				tag = "text")
			self.first = 0
			self.texts = text
		else:
			self.canvas.delete("text")
			text = self.canvas.create_text(
				self.numpixel[0]*self.scale/2+offsetValue[0],
				self.numpixel[1]*self.scale/2+offsetValue[1]+FONTSIZE*0.75,
				font = ("",FONTSIZE),
				text =received_text,
				tag = "text")

class DrawerVideo:
	"""mp4を再生するためのクラス"""
	def __init__(self,master):
		print("first")

		#各種設定後に最初のフレーム(画像)を描画する
		self.initSetting(master)
		self.createCanvas()
		self.video_thread = threading.Thread(target = self.video_frame_timer)
		self.video_thread.setDaemon(True)
		self.video_thread.start()
	def initSetting(self,master):
		"""動画の描画に必要な設定を行う"""
		#ウェジェットの作成先を設定
		self.master = master
		video = cv2.VideoCapture("goal.mp4")
		ret,firstFrame=video.read()
		self.size=[video.get(cv2.CAP_PROP_FRAME_WIDTH),video.get(cv2.CAP_PROP_FRAME_HEIGHT)]
		#print(video.get(cv2.CAP_PROP_FRAME_COUNT))
		self.scale = 500/self.size[0]
		self.center = [self.size[0]*self.scale/2,
				 self.size[1]*self.scale/2]
		self.firstImage = self.Frame2Image(firstFrame)
		#Frame開始位置の初期化
		self.initVideo = 1
		self.playing = 0
	def createCanvas(self):
		print("fuga")
		self.canvas = tkinter.Canvas(
			self.master,
			width = self.size[0]*self.scale,
			height = self.size[1]*self.scale,
			highlightthickness = 0)
		self.canvas.place(x=250,y=250,anchor=tkinter.CENTER)
		self.canvas.create_image(self.center[0],
						   self.center[1],
						   image =self.firstImage)
		self.canvas.create_text(
			self.center[0],self.center[1],
			font = ("",FONTSIZE),
			text = "入った数")
	def createVideo(self):
		if self.initVideo == 1:
			self.video = cv2.VideoCapture("goal.mp4")
			self.initVideo = 0
		ret, frame = self.video.read()
		if ret:
			img=self.Frame2Image(frame)
			for num in range(10):
				self.canvas.create_image(self.center[0],
								self.center[1],
								image = img)
		else:
			self.playing = 0
	def video_frame_timer(self):
		while TRUE:
			while self.playing==1:
				self.createVideo()
			time.sleep(0.001)
	def video_start(self):
		print("hoge")
		self.playing = 1
		self.initVideo =1
	def Frame2Image(self,frame):
		#BGR->RGB変換
		cv_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
		# NumpyのndarrayからPillowのImageへ変換
		pil_image = Image.fromarray(cv_image)
		#画像のリサイズ
		pil_image = ImageOps.pad(pil_image,(int(self.center[0]*2),int(self.center[1]*2)))
		photo_image = ImageTk.PhotoImage(image = pil_image)
		return photo_image


#シリアルポート情報の取得
def refresh_serial_ports(port):
	ports = serial.tools.list_ports.comports()
	port_names = [port.device for port in ports]
	port.var.set('')
	port.menu["menu"].delete(0, tkinter.END)
	for port_name in port_names:
		port.menu["menu"].add_command(
			label=port_name,
			command=tkinter._setit(port.var, port_name))
	return port

def toggle_serial_port(port):
	global ser
	global read_thread

	if ser.is_open:
		try:
			ser.close()
			baudrate.menu.config(state="normal")
			toggle_button.config(text="接続")
			messagebox.showinfo("情報", "シリアルポートが切断されました")
		except Exception as e:
			messagebox.showerror("切断エラー",str(e))
	else:
		port_value = port.var.get()
		if not port_value:
			messagebox.showerror("接続エラー", "シリアルポートが選択されていません")
			return
		baudrate_value = int(baudrate.var.get())
		try:
			ser = Serial(port_value,baudrate_value)
			baudrate.menu.config(state="disabled")
			messagebox.showinfo("情報", "シリアルポートに接続しました")
			# 受信処理の開始
			read_thread = threading.Thread(target = read_data,daemon=True)
			read_thread.start()
			toggle_button.config(text="切断")
		except Exception as e:
			messagebox.showerror("受信エラー", str(e))
		
def read_data():
	NUM_GOAL=0
	num_values=4
	while(1):
		if ser.is_open:
			#文字列の長さの予計算
			line_len = num_values + (num_values -1)
			line = ser.readline()
			line_string = line.strip().decode("utf-8")
			line_elements = line_string.split(",")
			if(len(line_elements) == num_values):
				read_values = [0]*num_values
				#update_string
				analogClock.drawHands(float(line_elements[3]))
				ballData.createText("",line_elements[2])
				humanData.createText("",line_elements[0])
				# Goal数の更新
				if int(float(line_elements[1])) - NUM_GOAL>5:
					print("start")
					NUM_GOAL=int(float(line_elements[1]))
					# 動画のスタート
					videoData.video_start()
			else:
				read_values = None

if __name__ == "__main__":
	root = tkinter.Tk()
	root.title("Timer")
	root.geometry("1500x1000")

	port = SerialPort

	#上部フレーム
	frame_top = tkinter.Frame(root)
	frame_top.pack(pady=5)

	#シリアルポート選択メニュー
	port.var = tkinter.StringVar(root)
	port.menu = tkinter.OptionMenu(frame_top,port.var,'')
	port.menu.pack(side=tkinter.LEFT, padx=5)
	
	#シリアルポートの更新
	port=refresh_serial_ports(port)

	#接続/切断ボタン
	ser = Serial()
	ser.close()
	toggle_button = tkinter.Button(
		frame_top,text="接続",
		command=lambda:toggle_serial_port(port))
	toggle_button.pack(side=tkinter.LEFT, padx=5)
	#ボーレート選択メニュー
	baudrate=Baudrate
	baudrate.var = tkinter.StringVar(root)
	baudrate.var.set(BAUDRATES[0])
	baudrate.menu = tkinter.OptionMenu(frame_top, baudrate.var, *BAUDRATES)
	baudrate.menu.pack(side=tkinter.LEFT, padx=5)
	
	
	ballData = DrawerFig(root,["成功率","00.00%"],"ball_sche.png",[750,400],[5,-45])
	humanData = DrawerFig(root,["投球数","00"],"human_sche.png",[1200,700],[-70,-80])
	#goalData = DrawerFig(root,["ゴール数","00"],"goal.png",[350,250],[-125,125])
	analogClock=DrawerClock(root)
	videoData = DrawerVideo(root)
	#videoData.createVideo()


	root.mainloop()
```

```
from ast import Lambda
import math
import sys
import threading
import serial.tools.list_ports
from serial import Serial
import argparse
import tkinter
import tkinter.messagebox as messagebox
from PIL import Image,ImageTk

BAUDRATES=[115200,38400,19200,9600]
# 時計のキャンバスのサイズの指定
CANVAS_WIDTH = 450
#時計の前面と背景の色の設定
BG_COLOR = "white"
FG_COLOR = "gray"
# 時計の数字の位置の設定
DISTANCE_NUMBER = CANVAS_WIDTH / 2 * 0.8

# 投球数とかのフォントサイズ
FONTSIZE = 30

class SerialPort:
	"""シリアル通信に関するクラス"""
	def __init__(self):
		self.var = ""
		self.menu = ""
	def getVar(self):
		return self.var

class Baudrate:
	"""ボーレートに関するクラス"""
	def __init__(self):
		self.var=""
		self.menu=""

class DrawerClock:
	"""時計を描画するためのクラス"""
	def __init__(self,master):
		#各種設定後に時計の盤面を描画する
		self.initSetting(master)
		self.createClock()
	def initSetting(self,master):
		"""時計の描画に必要な設定を行う"""
		# ウィジェットの作成先を設定
		self.master = master
		# 針のオブジェクトの格納器
		self.hands = []
		self.texts = []
		# 針の描画の初回判定
		self.first = 1
		# 針の色の指定
		self.color = "red"
		# 針の太さの指定
		self.width = 6
		# 針の長さの指定
		self.length = CANVAS_WIDTH/2 * 0.8
		#キャンバスの中心点座標を定義しておく
		self.center = [CANVAS_WIDTH/2, CANVAS_WIDTH/2]
	def createClock(self):
		"""時計の盤面を作成する"""
		# キャンバスを作成して配置する
		self.canvas = tkinter.Canvas(
			self.master,
			width = CANVAS_WIDTH,
			height = CANVAS_WIDTH,
			highlightthickness = 0,
		)
		self.canvas.place(x=1000,y=50)

		# 時計の盤面の円を描画する
		x1 = self.center[0]-CANVAS_WIDTH*0.95/2
		y1 = self.center[1]-CANVAS_WIDTH*0.95/2
		x2 = self.center[0]+CANVAS_WIDTH*0.95/2
		y2 = self.center[1]+CANVAS_WIDTH*0.95/2

		self.canvas.create_oval(
			x1,y1,x2,y2,
			fill = BG_COLOR,
			width = 4,
			outline = FG_COLOR)

		# 盤面上に数字を描画する
		for second in range(1,13):
			#角度を計算する
			angle = 360/12 * second + (-360/12-90)
			# 描画位置を計算
			x1 = self.center[0]
			y1 = self.center[1]
			dx = DISTANCE_NUMBER*math.cos(math.radians(angle))
			dy = DISTANCE_NUMBER*math.sin(math.radians(angle))
			x2 = x1+dx
			y2 = y1+dy
			self.canvas.create_text(
				x2, y2,
				font = ("",20),
				fill = FG_COLOR,
				text = str((13-second)*5))
			self.canvas.create_rectangle(
				-40+self.center[0],14+self.center[1],
				40+self.center[0],-14+self.center[1],
				fill = "white"
				)
			text = self.canvas.create_text(
				self.center[0],self.center[1],
				font = ("",20),
				text = "60.00",
				tag = "text")
			self.texts = text
	def drawHands(self,received_second):
		angle = -6*received_second-90
		x1 = self.center[0]
		y1 = self.center[1]
		x2 = x1 + self.length*math.cos(math.radians(angle))
		y2 = y1 + self.length*math.sin(math.radians(angle))
		if (self.first==1):
			hand = self.canvas.create_line(
				x1,y1,x2,y2,
				fill = self.color,
				width = self.width)
			self.canvas.create_rectangle(
				-40+self.center[0],14+self.center[1],
				40+self.center[0],-14+self.center[1],
				fill = "white"
				)
			self.canvas.delete("text")
			text = self.canvas.create_text(
				x1,y1,
				font = ("",20),
				text = str(received_second),
				tag = "text")
			self.first = 0
			self.hands = hand
			self.texts = text
		else:
			#coordsメソッドにより描画済みの座標を変更
			hand = self.canvas.coords(
				self.hands,
				x1,y1,x2,y2)
			self.canvas.delete("text")
			text = self.canvas.create_text(
				x1,y1,
				font = ("",20),
				text = str(received_second),
				tag = "text")
		

class DrawerFig:
	"""画像とテキストを描画するためのクラス"""
	def __init__(self,master,labelName,imageName,place,offset):
		#各種設定後に画像>テキストを描画する
		self.initSetting(master,offset)
		self.createFig(imageName,place)
		self.createText(labelName[0],labelName[1])
	def initSetting(self,master,offset):
		"""画像とテキストの描画に必要な設定を行う"""
		# ウィジェットの作成先を設定
		self.master = master
		# テキストのオブジェクトの格納器
		self.texts = []
		# テキストの描画の初回判定
		self.first = 1
		self.offset = offset#[5,0]
	def createFig(self,imageName,place):
		# 画像ファイルを開く
		self.photo_image = Image.open(imageName)
		self.scale = 500/self.photo_image.width
		self.numpixel = [self.photo_image.width,self.photo_image.height]
		print(self.scale)
		self.photo_image = self.photo_image.resize((int(self.numpixel[0]*self.scale),
											  int(self.numpixel[1]*self.scale)))
		self.photo_image = ImageTk.PhotoImage(self.photo_image)
		#canvasの作成
		self.canvas = tkinter.Canvas(
			self.master,
			width = self.numpixel[0]*self.scale,
			height = self.numpixel[1]*self.scale,
			highlightthickness = 0)
		self.canvas.place(x=place[0],y=place[1],
					anchor=tkinter.CENTER)
		#　画像の描画
		self.canvas.create_image(
			self.numpixel[0]*self.scale/2,self.numpixel[1]*self.scale/2,
			image = self.photo_image)
	def createText(self,labelName,received_text):
		offsetValue = self.offset
		if(self.first==1):
			label = self.canvas.create_text(
				int(self.numpixel[0]*self.scale/2)+offsetValue[0],
				int(self.numpixel[1]*self.scale/2)+offsetValue[1]-FONTSIZE*0.75,
				font = ("",FONTSIZE),
				text = labelName)
			text = self.canvas.create_text(
				self.numpixel[0]*self.scale/2+offsetValue[0],
				self.numpixel[1]*self.scale/2+offsetValue[1]+FONTSIZE*0.75,
				font = ("",FONTSIZE),
				text =received_text,
				tag = "text")
			self.first = 0
			self.texts = text
		else:
			self.canvas.delete("text")
			text = self.canvas.create_text(
				self.numpixel[0]*self.scale/2+offsetValue[0],
				self.numpixel[1]*self.scale/2+offsetValue[1]+FONTSIZE*0.75,
				font = ("",FONTSIZE),
				text =received_text,
				tag = "text")


#シリアルポート情報の取得
def refresh_serial_ports(port):
	ports = serial.tools.list_ports.comports()
	port_names = [port.device for port in ports]
	port.var.set('')
	port.menu["menu"].delete(0, tkinter.END)
	for port_name in port_names:
		port.menu["menu"].add_command(
			label=port_name,
			command=tkinter._setit(port.var, port_name))
	return port

def toggle_serial_port(port):
	global ser
	global read_thread

	if ser.is_open:
		try:
			ser.close()
			baudrate.menu.config(state="normal")
			toggle_button.config(text="接続")
			messagebox.showinfo("情報", "シリアルポートが切断されました")
		except Exception as e:
			messagebox.showerror("切断エラー",str(e))
	else:
		port_value = port.var.get()
		if not port_value:
			messagebox.showerror("接続エラー", "シリアルポートが選択されていません")
			return
		baudrate_value = int(baudrate.var.get())
		try:
			ser = Serial(port_value,baudrate_value)
			baudrate.menu.config(state="disabled")
			messagebox.showinfo("情報", "シリアルポートに接続しました")
			# 受信処理の開始
			read_thread = threading.Thread(target = read_data,daemon=True)
			read_thread.start()
			toggle_button.config(text="切断")
		except Exception as e:
			messagebox.showerror("受信エラー", str(e))
		
def read_data():
	num_values=4
	while(1):
		if ser.is_open:
			#文字列の長さの予計算
			line_len = num_values + (num_values -1)
			line = ser.readline()
			line_string = line.strip().decode("utf-8")
			line_elements = line_string.split(",")
			if(len(line_elements) == num_values):
				read_values = [0]*num_values
				#update_string
				analogClock.drawHands(float(line_elements[3]))
				ballData.createText("",line_elements[2])
				humanData.createText("",line_elements[1])
			else:
				read_values = None

if __name__ == "__main__":
	root = tkinter.Tk()
	root.title("Timer")
	root.geometry("1500x1000")

	port = SerialPort

	#上部フレーム
	frame_top = tkinter.Frame(root)
	frame_top.pack(pady=5)

	#シリアルポート選択メニュー
	port.var = tkinter.StringVar(root)
	port.menu = tkinter.OptionMenu(frame_top,port.var,'')
	port.menu.pack(side=tkinter.LEFT, padx=5)
	
	#シリアルポートの更新
	port=refresh_serial_ports(port)

	#接続/切断ボタン
	ser = Serial()
	ser.close()
	toggle_button = tkinter.Button(
		frame_top,text="接続",
		command=lambda:toggle_serial_port(port))
	toggle_button.pack(side=tkinter.LEFT, padx=5)
	#ボーレート選択メニュー
	baudrate=Baudrate
	baudrate.var = tkinter.StringVar(root)
	baudrate.var.set(BAUDRATES[0])
	baudrate.menu = tkinter.OptionMenu(frame_top, baudrate.var, *BAUDRATES)
	baudrate.menu.pack(side=tkinter.LEFT, padx=5)
	
	
	ballData = DrawerFig(root,["成功率","00.00%"],"ball_sche.png",[750,400],[5,-45])
	humanData = DrawerFig(root,["投球数","00"],"human_sche.png",[1200,700],[-70,-80])
	goalData = DrawerFig(root,["ゴール数","00"],"goal.png",[350,250],[-125,125])
	analogClock=DrawerClock(root)


	root.mainloop()
```