以Python 做服务器,N Robot 做客户端,小小UI,拿捏

先Show 结果~

虚拟开足

python 复制代码
import socket
import threading
import logging
from tkinter import Tk, Canvas, BOTH, YES, NW, N, mainloop, font as tkfont
from tkinter.constants import S, E, W
from datetime import datetime
import os

# 定义文件夹路径
folder_path = r'c:\Log6'
# 检查文件夹是否存在,如果不存在则创建
if not os.path.exists(folder_path):
    os.makedirs(folder_path)

class RobotServer:
    def __init__(self, host, port, canvas):
        self.host = host
        self.port = port
        self.server_socket = None
        self.clients = {}
        self.lock = threading.Lock()
        self.canvas = canvas
        self.A = 0
        self.B = 4
        self.R1_LS = []
        self.R2_LS = []
        self.R3_LS = []
        self.R4_LS = []

        logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

        # 初始化画布内容
        self.init_canvas()

    def init_canvas(self):
        w = self.canvas.winfo_width()
        h = self.canvas.winfo_height()
        L_w, M_w, R_w = w//3, w//3, w - (w//3 * 2)
        L_h, M_h, R_h = h, h, h

        # 显示整体、曲线、计划的文本标签,字体为60,背景为立体感
        titles = ["整体"]
        for i, title in enumerate(titles):
            x = 130
            shadow_offset = 4
            #self.canvas.create_rectangle(x-100+shadow_offset, 20+shadow_offset, x+100+shadow_offset, 80+shadow_offset, fill="darkgray", outline="")
            #self.canvas.create_rectangle(x-100, 20, x+100, 80, fill="lightgray", outline="gray")
            self.canvas.create_text(x, 50, text=title, anchor=N, font=tkfont.Font(size=60))

        titles = ["Remark:\r\n分子为在线数量,\r\n分母为Total 数量,\r\n小于55%,显示红色,\r\n50%~75%,显示黄色,\r\n大于75%,显示绿色"]
        for i, title in enumerate(titles):
            self.canvas.create_text(130, 400, text=title, anchor=N, font=tkfont.Font(size=16))

        titles = ["曲线"]
        for i, title in enumerate(titles):
            x = (i+0.5) * w/3 +300
            shadow_offset = 4
            #self.canvas.create_rectangle(x-100+shadow_offset, 20+shadow_offset, x+100+shadow_offset, 80+shadow_offset, fill="darkgray", outline="")
            #self.canvas.create_rectangle(x-100, 20, x+100, 80, fill="lightgray", outline="gray")
            self.canvas.create_text(x, 50, text=title, anchor=N, font=tkfont.Font(size=60))

        titles = ["计划"]
        for i, title in enumerate(titles):
            x = 1080
            shadow_offset = 4
            #self.canvas.create_rectangle(x-100+shadow_offset, 20+shadow_offset, x+100+shadow_offset, 80+shadow_offset, fill="darkgray", outline="")
            #self.canvas.create_rectangle(x-100, 20, x+100, 80, fill="lightgray", outline="gray")
            self.canvas.create_text(x, 50, text=title, anchor=N, font=tkfont.Font(size=60))

        titles = ["R1_Data"]
        for i, title in enumerate(titles):
            x = 350
            self.canvas.create_rectangle(x-70, 160, x+60, 160+30, fill="lightgray", outline="gray")
            self.canvas.create_text(x, 160, text=title, anchor=N, font=tkfont.Font(size=20))

        titles = ["R2_Data"]
        for i, title in enumerate(titles):
            x = 350
            self.canvas.create_rectangle(x-70, 460, x+60, 460+30, fill="darkgray", outline="gray")
            self.canvas.create_text(x, 460, text=title, anchor=N, font=tkfont.Font(size=20))


        self.draw_ring(90, 300, 70, 40)    
        self.update_connection_status()   
        self.draw_table(w, h)         

        # 使用 after 方法更新曲线
        self.update_scatter_plots()

    def draw_ring(self, x, y, outer_radius, inner_radius):
        ratio = self.A / self.B if self.B != 0 else 0
        color = "red" if ratio < 0.5 else "yellow" if ratio <= 0.75 else "green"
        self.canvas.create_oval(x-outer_radius, y-outer_radius, x+outer_radius, y+outer_radius, fill=color)
        self.canvas.create_oval(x-inner_radius, y-inner_radius, x+inner_radius, y+inner_radius, fill="white")
        self.connection_text = self.canvas.create_text(x, y, text=f"{self.A}/{self.B}", fill="black")

    def update_connection_status(self):
        ratio = self.A / self.B if self.B != 0 else 0
        color = "red" if ratio < 0.5 else "yellow" if ratio <= 0.75 else "green"
        self.canvas.itemconfig(self.connection_text, text=f"{self.A}/{self.B}")
        self.canvas.itemconfig(self.connection_text, fill="black")
        self.canvas.itemconfig(self.connection_text, state='normal')
        self.draw_ring(90, 300, 70, 40)

    def draw_table(self, w, h):
        # 计划区域的宽度和高度
        plan_area_width = w // 3
        plan_area_height = h

        # 表格应该占用计划区域的大部分空间,但要留出一些边距
        margin = 20
        table_width = plan_area_width - 2 * margin
        table_height = min(150, plan_area_height - 2 * margin)  # 确保表格不会过高

        # 表格起始坐标,使得表格居中于计划区域
        table_x = w - plan_area_width + margin
        table_y = (h - table_height) // 2 -30

        row_height = 40
        col_width = table_width // 4  # 每一列的宽度等于表格宽度的四分之一

        # 清除旧表格
        self.canvas.delete("table")

        # 使用"+"绘制表格
        for j in range(6):  # 包括表头和四行数据  6
            self.canvas.create_line(table_x, table_y + j * row_height, table_x + table_width, table_y + j * row_height, fill="black", tags="table")
            for i in range(table_width // col_width + 1):
                self.canvas.create_text(table_x + i * col_width, table_y + j * row_height, text="+", fill="black", font=tkfont.Font(size=10), tags="table")
        for i in range(6):  # 包括边界线和四个分隔线  6                                           ##       5   ##
            self.canvas.create_line(table_x + i * col_width, table_y, table_x + i * col_width, table_y + 5 * row_height, fill="black", tags="table")

        # 绘制表头(列标签)
        for j, quarter in enumerate(['', 'Q1', 'Q2', 'Q3', 'Q4']):
            self.canvas.create_text(table_x + j * col_width + col_width // 2, table_y - 10, text=quarter, font=tkfont.Font(size=30), anchor=N, tags="table")

        # 绘制表格内容(行标签与X标记)
        for i, row in enumerate(['R1', 'R2', 'R3', 'R4']):
            self.canvas.create_text(table_x + col_width // 2, table_y + (i + 1) * row_height, text=row, font=tkfont.Font(size=30), anchor=N, tags="table")
            self.canvas.create_text(table_x + (i + 1) * col_width + col_width // 2, table_y + (i + 1) * row_height, text="X", font=tkfont.Font(size=30), anchor=N, tags="table")

    def update_scatter_plots(self):
        self.draw_scatter_plot('R1', self.R1_LS[-30:], 270, 250, 500, 100)  
        self.draw_scatter_plot('R2', self.R2_LS[-30:], 270, 350, 500, 100)
        self.draw_scatter_plot('R3', self.R3_LS[-30:], 270, 550, 500, 100)  
        self.draw_scatter_plot('R4', self.R4_LS[-30:], 270, 650, 500, 100)
        self.canvas.after(500, self.update_scatter_plots)  

    def draw_scatter_plot(self, prefix, data_list, start_x, start_y, width, height):
        scale = width / max(len(data_list), 1)
        self.canvas.delete(f"{prefix}_scatter")  # 删除旧的散点图元素
        
        # 增加圆点大小
        dot_size = 6  # 圆点直径变为原来的三倍大

        for i, value in enumerate(data_list):
            x = start_x + i * scale
            y = start_y - int(value) * (height / 100)
            color = "green" if 23 <= int(value) <= 29 else "red"
            self.canvas.create_oval(x - dot_size, y - dot_size, x + dot_size, y + dot_size, fill=color, tags=f"{prefix}_scatter")  

        # 清除旧的文本标签
        self.canvas.delete(f"{prefix}_text")

        # 只显示最近进来的2个数字,蓝色显示值
        for i, value in enumerate(data_list[-2:]):
            text_color = "blue"
            self.canvas.create_text(start_x + len(data_list) * scale - i * scale, start_y - height / 2 - 10, 
                                    text=value, font=tkfont.Font(size=12), anchor=S, fill=text_color, tags=f"{prefix}_text")

        # 中间水平线
        self.canvas.create_line(start_x, start_y, start_x + width, start_y, fill="black", tags=f"{prefix}_line")
        # 中间分割线
        self.canvas.create_line(start_x, start_y - height / 2, start_x + width, start_y - height / 2, fill="black", tags=f"{prefix}_line")

    def start_server(self):
        try:
            self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.server_socket.bind((self.host, self.port))
            self.server_socket.listen(5)
            logging.info(f"Server started at {self.host}:{self.port}")
            while True:
                client_socket, client_address = self.server_socket.accept()
                with self.lock:
                    self.clients[client_address] = client_socket
                    self.A += 1
                    self.update_connection_status()
                logging.info(f"Accepted connection from {client_address}")
                threading.Thread(target=self.handle_client, args=(client_socket, client_address), daemon=True).start()
        except Exception as e:
            logging.error(f"Failed to start server: {e}")
        finally:
            if self.server_socket:
                self.server_socket.close()

    def handle_client(self, client_socket, client_address):
        try:
            while True:
                current_time = datetime.now().strftime("%H:%M:%S")
                data = client_socket.recv(1024)
                if not data:
                    break
                decoded_data = data.decode('utf-8')
                logging.info(f"{current_time} -- Received from {client_address}: {decoded_data}")
                response = current_time
                client_socket.sendall(response.encode('utf-8'))
                logging.info(f"{current_time} -- response to RobotServer('192.168.0.109', 7777): {response}")

                # 解析数据并更新散点图数据
                if decoded_data.startswith('R1'):
                    self.R1_LS.append(decoded_data[2:4])
                elif decoded_data.startswith('R2'):
                    self.R2_LS.append(decoded_data[2:4])
                elif decoded_data.startswith('R3'):
                    self.R3_LS.append(decoded_data[2:4])
                elif decoded_data.startswith('R4'):
                    self.R4_LS.append(decoded_data[2:4])

                log_entry = f"[{current_time}]-Recive:{data}---Response:{response}\n"  
                with open(r"c:\Log6\log.txt", "a") as log_file:  
                    log_file.write(log_entry)  

        except Exception as e:
            logging.error(f"Error handling client {client_address}: {e}")
        finally:
            client_socket.close()
            with self.lock:
                del self.clients[client_address]
                self.A -= 1
                self.update_connection_status()
            logging.info(f"Closed connection from {client_address}")

def on_resize(event):
    global server
    w = event.width
    h = event.height
    canvas = event.widget
    canvas.delete("all")
    server.init_canvas()

def main():
    global server
    root = Tk()
    root.title("Design By Tim")
    root.geometry("800x600")

    canvas = Canvas(root, bg="white")  
    canvas.pack(fill=BOTH, expand=YES)

    server = RobotServer('192.168.0.109', 7788, canvas)

    threading.Thread(target=server.start_server, daemon=True).start()

    canvas.bind("<Configure>", on_resize)

    root.mainloop()

if __name__ == "__main__":
    main()
相关推荐
久绊A2 小时前
Python 基本语法的详细解释
开发语言·windows·python
勤奋的凯尔森同学5 小时前
webmin配置终端显示样式,模仿UbuntuDesktop终端
linux·运维·服务器·ubuntu·webmin
Hylan_J5 小时前
【VSCode】MicroPython环境配置
ide·vscode·python·编辑器
莫忘初心丶5 小时前
在 Ubuntu 22 上使用 Gunicorn 启动 Flask 应用程序
python·ubuntu·flask·gunicorn
丁卯4046 小时前
Go语言中使用viper绑定结构体和yaml文件信息时,标签的使用
服务器·后端·golang
chengooooooo6 小时前
苍穹外卖day8 地址上传 用户下单 订单支付
java·服务器·数据库
人间打气筒(Ada)7 小时前
MySQL主从架构
服务器·数据库·mysql
失败尽常态5238 小时前
用Python实现Excel数据同步到飞书文档
python·excel·飞书
落笔画忧愁e8 小时前
FastGPT快速将消息发送至飞书
服务器·数据库·飞书
2501_904447748 小时前
OPPO发布新型折叠屏手机 起售价8999
python·智能手机·django·virtualenv·pygame