20231213 实验四《Python程序设计》实验报告

20231213 2024-2025-2 《Python程序设计》实验四报告

课程:《Python程序设计》
班级: 2312
姓名: 蔡明辉
学号:20231213
实验教师:王志强
实验日期:2025年5月14日
必修/选修: 公选课

实验内容

设计一个编码工具箱,集成日常专业知识学习时所用到的功能。
主要包括:

  • 各种编码格式的文字编码和解码
  • 哈希函数的计算
  • 生成大素数、生成随机数
  • 对大数进行因子分解

使用tkinter库设计图形化界面

改进方向

考虑拓展方向:
在编码和解码功能部分增加导入和导出文件的功能
增加自动推测可能的编码解码格式,简化操作步骤
在哈希函数部分增加导入文件的功能,方便计算各种文件的哈希值
在大数功能可以增加公钥密码相关的功能,比如生成公私钥对的功能
在编码和解码设计好的接口基础上,进一步实现各种密码加密算法
在生成随机数的功能中,在实际环境中需要生成的大素数超过1024位,此时应当使用判断大数素性的其它计算方法。

设计过程

版本迭代情况

实现基本布局框架
完成分功能模块设计
添加异常处理和输入验证
优化UI布局和交互体验
添加多线程和超时机制
增加公钥密码RSA的密钥生成与加密解密

窗口设计

选择ttk主题控件提升美观度
使用ScrolledText处理长文本输出
使用tkinter.TK()生成窗口,采用响应式布局适应窗口缩放
采用Notebook多标签页布局,方便区分三个功能区
使用frame设计选择框架,进行分组管理

点击查看代码
class codehelper:
    def __init__(self, root):
        self.root = root
        self.root.title("lumiere的编码工具箱")
        self.setup_ui()

        self.executor = ThreadPoolExecutor(max_workers=1)
        self.active_task = None

    def setup_ui(self):
        style = ttk.Style()
        style.configure('TButton', font=('None', 12), padding=6)
        style.configure('TLabel', font=('None', 12))
        style.configure('TEntry', font=('None', 12))
        style.configure('TCombobox', font=('None', 12))

        self.notebook = ttk.Notebook(self.root)
        self.notebook.pack(fill='both', expand=True, padx=10, pady=10)
        #
        self.encoding_system()
        self.hash_system()
        self.math_number_system()
        self.exit()

功能分区

下面设计程序的主体框架
主要实现三个功能分区的细节布局

点击查看代码
    def encoding_system(self):
        tab = ttk.Frame(self.notebook)
        self.notebook.add(tab, text="编码工具")

        encoding_frame = ttk.LabelFrame(tab, text="编码/解码")
        encoding_frame.grid(row=0, column=0, padx=10, pady=5, sticky="nsew")#

        ttk.Label(encoding_frame, text="输入文本:").grid(row=0, column=0, sticky="w")

        self.input_text = scrolledtext.ScrolledText(encoding_frame, width=50, height=5, font=('None', 12))
        self.input_text.grid(row=1, column=0, columnspan=3, pady=5)

        ttk.Label(encoding_frame, text="编码类型:").grid(row=2, column=0, sticky="w")
        self.encoding_var = tk.StringVar()
        encodings = ['utf-8', 'utf-16', 'gbk', 'gb2312','待开发']#
        self.encoding_menu = ttk.Combobox(encoding_frame, textvariable=self.encoding_var, values=encodings)
        self.encoding_menu.grid(row=2, column=1, pady=5)

        ttk.Label(encoding_frame, text="操作:").grid(row=3, column=0, sticky="w")
        self.operation_var = tk.StringVar()


        ttk.Radiobutton(encoding_frame, text="编码", variable=self.operation_var, value="encode").grid(row=3, column=1)
        ttk.Radiobutton(encoding_frame, text="解码", variable=self.operation_var, value="decode").grid(row=3, column=2)
        '''
        由于方框选项选中后暂时无法实现高亮提示
        故此处将编码和解码的操作用圆点选项实现
        '''

        ttk.Button(encoding_frame, text="执行转换", command=self.encode_and_decode).grid(row=4, column=0, columnspan=3, pady=10)

        ttk.Label(encoding_frame, text="输出结果:").grid(row=5, column=0, sticky="w")
        self.output_text = scrolledtext.ScrolledText(encoding_frame, width=50, height=5, font=('None', 12))
        self.output_text.grid(row=6, column=0, columnspan=3, pady=5)

    def hash_system(self):
        tab = ttk.Frame(self.notebook)
        self.notebook.add(tab, text="哈希函数")

        hash_frame = ttk.LabelFrame(tab, text="哈希计算")
        hash_frame.pack(padx=10, pady=5, fill='both', expand=True)

        ttk.Label(hash_frame, text="输入文本:").pack(anchor="w")
        self.hash_input = scrolledtext.ScrolledText(hash_frame, height=4, font=('None', 12))
        self.hash_input.pack(fill='x', pady=5)

        ttk.Label(hash_frame, text="哈希算法:").pack(anchor="w")
        self.hash_type = ttk.Combobox(hash_frame, values=['md5', 'sha1', 'sha256'])
        self.hash_type.pack(fill='x', pady=5)
        self.hash_type.set('md5')

        ttk.Button(hash_frame, text="计算哈希值", command=self.calculate_hash).pack(pady=10)
        ttk.Label(hash_frame, text="计算结果:").pack(anchor="w")
        self.hash_output = ttk.Entry(hash_frame, font=('None', 12))
        self.hash_output.pack(fill='x', pady=5)

    def math_number_system(self):
        tab = ttk.Frame(self.notebook)
        self.notebook.add(tab, text="数字工具")

        rand_frame = ttk.LabelFrame(tab, text="随机数生成")
        rand_frame.grid(row=0, column=0, padx=10, pady=5, sticky="nsew")

        ttk.Label(rand_frame, text="二进制最大位数:").grid(row=0, column=0)
        self.rand_input = ttk.Entry(rand_frame)
        self.rand_input.grid(row=0, column=1)
        ttk.Button(rand_frame, text="生成随机数", command=self.generate_random).grid(row=0, column=2)
        ttk.Button(rand_frame, text="生成大素数", command=self.generate_prime).grid(row=0, column=3)
        self.rand_output = scrolledtext.ScrolledText(rand_frame, height=4, font=('None', 12))
        self.rand_output.grid(row=1, column=0, columnspan=4, sticky="ew")

        factor_frame = ttk.LabelFrame(tab, text="因数分解")
        factor_frame.grid(row=1, column=0, padx=10, pady=5, sticky="nsew")

        ttk.Label(factor_frame, text="输入数字:").grid(row=0, column=0)
        self.factor_input = ttk.Entry(factor_frame)
        self.factor_input.grid(row=0, column=1)
        ttk.Button(factor_frame, text="分解因数", command=self.factor_div).grid(row=0, column=2)
        self.factor_output = scrolledtext.ScrolledText(factor_frame, height=4, font=('None', 12))
        self.factor_output.grid(row=1, column=0, columnspan=3, sticky="ew")

        tab.columnconfigure(0, weight=1)
        tab.rowconfigure(0, weight=1)
        tab.rowconfigure(1, weight=1)

    def exit(self):
        exit_frame = ttk.Frame(self.root)
        exit_frame.pack(pady=10)
        ttk.Button(exit_frame, text="退出程序", command=self.root.quit, style='danger.TButton').pack()

功能实现

对程序主体调用的功能进行具体实现
直接调用hashlib库中的函数,实现哈希函数的计算
调用random库,实现生成随机数
messagebox._show设置弹窗,在计算超过10秒后提示运算超时,并终止运算
关于超时检测,使用线程池执行耗时任务,独立线程

点击查看代码
self.executor = ThreadPoolExecutor(max_workers=1)
future = self.executor.submit(trial_division, n)
self.active_task = future
def check_result():
    if future.done():
        # 更新UI
    else:
        if time.time() - start_time > 10:
            future.cancel()
            # 显示超时提示

具体功能

点击查看代码
    def encode_and_decode(self):
        try:
            text = self.input_text.get("1.0", "end").strip()
            encoding = self.encoding_var.get()
            operation = self.operation_var.get()

            if operation == "encode":
                encoded = text.encode(encoding)
                hex_result = encoded.hex()
                bin_result = bin(int.from_bytes(encoded, byteorder='big'))[2:]
                result = f"十六进制: {hex_result}\n二进制: {bin_result}"
            else:
                if all(c in "0123456789abcdefABCDEF" for c in text):
                    decoded = bytes.fromhex(text).decode(encoding) 
                else:
                    bytes(int(text[i:i+8], 2) for i in range(0, len(text), 8)).decode(encoding)
                result = f"解码结果: {decoded}"

            self.output_text.delete("1.0", "end")
            self.output_text.insert("1.0", result)
        except Exception as e:
            messagebox.showerror("错误", f"转换失败: {str(e)}")

    def calculate_hash(self):
        text = self.hash_input.get("1.0", "end").strip().encode()
        algorithm = self.hash_type.get()
        hash_func = {'md5': hashlib.md5,'sha1': hashlib.sha1,'sha256': hashlib.sha256}[algorithm]
        self.hash_output.delete(0, "end")
        self.hash_output.insert(0, hash_func(text).hexdigest())

    def generate_random(self):
        try:
            bits = int(self.rand_input.get())
            number = random.getrandbits(bits)
            self.rand_output.delete("1.0", "end")
            self.rand_output.insert("1.0", str(number))
        except:
            messagebox.showerror("错误", "请输入有效的位数")

    #
    def generate_prime(self):
        """
        由于埃氏筛不适用于本场景
        更换为密码学的概率性算法
        Miller-Rabin素性测试
        时间限制,区测试次数为5
        """
        def is_prime(n, k=5):
            if n <= 1:
                return False
            for p in [2,3,5,7,11,13,17,19,23,29]:
                if n % p == 0:
                    return n == p
            d = n - 1
            s = 0
            while d % 2 == 0:
                d //= 2
                s += 1
            for _ in range(k):
                a = random.randint(2, min(n-2, 1<<20))
                x = pow(a, d, n)
                if x == 1 or x == n-1:
                    continue
                for __ in range(s-1):
                    x = pow(x, 2, n)
                    if x == n-1:
                        break
                else:
                    return False
            return True

        try:
            bits = int(self.rand_input.get())
            while True:
                p = random.getrandbits(bits) | (1 << (bits-1)) | 1
                if is_prime(p):
                    self.rand_output.delete("1.0", "end")
                    self.rand_output.insert("1.0", str(p))
                    break
        except:
            messagebox.showerror("错误", "请输入有效的位数")

    def factor_div(self):
        """
        分解因子
        """
        def trial_division(n):
            factors = []
            while n % 2 == 0:
                factors.append(2)
                n //= 2
            i = 3
            max_factor = isqrt(n) + 1
            while i <= max_factor and n > 1:
                while n % i == 0:
                    factors.append(i)
                    n //= i
                    max_factor = isqrt(n) + 1
                i += 2
            if n > 1:
                factors.append(n)
            return factors

        try:
            n = int(self.factor_input.get())
            start_time = time.time()
            
            future = self.executor.submit(trial_division, n)
            self.active_task = future
            
            def check_result():
                if future.done():
                    try:
                        factors = future.result()
                        if len(factors) == 1:
                            result = f"{n} 是素数"
                        else:
                            result = " × ".join(map(str, factors))
                        self.factor_output.delete("1.0", "end")
                        self.factor_output.insert("1.0", result)
                    except Exception as e:
                        messagebox.showerror("错误", str(e))
                else:
                    """
                    为避免超出能力的因子分解计算
                    设定最大运算时间为10秒
                    据测试可以完成70位二进制非素数的分解
                    """
                    if time.time() - start_time > 10:
                        future.cancel()
                        messagebox.showwarning("超时", "运算超时,已终止")
                    else:
                        self.root.after(500, check_result)
            
            check_result()
        except ValueError:
            messagebox.showerror("错误", "请输入有效整数")

主函数

由此已完成所有功能的实现,下面运行主函数

点击查看代码
root = tk.Tk()
root.geometry("800x600")
app = codehelper(root)
root.mainloop()

更新RSA功能

补充增加RSA的密钥生成与加密解密

点击查看代码
    def create_rsa_tab(self):
        tab = ttk.Frame(self.notebook)
        self.notebook.add(tab, text="RSA密码系统")
        
        key_frame = ttk.LabelFrame(tab, text="密钥生成")
        key_frame.pack(fill='x', padx=10, pady=5)
        
        ttk.Label(key_frame, text="密钥长度:").grid(row=0, column=0, padx=5, pady=5)
        self.rsa_key_size = ttk.Combobox(key_frame, values=[1024, 2048, 4096], width=10)
        self.rsa_key_size.grid(row=0, column=1, padx=5, pady=5)
        self.rsa_key_size.set(2048)
        
        ttk.Button(key_frame, text="生成密钥对", command=self.generate_rsa_keys).grid(row=0, column=2, padx=5, pady=5)
        
        pub_key_frame = ttk.Frame(key_frame, style='Key.TFrame')
        pub_key_frame.grid(row=1, column=0, columnspan=3, padx=5, pady=5, sticky='we')
        ttk.Label(pub_key_frame, text="公钥 (n, e):", style='Header.TLabel').pack(anchor='w')
        self.rsa_pub_key_text = scrolledtext.ScrolledText(pub_key_frame, height=3, width=80)
        self.rsa_pub_key_text.pack(fill='x', padx=5, pady=5)
        
        priv_key_frame = ttk.Frame(key_frame, style='Key.TFrame')
        priv_key_frame.grid(row=2, column=0, columnspan=3, padx=5, pady=5, sticky='we')
        ttk.Label(priv_key_frame, text="私钥 (n, d):", style='Header.TLabel').pack(anchor='w')
        self.rsa_priv_key_text = scrolledtext.ScrolledText(priv_key_frame, height=3, width=80)
        self.rsa_priv_key_text.pack(fill='x', padx=5, pady=5)
        
        crypto_frame = ttk.LabelFrame(tab, text="加密/解密")
        crypto_frame.pack(fill='x', padx=10, pady=5)
        
        ttk.Label(crypto_frame, text="输入文本:").grid(row=0, column=0, padx=5, pady=5, sticky='w')
        self.rsa_input_text = scrolledtext.ScrolledText(crypto_frame, height=4, width=80)
        self.rsa_input_text.grid(row=1, column=0, columnspan=3, padx=5, pady=5)
        
        ttk.Button(crypto_frame, text="加密", command=self.rsa_encrypt).grid(row=2, column=0, padx=5, pady=5)
        ttk.Button(crypto_frame, text="解密", command=self.rsa_decrypt).grid(row=2, column=1, padx=5, pady=5)
        
        ttk.Label(crypto_frame, text="结果:").grid(row=3, column=0, padx=5, pady=5, sticky='w')
        self.rsa_result_text = scrolledtext.ScrolledText(crypto_frame, height=4, width=80)
        self.rsa_result_text.grid(row=4, column=0, columnspan=3, padx=5, pady=5)
        
        self.rsa_public_key = None
        self.rsa_private_key = None

RSA的具体功能

点击查看代码
import rsa
    def generate_rsa_keys(self):
        key_size = int(self.rsa_key_size.get())
        try:
            (pubkey, privkey) = rsa.newkeys(key_size)
            self.rsa_public_key = pubkey
            self.rsa_private_key = privkey
            
            self.rsa_pub_key_text.delete(1.0, tk.END)
            self.rsa_pub_key_text.insert(tk.END, f"n = {pubkey.n}\n")
            self.rsa_pub_key_text.insert(tk.END, f"e = {pubkey.e}")
            
            self.rsa_priv_key_text.delete(1.0, tk.END)
            self.rsa_priv_key_text.insert(tk.END, f"n = {privkey.n}\n")
            self.rsa_priv_key_text.insert(tk.END, f"d = {privkey.d}")
            
            messagebox.showinfo("成功", "RSA密钥对生成成功!")
        except Exception as e:
            messagebox.showerror("错误", f"生成密钥时出错: {str(e)}")
    
    def rsa_encrypt(self):
        if not self.rsa_public_key:
            messagebox.showerror("错误", "请先生成RSA公钥")
            return
        
        text = self.rsa_input_text.get(1.0, tk.END).strip()
        if not text:
            messagebox.showerror("错误", "请输入要加密的文本")
            return
        
        try:
            encrypted = rsa.encrypt(text.encode('utf-8'), self.rsa_public_key)
            # (十六进制)
            self.rsa_result_text.delete(1.0, tk.END)
            self.rsa_result_text.insert(tk.END, encrypted.hex())
            messagebox.showinfo("成功", "加密完成!")
        except Exception as e:
            messagebox.showerror("错误", f"加密失败: {str(e)}")
    
    def rsa_decrypt(self):
        if not self.rsa_private_key:
            messagebox.showerror("错误", "请先生成RSA私钥")
            return
        
        text = self.rsa_input_text.get(1.0, tk.END).strip()
        if not text:
            messagebox.showerror("错误", "请输入要解密的文本")
            return
        
        try:
            encrypted = bytes.fromhex(text)
            decrypted = rsa.decrypt(encrypted, self.rsa_private_key).decode('utf-8')
            self.rsa_result_text.delete(1.0, tk.END)
            self.rsa_result_text.insert(tk.END, decrypted)
            messagebox.showinfo("成功", "解密完成!")
        except Exception as e:
            messagebox.showerror("错误", f"解密失败: {str(e)}")

代码:
https://gitee.com/cloud-lumiere/2025-python-course/blob/master/lumiere's codehelpher.py
https://gitee.com/cloud-lumiere/2025-python-course/blob/master/lumiere's codehelpher v2.py (增添了RSA功能)

运行结果

程序测试视频:https://mp.weixin.qq.com/s/DLSVCEc9JfXRwGXHhd53XA

编码与解码
image
image
哈希函数
image
生成随机数与分解因子
image
RSA的加密与解密
image
image

课程总结

序列、函数、流程控制、字符串、面相对象、模块、异常处理、文件,等等这些基础内容,感觉自学一周就差不多掌握,可以加快速度
最重要的是平时练,代码量上去当然就不成问题
正则表达式,socket,爬虫、数据库,这些是课上学习收获较大的地方
没有学到GUI、Web等挺可惜的,不过不耽误自学
补充一点,总是缺少面向对象的意识,总是拿写C的习惯只用函数,这是急需改正的

感想

在上本学期python课之前,我已经有过一年python自学经历。去年4月,在学长的建议下,我开始自学python,看的就是《Python编程:从入门到实践》这本书,学习的效果可以说马马虎虎吧,在学习数据结构时,越来越感觉C语言的不方便,于是平时更加喜欢用python。去年报名参加蓝桥杯,报了python B组,寒假只顾着玩了,算法只粗略的学了一遍、也没刷几道题。虽然觉得这次蓝桥杯特别简单,但最后只拿了省二,真是有些离谱了,我在洛谷上测了有80分……明年再战吧。
在做最后的课程大作业时,结合专业知识、结合本学期所学密码学课程内容及实验,结合本学期所学信息论与编码理论、信息安全课程以及学习中遇到的困惑与不便,我最终决定设计一个编码工具箱,集成日常可能用到的编码工具。
通过自己设计的编码工具箱,解决了平时需要到网络上搜索工具的问题,提高了学习效率,并保证了工具的安全可靠与适合自己使用的便利性,有很大可能日后进一步拓展功能。
C语言感觉学了很多,但自己一直都造不出什么像样的东西,一直面对一个黑框框实在是太难受了。寒假前的计算机实习,勉强做了一个看得过去的界面,很可惜还是死的界面。这学期Python课后,我终于决定做一个顺眼的程序界面。最初本来想着用pygame完成,但配置输入法输入没搞会而且布置界面太复杂,成了废案。最后就用了tkinter库来做界面,完成了整个设计。
Python课我学到的最重要的就是:利用知识解决实际问题。不管是解决密码学的编程实验、还是用外部库实现有趣的功能,Python都是趁手的工具。
终身学习,投入实践,共勉。

posted @ 2025-05-23 20:09  lumiere_cloud  阅读(72)  评论(0)    收藏  举报