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()