LoginSignup
0
1

AnalogClock

Posted at
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()
0
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1