python局域网实现投票udp实现,matplotlib绘制条形统计图输出结果

话不多说先看代码!

需要网络调试助手配合


import socket
import time
import os
import threading
from matplotlib import pyplot as plt


class VoteSys(object):
    """
    投票系统+数据分析
    """

    def __init__(self, theme, num_info, time_info):
        self.udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 创建套接字
        self.udp_socket.bind(("", 6666))  # 绑定接受端口
        self.send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 广播套接字
        self.theme = theme  # 投票主题(例:第三次项目投票)
        self.num = num_info  # 投票上限
        self.cycle = time_info  # 计时周期
        self.Dataset = list()  # 数据列表
        self.id_list = list()
        self.save_data_info = dict()  # 数据分析的得分
        self.team_name = None

    def end_sys(self):
        """
        主要负责计时和负责发送广播
        :return:
        """
        self.team_name = input("请输入本轮投票的团队名称(例:一组)")
        # self.send_info(team_name)
        for i in range(self.cycle):
            content = self.cycle - i
            if i % 15 == 0:
                self.send_info(self.team_name)
            time.sleep(1)
            print("Remaining time for voting:(%ds) Current participants:(%d)" % (content, len(self.Dataset)), end="\r")
        self.udp_socket.close()  # 关闭接受套接字
        self.send_socket.close()

    def send_info(self, team_name):
        """启动方法"""
        # 设置UDP套接字允许其广播(注意如果udp套接字需要广播,则一定要添加此语句,否则不用添加)
        self.send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

        dest_info = ("<broadcast>", 10001)  # <broadcast>会自动改为本语句网的广播ip

        send_info = "正在进行:<" + team_name + ">项目投票!回复本ip固定接收端口:6666(已投请忽略)"

        # 此时只要是本局域网中的电脑上有 用10001端口的udp程序 它就会收到此数据
        self.send_socket.sendto(send_info.encode('utf-8'), dest_info)

    def rec_info(self):
        """
        接收投票信息筛选
        """
        # 多线程计时
        end_time = threading.Thread(target=self.end_sys)
        end_time.start()
        # print("投票通道创建成功!")
        while True:
            try:
                # 接收消息
                contents, client_info = self.udp_socket.recvfrom(100)
                # 是否是数字,是否超过上限,判断是否重复投票,
                if contents.isdigit() and client_info[0] not in \
                        self.id_list and int(contents) <= self.num:
                    # 将数据存入数据组
                    self.Dataset.append(int(contents))
                    # 将ip存入列表判断是否重复
                    self.id_list.append(client_info[0])
                    # 回送数据收到信息
                    self.remind_info(client_info[0], client_info[1])
                    # print("Got a vote from %s Current participants:%d" % (client_info[0], len(self.Dataset)))
                else:
                    # 不满足条件发送数据错误信息
                    self.remind_error(client_info[0], client_info[1], contents)
            except OSError:
                print("")
                # print("投票通道关闭")
                break
        self.math_data()

    def remind_error(self, temp_id, temp_port, temp_content):
        """报错方法"""
        # 1. 创建一个udp套接字
        temp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

        if temp_id in self.id_list:
            temp.sendto("Error:重复投票无效(一次就好)".encode("utf-8"), (temp_id, temp_port))

        elif not temp_content.isdigit():
            temp.sendto("Error:请输入有效数字(整数)".encode("utf-8"), (temp_id, temp_port))

        elif int(temp_content) >= self.num:
            temp.sendto(("Error:超过上限(上限:%d)" % self.num).encode("utf-8"), (temp_id, temp_port))

        temp.close()

    def save_data(self, temp_name, temp_data):
        try:
            with open("Vote_data.txt", "r") as r1:
                # 读取数据中的字典
                self.save_data_info = eval(r1.read())
                # 将结果保存
        except FileNotFoundError:
            pass
        finally:
            # 向字典中添加数据
            self.save_data_info[temp_name] = temp_data
            # 保存字典
            with open("Vote_data.txt", "w") as w1:
                w1.write(str(self.save_data_info))
                print("The data has been saved!")

    def math_data(self):
        self.Dataset.sort()
        if len(self.Dataset) == 0:
            # print("无人投票")
            self.save_data(self.team_name, 0)
        elif 0 < len(self.Dataset) <= 3:
            result = 0
            for num in self.Dataset:
                result += num
            result = result / len(self.Dataset)
            self.save_data(self.team_name, result)
        else:
            del self.Dataset[0]  # 删除最小值
            del self.Dataset[-1]  # 删除最大值
            result = 0
            for num in self.Dataset:
                result += num
            result = result / len(self.Dataset)
            self.save_data(self.team_name, result)

    @staticmethod
    def remind_info(temp_id, temp_port):
        """投票成功"""

        # 1. 创建一个udp套接字
        t = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

        t.sendto("Author: 投票成功!谢谢参与!".encode("utf-8"), (temp_id, temp_port))

        # 3. 关闭
        t.close()


class Initiate(object):
    def __init__(self):
        """初始化方法:创建读取数据"""
        self.init_info = None
        self.judge()  # 判断是否有正在进行的投票

    def main(self):
        if self.init_info:
            # 这种情况是已经有投票的数据
            temp_judge = input("当前正在进行(%s)投票,是否进入(y/n)<打印投票结果请输入:print>" % self.init_info[0])
            if temp_judge.lower() == "y":
                print("主题:%s 上限:%d 周期:%d" % (self.init_info[0], self.init_info[1], self.init_info[2]))
                test = VoteSys(self.init_info[0], self.init_info[1], self.init_info[2])
                test.rec_info()  # 等待接收
            elif temp_judge.lower() == "n":
                # 删除历史数据重新开始
                os.remove("./user_info.txt")
                os.remove("./Vote_data.txt")
                self.reset()
                test = VoteSys(self.init_info[0], self.init_info[1], self.init_info[2])
                test.rec_info()  # 等待接收
            elif temp_judge == "print":
                self.draw_show()
        else:
            # 直接重新开始
            self.reset()
            test = VoteSys(self.init_info[0], self.init_info[1], self.init_info[2])
            test.rec_info()  # 等待接收

    def judge(self):
        """
        判断是否有初始化设置
        :return:
        """
        try:
            with open("user_info.txt", "r") as t1:
                content_info = t1.read()
                self.init_info = eval(content_info)

        except FileNotFoundError:
            self.init_info = None

    def reset(self):
        theme_info = input("请输入本次投票的主题(例:Python1/2第一次项目竞赛):")
        num_info = int(input("请输入本次头票上限(例:5):"))
        temp_time_info = int(input("请输入本次投票周期(秒):"))
        self.init_info = [theme_info, num_info, temp_time_info]
        with open("user_info.txt", "w") as t3:
            t3.write(str([theme_info, num_info, temp_time_info]))

    @staticmethod
    def draw_show():
        a = list()
        b = list()
        with open("Vote_data.txt", "r") as r1:
            content = eval(r1.read())
        for temp_a, temp_b in content.items():
            a.append(temp_a)
            b.append(temp_b)
        with open("user_info.txt", "r") as r6:
            temp_title = eval(r6.read())[0]

        plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
        plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

        plt.figure(figsize=(20, 8), dpi=80)

        plt.bar(range(len(a)), b, width=0.25, color="orange", label="组别")

        plt.xticks(range(len(a)), a, fontsize=12)  # , rotation=45

        # 添加描述
        plt.xlabel("组名")  # x轴描述
        plt.ylabel("得分")  # y轴描述
        plt.title(temp_title)  # 标题

        # 添加图例
        # plt.legend(loc=0)  # loc=位置参数(默认识别位置)

        # 绘制网格(默认对应)
        plt.grid(alpha=0.4)  # alpha=透明度

        plt.show()


if __name__ == "__main__":
    """
    说明:
    """
    t1 = Initiate()
    t1.main()

效果展示:

开始输入投票主题作为条形统计图的主题,然后输入投票上限和时间
然后输入项目的名称作为统计图的x轴,然后会程序后发送广播,局域网中端口在10001的调试助手都会收到消息,然后向端口6666(可自己修改)回送结果,程序会记录下ip地址避免重复投票,然后会筛选删除最高分和最低分给出平均分
在这里插入图片描述
这是投票成功后的反馈:如果重复投票也会收到反馈
在这里插入图片描述
这是输出的结果:
在这里插入图片描述
结语:
这个项目主要是为了练习我们的python基础多线程和python的网络编程能力,还有一点点的数据处理的相关知识,matplotlib是一个python底层的绘图库直接pip install 即可,欢迎交流!有GUI非常厉害的朋友能不能教教我把这个做成一个有界面的程序呢?有什么问题欢迎交流!共同学习,共同进步

发布了18 篇原创文章 · 获赞 15 · 访问量 2796

猜你喜欢

转载自blog.csdn.net/qq_42768234/article/details/87348838
今日推荐