├── .gitignore ├── LICENSE ├── README.md ├── calculator ├── ScreenShot │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ └── 5.png ├── dist │ └── main.exe └── src │ ├── main.py │ └── view.py ├── file_encryption ├── Python作业-8.docx ├── dist │ └── main.exe └── src │ ├── core.py │ ├── main.py │ ├── requirements.txt │ └── view.py └── student ├── ScreenShot ├── login.png ├── main.png ├── main1.png └── register.png ├── dist ├── config.ini ├── login.exe └── welcome.gif └── src ├── config.ini ├── database.py ├── gui.py ├── login.py ├── student.sql ├── view.py └── welcome.gif /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### JetBrains ### 17 | .idea 18 | __pycache__ 19 | venv 20 | *.iws 21 | *.iml 22 | *.ipr 23 | 24 | ## PyInstaller ## 25 | build 26 | *.spec 27 | 28 | ### NetBeans ### 29 | /nbproject/private/ 30 | /nbbuild/ 31 | /nbdist/ 32 | /.nb-gradle/ 33 | build/ 34 | 35 | ### VS Code ### 36 | .vscode/ 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Weixing Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # student-assigment 2 | Python课程作业,win32软件 3 | 4 | 其中一个是基于tkinter和MySQL实现的学生管理系统,因为只是一个日常作业,所以没有花费太多时间仅实现了基本的增删改查功能。 5 | 6 | 另一个是使用Tkinter实现的简单计算器,可以处理带括号的长算式。 7 | 8 | 还有一个是使用PyQT5开发的文件加密软件。 9 | 10 | [学神信息管理](./student) 11 | 12 | [计算器](./calculator) 13 | 14 | [文件加密](./file_encryption) 15 | 16 | ## 开发环境 17 | 18 | PyCharm 19 | 20 | python == 3.7 21 | 22 | 打包工具: PyInstaller == 3.6 23 | 24 | ## 部署说明 25 | 26 | ### 学生管理系统 27 | 28 | 数据使用MySQL存储,请先使用**student.sql**建立数据库,然后修改**config.ini**中的数据库连接信息,之后再将项目导入PyCharm或者其他IDE,程序入口文件为**login.py**。 29 | 30 | dist目录中有已经打包好的exe文件,修改**config.ini**中的数据库配置后理论上可以运行。 31 | 32 | 如果需要修改程序后重新打包成exe文件,可使用PyInstaller或者其他打包工具,打包后记得**将config.ini和welcome.gif复制到exe文件所在目录,否则无法运行**(具体视修改情况)。 33 | 34 | 学生管理打包命令: 35 | 36 | ```cmd 37 | pyinstaller -F -w login.py -p database.py -p gui.py -p view.py 38 | ``` 39 | 40 | --------- 41 | 42 | ### 计算器 43 | 44 | 计算器入口文件为**main.py**,view.py只是一个界面,无功能 45 | 46 | 计算器打包命令: 47 | 48 | ```cmd 49 | pyinstaller -F -w main.py -p view.py 50 | ``` 51 | 52 | --- 53 | 54 | ### 文件加密 55 | 56 | 见[文件加密](./file_encryption)的word文件 57 | 58 | ## 运行截图 59 | 60 | ### 学生管理系统 61 | 62 | ![](./student/ScreenShot/login.png) 63 | 64 | ![](./student/ScreenShot/register.png) 65 | 66 | ![](./student/ScreenShot/main.png) 67 | 68 | ![](./student/ScreenShot/main1.png) 69 | 70 | ### 计算器 71 | 72 | ![](./calculator/ScreenShot/1.png) 73 | 74 | ![](./calculator/ScreenShot/2.png) 75 | 76 | ![](./calculator/ScreenShot/3.png) 77 | 78 | ![](./calculator/ScreenShot/4.png) 79 | 80 | ![](./calculator/ScreenShot/5.png) 81 | 82 | ### 文件加密 83 | 84 | 见[文件加密](./file_encryption)中的word文件 -------------------------------------------------------------------------------- /calculator/ScreenShot/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weingxing/python-assigment/5726f3a6bb4f71e91a5802d1670a726c9671362a/calculator/ScreenShot/1.png -------------------------------------------------------------------------------- /calculator/ScreenShot/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weingxing/python-assigment/5726f3a6bb4f71e91a5802d1670a726c9671362a/calculator/ScreenShot/2.png -------------------------------------------------------------------------------- /calculator/ScreenShot/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weingxing/python-assigment/5726f3a6bb4f71e91a5802d1670a726c9671362a/calculator/ScreenShot/3.png -------------------------------------------------------------------------------- /calculator/ScreenShot/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weingxing/python-assigment/5726f3a6bb4f71e91a5802d1670a726c9671362a/calculator/ScreenShot/4.png -------------------------------------------------------------------------------- /calculator/ScreenShot/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weingxing/python-assigment/5726f3a6bb4f71e91a5802d1670a726c9671362a/calculator/ScreenShot/5.png -------------------------------------------------------------------------------- /calculator/dist/main.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weingxing/python-assigment/5726f3a6bb4f71e91a5802d1670a726c9671362a/calculator/dist/main.exe -------------------------------------------------------------------------------- /calculator/src/main.py: -------------------------------------------------------------------------------- 1 | from view import View 2 | 3 | 4 | # 是否点击 等于 按钮 5 | flag = False 6 | 7 | 8 | class Main: 9 | def __init__(self): 10 | self.view = View() 11 | self.view.set_ui() 12 | self.connect() 13 | 14 | # 绑定函数 15 | def connect(self): 16 | self.view.btn_zero.bind('', lambda e: self.input('0')) 17 | self.view.btn_one.bind('', lambda e: self.input('1')) 18 | self.view.btn_two.bind('', lambda e: self.input('2')) 19 | self.view.btn_three.bind('', lambda e: self.input('3')) 20 | self.view.btn_four.bind('', lambda e: self.input('4')) 21 | self.view.btn_five.bind('', lambda e: self.input('5')) 22 | self.view.btn_six.bind('', lambda e: self.input('6')) 23 | self.view.btn_seven.bind('', lambda e: self.input('7')) 24 | self.view.btn_eight.bind('', lambda e: self.input('8')) 25 | self.view.btn_nine.bind('', lambda e: self.input('9')) 26 | self.view.btn_add.bind('', lambda e: self.input('+')) 27 | self.view.btn_subtract.bind('', lambda e: self.input('-')) 28 | self.view.btn_multiply.bind('', lambda e: self.input('*')) 29 | self.view.btn_divide.bind('', lambda e: self.input('/')) 30 | self.view.btn_left.bind('', lambda e: self.input('(')) 31 | self.view.btn_right.bind('', lambda e: self.input(')')) 32 | self.view.btn_dot.bind('', lambda e: self.input('.')) 33 | self.view.btn_negate.bind('', lambda e: self.input('--')) 34 | self.view.btn_clear.bind('', self.clear) 35 | self.view.btn_equal.bind('', lambda e: self.view.show.configure( 36 | text=self.calculate(self.view.show_exp.cget('text')) 37 | )) 38 | 39 | # 计算表达式 40 | def calculate(self, expression): 41 | self.view.show_exp.configure(text=expression+'=') 42 | try: 43 | result = eval(expression) 44 | global flag 45 | flag = True 46 | return str(result) 47 | except (SyntaxError, NameError): 48 | flag = True 49 | return '错误' 50 | 51 | # 显示、处理输入(按钮点击) 52 | def input(self, value): 53 | # 刚刚计算完,清空原有数据 54 | global flag 55 | if flag: 56 | self.clear('') 57 | flag = False 58 | operate = ['+', '-', '*', '/'] 59 | text = self.view.show_exp.cget('text') 60 | # 正/负数转换 61 | tmp = True 62 | if value == '--': 63 | value = '' 64 | # 反转原来的数据 65 | text = text[::-1] 66 | idx = [] 67 | # 取得反转后的运算符索引 68 | for op in operate: 69 | i = text.find(op) 70 | if i != -1: 71 | idx.append(i) 72 | # 取得最前面的运算符 73 | try: 74 | index = min(idx) 75 | except ValueError: 76 | tmp = False 77 | index = len(text) 78 | # text = text + '-' 79 | try: 80 | if tmp and text[index] == '-' and text[index + 1] in operate: 81 | # 当前数是负数,变为正数,删除负号即可 82 | text = text[:index] + text[index + 1:] 83 | else: 84 | # 当前数不是负数,变为负数,插入负号即可 85 | text = text[:index] + '-' + text[index:] 86 | except IndexError: 87 | # 当前仅有一个数字,没有运算符 88 | text = text[:index] 89 | # 将数据恢复原来的顺序 90 | text = text[::-1] 91 | # 防止一个数输入多个小数点、运算符后跟小数点 92 | try: 93 | if value == '.': 94 | # 末尾已经是小数点 95 | if text[-1] == '.': 96 | value = '' 97 | # 反转方便取得最后一个操作数 98 | text = text[::-1] 99 | idx = [] 100 | # 取得反转后的运算符索引 101 | for op in operate: 102 | i = text.find(op) 103 | if i != -1: 104 | idx.append(i) 105 | # 取得最前面的运算符 106 | try: 107 | index = min(idx) 108 | except ValueError: 109 | # 第一个操作数已经包含小数点 110 | index = 0 111 | if '.' in text: 112 | value = '' 113 | # 最后输入的操作数已经包含小数点 114 | if '.' in text[:index]: 115 | value = '' 116 | # 转回原顺序 117 | text = text[::-1] 118 | except IndexError: 119 | pass 120 | # 运算符更改 最后一个是运算符且输入的也是运算符,则更改运算符 121 | try: 122 | if text[-1] in operate and value in operate: 123 | text = text[:-1] 124 | except IndexError: 125 | pass 126 | # 更新数据显示 127 | val = text + value 128 | self.view.show_exp.configure(text=val) 129 | 130 | # 清除 131 | def clear(self, event): 132 | self.view.show_exp.configure(text='') 133 | self.view.show.configure(text='') 134 | 135 | def start(self): 136 | self.view.window.mainloop() 137 | 138 | 139 | if __name__ == '__main__': 140 | main = Main() 141 | main.start() 142 | -------------------------------------------------------------------------------- /calculator/src/view.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | import tkinter.font as font 3 | 4 | 5 | class View: 6 | def __init__(self): 7 | self.window = tk.Tk() 8 | self.window.title('计算器') 9 | # 大小 840x540,窗口移动到 距屏幕左侧350,上侧150 的位置 10 | self.window.geometry('599x355+350+150') 11 | # 按钮宽度,统一设置,便于修改 12 | self.btn_width = 10 13 | # 按钮字体大小 14 | self.btn_font = font.Font(size=20) 15 | # frame 便于布局 16 | self.frame = tk.Frame(self.window) 17 | self.display = tk.Frame(self.frame) 18 | self.buttons = tk.Frame(self.frame) 19 | # 显示区域 20 | self.show_exp = tk.Label(self.display, font=self.btn_font, 21 | background='#FCFCFC', width=42, height=2) 22 | self.show = tk.Label(self.display, font=self.btn_font, 23 | background='#FCFCFC', width=42, height=2) 24 | # 按钮 0-9 25 | self.btn_zero = tk.Button(self.buttons, text='0', 26 | font=self.btn_font, width=self.btn_width) 27 | self.btn_one = tk.Button(self.buttons, text='1', 28 | font=self.btn_font, width=self.btn_width) 29 | self.btn_two = tk.Button(self.buttons, text='2', 30 | font=self.btn_font, width=self.btn_width) 31 | self.btn_three = tk.Button(self.buttons, text='3', 32 | font=self.btn_font, width=self.btn_width) 33 | self.btn_four = tk.Button(self.buttons, text='4', 34 | font=self.btn_font, width=self.btn_width) 35 | self.btn_five = tk.Button(self.buttons, text='5', 36 | font=self.btn_font, width=self.btn_width) 37 | self.btn_six = tk.Button(self.buttons, text='6', 38 | font=self.btn_font, width=self.btn_width) 39 | self.btn_seven = tk.Button(self.buttons, text='7', 40 | font=self.btn_font, width=self.btn_width) 41 | self.btn_eight = tk.Button(self.buttons, text='8', 42 | font=self.btn_font, width=self.btn_width) 43 | self.btn_nine = tk.Button(self.buttons, text='9', 44 | font=self.btn_font, width=self.btn_width) 45 | # 按钮 运算符号 46 | self.btn_dot = tk.Button(self.buttons, text='.', 47 | font=self.btn_font, width=self.btn_width) 48 | self.btn_add = tk.Button(self.buttons, text='+', 49 | font=self.btn_font, width=self.btn_width) 50 | self.btn_subtract = tk.Button(self.buttons, text='-', 51 | font=self.btn_font, width=self.btn_width) 52 | self.btn_multiply = tk.Button(self.buttons, text='*', 53 | font=self.btn_font, width=self.btn_width) 54 | self.btn_divide = tk.Button(self.buttons, text='/', 55 | font=self.btn_font, width=self.btn_width) 56 | self.btn_equal = tk.Button(self.buttons, text='=', 57 | font=self.btn_font, width=self.btn_width) 58 | self.btn_negate = tk.Button(self.buttons, text='+/-', 59 | font=self.btn_font, width=self.btn_width) 60 | self.btn_left = tk.Button(self.buttons, text='(', 61 | font=self.btn_font, width=self.btn_width) 62 | self.btn_right = tk.Button(self.buttons, text=')', 63 | font=self.btn_font, width=self.btn_width) 64 | self.btn_clear = tk.Button(self.buttons, text='C', 65 | font=self.btn_font, width=self.btn_width) 66 | 67 | def set_ui(self): 68 | # 放置 frame 69 | self.frame.grid(row=0, column=0) 70 | self.display.grid(row=0, column=0) 71 | self.buttons.grid(row=1, column=0) 72 | # 放置显示区域 73 | self.show_exp.grid(row=0, column=0) 74 | self.show.grid(row=1, column=0) 75 | # 放置 C、(、)、/ 76 | self.btn_clear.grid(row=0, column=0) 77 | self.btn_left.grid(row=0, column=1) 78 | self.btn_right.grid(row=0, column=2) 79 | self.btn_divide.grid(row=0, column=3) 80 | # 放置 7、8、9、* 81 | self.btn_seven.grid(row=1, column=0) 82 | self.btn_eight.grid(row=1, column=1) 83 | self.btn_nine.grid(row=1, column=2) 84 | self.btn_multiply.grid(row=1, column=3) 85 | # 放置 4、5、6、- 86 | self.btn_four.grid(row=2, column=0) 87 | self.btn_five.grid(row=2, column=1) 88 | self.btn_six.grid(row=2, column=2) 89 | self.btn_subtract.grid(row=2, column=3) 90 | # 放置 1、2、3、+ 91 | self.btn_one.grid(row=3, column=0) 92 | self.btn_two.grid(row=3, column=1) 93 | self.btn_three.grid(row=3, column=2) 94 | self.btn_add.grid(row=3, column=3) 95 | # 放置 +/- 、0、. 、= 96 | self.btn_negate.grid(row=4, column=0) 97 | self.btn_zero.grid(row=4, column=1) 98 | self.btn_dot.grid(row=4, column=2) 99 | self.btn_equal.grid(row=4, column=3) 100 | 101 | 102 | if __name__ == '__main__': 103 | # 测试 104 | view = View() 105 | view.set_ui() 106 | view.window.mainloop() 107 | -------------------------------------------------------------------------------- /file_encryption/Python作业-8.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weingxing/python-assigment/5726f3a6bb4f71e91a5802d1670a726c9671362a/file_encryption/Python作业-8.docx -------------------------------------------------------------------------------- /file_encryption/dist/main.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weingxing/python-assigment/5726f3a6bb4f71e91a5802d1670a726c9671362a/file_encryption/dist/main.exe -------------------------------------------------------------------------------- /file_encryption/src/core.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from Cryptodome.Cipher import DES 3 | from Cryptodome.Cipher import AES 4 | 5 | 6 | BLOCK_SIZE = 16 7 | 8 | 9 | # 错误日志记录 10 | def error_log(error): 11 | with open('error_log.txt', 'a') as f: 12 | f.write(str(datetime.datetime.now()) + 13 | ': ' + str(error) + '\n') 14 | 15 | 16 | # 填充数据 17 | def pad(data): 18 | return data + (BLOCK_SIZE - len(data) % BLOCK_SIZE) * \ 19 | chr(BLOCK_SIZE - len(data) % BLOCK_SIZE).encode() 20 | 21 | 22 | # 去掉填充的数据 23 | def un_pad(data): 24 | return data[:-ord(data[len(data) - 1:])] 25 | 26 | 27 | # DES加密——ECB模式 28 | def des_encrypt_ecb(data, key): 29 | try: 30 | data = pad(data) 31 | des = DES.new(key.encode('utf-8'), DES.MODE_ECB) 32 | encrypted_data = des.encrypt(data) 33 | return encrypted_data 34 | except Exception as e: 35 | error_log(e) 36 | return False 37 | 38 | 39 | # DES解密——ECB模式 40 | def des_decrypt_ecb(data, key): 41 | try: 42 | des = DES.new(key.encode('utf-8'), DES.MODE_ECB) 43 | plain_data = des.decrypt(data) 44 | plain_data = un_pad(plain_data) 45 | return plain_data 46 | except Exception as e: 47 | error_log(e) 48 | return False 49 | 50 | 51 | # DES加密——CBC模式 52 | def des_encrypt_cbc(data, key, vi): 53 | try: 54 | data = pad(data) 55 | des = DES.new(key.encode('utf-8'), DES.MODE_CBC, vi.encode('utf-8')) 56 | encrypted_data = des.encrypt(data) 57 | return encrypted_data 58 | except Exception as e: 59 | error_log(e) 60 | return False 61 | 62 | 63 | # DES解密——CBC模式 64 | def des_decrypt_cbc(data, key, vi): 65 | try: 66 | des = DES.new(key.encode('utf-8'), DES.MODE_CBC, vi.encode('utf-8')) 67 | plain_data = des.decrypt(data) 68 | plain_data = un_pad(plain_data) 69 | return plain_data 70 | except Exception as e: 71 | error_log(e) 72 | return False 73 | 74 | 75 | # AES加密——CBC模式 76 | def aes_encrypt_cbc(data, key, vi): 77 | try: 78 | data = pad(data) 79 | cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, vi.encode('utf-8')) 80 | encrypted_data = cipher.encrypt(data) 81 | return encrypted_data 82 | except Exception as e: 83 | error_log(e) 84 | return False 85 | 86 | 87 | # AES解密——CBC模式 88 | def aes_decrypt_cbc(data, key, vi): 89 | try: 90 | cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, vi.encode('utf-8')) 91 | plain_data = cipher.decrypt(data) 92 | plain_data = un_pad(plain_data) 93 | return plain_data 94 | except Exception as e: 95 | error_log(e) 96 | return False 97 | 98 | 99 | # AES加密——ECB模式 100 | def aes_encrypt_ecb(data, key): 101 | try: 102 | key = key.encode('utf-8') 103 | data = pad(data) 104 | cipher = AES.new(key, AES.MODE_ECB) 105 | encrypted_data = cipher.encrypt(data) 106 | return encrypted_data 107 | except Exception as e: 108 | error_log(e) 109 | return False 110 | 111 | 112 | # AES解密——ECB模式 113 | def aes_decrypt_ecb(data, key): 114 | try: 115 | key = key.encode('utf-8') 116 | cipher = AES.new(key, AES.MODE_ECB) 117 | plain_data = cipher.decrypt(data) 118 | plain_data = un_pad(plain_data) 119 | return plain_data 120 | except Exception as e: 121 | error_log(e) 122 | return False 123 | 124 | 125 | if __name__ == '__main__': 126 | data = '123 ' 127 | 128 | print('AES_ECB: ') 129 | en = aes_encrypt_ecb(data.encode(), '1234567812345678') 130 | print(en) 131 | de = aes_decrypt_ecb(en, '1234567812345678') 132 | print(de) 133 | 134 | print('\nDES_ECB: ') 135 | en = des_encrypt_ecb(data.encode(), '12345678') 136 | print(en) 137 | de = des_decrypt_ecb(en, '12345678') 138 | print(de) 139 | 140 | print('\nDES_CBC: ') 141 | en = des_encrypt_cbc(data.encode(), '12345678', '12345678') 142 | print(en) 143 | de = des_decrypt_cbc(en, '12345678', '12345678') 144 | print(de) 145 | 146 | print('\nAES_CBC: ') 147 | en = aes_encrypt_cbc(data.encode(), '12345678123456781234567812345678', '1234567812345678') 148 | print(en) 149 | de = aes_decrypt_cbc(en, '12345678123456781234567812345678', '123456781234567812345678') 150 | print(de) 151 | -------------------------------------------------------------------------------- /file_encryption/src/main.py: -------------------------------------------------------------------------------- 1 | from view import MainWindow 2 | import sys 3 | from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox, QFileDialog 4 | from PyQt5.QtCore import QDir 5 | import core 6 | from core import error_log 7 | import time 8 | 9 | 10 | class Main(QWidget, MainWindow): 11 | def __init__(self): 12 | super(Main, self).__init__() 13 | self.setupUi(self) 14 | self.slot_init() 15 | 16 | # 连接槽函数 17 | def slot_init(self): 18 | self.combo_model.currentTextChanged.connect(self.change_state) 19 | self.btn_src.clicked.connect(lambda: self.chose_file(flag='src')) 20 | self.btn_dist.clicked.connect(lambda: self.chose_file(flag='dist')) 21 | self.btn_encrypt.clicked.connect(self.do_encrypt) 22 | self.btn_decrypt.clicked.connect(self.do_decrypt) 23 | self.btn_close.clicked.connect(self.close) 24 | 25 | # 读取数据 26 | def read_data(self, file): 27 | try: 28 | with open(file, 'rb') as f: 29 | return f.read() 30 | except Exception as e: 31 | error_log(e) 32 | QMessageBox.critical(self, '错误', '读取文件时出错') 33 | 34 | # 写入数据 35 | def write_data(self, file, data): 36 | try: 37 | with open(file, 'wb') as f: 38 | f.write(data) 39 | return True 40 | except Exception as e: 41 | error_log(e) 42 | QMessageBox.critical(self, '错误', '写入文件时出错') 43 | 44 | # 更改 偏移量编辑框 的可用状态 45 | def change_state(self): 46 | model = self.combo_model.currentText() 47 | if model == 'ECB': 48 | self.edit_vi.setEnabled(False) 49 | self.edit_vi.setText('ECB模式无需偏移量') 50 | else: 51 | self.edit_vi.setEnabled(True) 52 | self.edit_vi.clear() 53 | 54 | # 进行加密 55 | def do_encrypt(self): 56 | method = self.combo_method.currentText() 57 | model = self.combo_model.currentText() 58 | key = self.edit_key.text() 59 | vi = self.edit_vi.text() 60 | src = self.edit_src.text() 61 | dist = self.edit_dist.text() 62 | result = self.encrypt(src, dist, method, model, key, vi) 63 | if not result: 64 | QMessageBox.information(self, '提示', '加密失败') 65 | return 66 | file = dist + '/' + src.split('/')[-1] + '.data' 67 | if self.write_data(file, result): 68 | QMessageBox.information(self, '提示', f'加密成功,文件路径:\n {file}') 69 | 70 | # 进行解密 71 | def do_decrypt(self): 72 | method = self.combo_method.currentText() 73 | model = self.combo_model.currentText() 74 | key = self.edit_key.text() 75 | vi = self.edit_vi.text() 76 | src = self.edit_src.text() 77 | dist = self.edit_dist.text() 78 | result = self.decrypt(src, dist, method, model, key, vi) 79 | if not result: 80 | QMessageBox.information(self, '提示', '解密失败') 81 | return 82 | try: 83 | # 文件后缀 84 | ext = src.split('/')[-1].split('.')[-2] 85 | file = dist + '/' + src.split('/')[-1] + '.' + ext 86 | if self.write_data(file, result): 87 | QMessageBox.information(self, '提示', f'解密成功,文件路径:\n {file}') 88 | except: 89 | file = dist + '/decrypt' + src.split('/')[-1] 90 | if self.write_data(file, result): 91 | QMessageBox.information(self, '提示', f'解密成功,文件路径:\n {file}\n' 92 | f'请自行修改文件后缀') 93 | 94 | # 浏览选择文件/文件夹,flag用于判断是选择文件,还是选择文件夹 95 | def chose_file(self, flag='src'): 96 | dig = QFileDialog() 97 | path = None 98 | if flag == 'src': 99 | # 设置可以打开任何文件 100 | dig.setFileMode(QFileDialog.AnyFile) 101 | # 文件过滤 102 | dig.setFilter(QDir.Files) 103 | if dig.exec_(): 104 | path = dig.selectedFiles()[0] 105 | self.edit_src.setText(path) 106 | else: 107 | path = dig.getExistingDirectory(self) 108 | self.edit_dist.setText(path) 109 | 110 | # 对选择的算法等进行校验,不通过则弹窗提示 111 | def check(self, src, dist, method, model, key, vi): 112 | if src == '': 113 | QMessageBox.information(self, '提示', '请输入输入路径') 114 | return False 115 | if dist == '': 116 | QMessageBox.information(self, '提示', '请输入输出路径') 117 | return False 118 | # 校验加密算法的选择 119 | if method == '请选择': 120 | QMessageBox.information(self, '提示', '请选择加密算法') 121 | return False 122 | # 校验加密模式的选择 123 | if model == '请选择': 124 | QMessageBox.information(self, '提示', '请选择加密模式') 125 | return False 126 | # 校验密钥 127 | if key == '': 128 | QMessageBox.information(self, '提示', '请输入加密密钥') 129 | return False 130 | elif method == 'DES' and len(key) != 8: 131 | QMessageBox.information(self, '提示', 'DES算法密钥长度位8位') 132 | return False 133 | elif method == 'AES' and len(key) not in [16, 24, 32]: 134 | QMessageBox.information(self, '提示', 'AES算法密钥长度位16/24/32位') 135 | return False 136 | # 校验偏移量 137 | if model == 'CBC': 138 | if vi == '': 139 | QMessageBox.information(self, '提示', '请输入偏移量') 140 | return False 141 | elif method == 'DES' and len(vi) != 8: 142 | QMessageBox.information(self, '提示', 'DES算法偏移量长度位8位') 143 | return False 144 | elif method == 'AES' and len(vi) != 16: 145 | QMessageBox.information(self, '提示', 'AES算法偏移量长度位16位') 146 | return False 147 | return True 148 | 149 | # 加密 150 | def encrypt(self, src, dist, method, model, key, vi): 151 | if not self.check(src, dist, method, model, key, vi): 152 | return 153 | data = self.read_data(src) 154 | if method == 'AES': 155 | if model == 'CBC': 156 | result = core.aes_encrypt_cbc(data, key, vi) 157 | else: 158 | result = core.aes_encrypt_ecb(data, key) 159 | else: 160 | if model == 'CBC': 161 | result = core.des_encrypt_cbc(data, key, vi) 162 | else: 163 | result = core.des_encrypt_ecb(data, key) 164 | return result 165 | 166 | # 解密 167 | def decrypt(self, src, dist, method, model, key, vi): 168 | if not self.check(src, dist, method, model, key, vi): 169 | return 170 | data = self.read_data(src) 171 | if method == 'AES': 172 | if model == 'CBC': 173 | result = core.aes_decrypt_cbc(data, key, vi) 174 | else: 175 | result = core.aes_decrypt_ecb(data, key) 176 | else: 177 | if model == 'CBC': 178 | result = core.des_decrypt_cbc(data, key, vi) 179 | else: 180 | result = core.des_decrypt_ecb(data, key) 181 | return result 182 | 183 | 184 | if __name__ == '__main__': 185 | app = QApplication(sys.argv) 186 | main = Main() 187 | main.show() 188 | sys.exit(app.exec_()) 189 | -------------------------------------------------------------------------------- /file_encryption/src/requirements.txt: -------------------------------------------------------------------------------- 1 | altgraph==0.17 2 | future==0.18.2 3 | pefile==2019.4.18 4 | pyasn1==0.4.8 5 | pycryptodome==3.9.7 6 | pycryptodomex==3.9.7 7 | PyInstaller==3.6 8 | PyQt5==5.14.2 9 | PyQt5-sip==12.7.2 10 | PyQt5-stubs==5.14.2.0 11 | pywin32-ctypes==0.2.0 12 | rsa==4.0 13 | -------------------------------------------------------------------------------- /file_encryption/src/view.py: -------------------------------------------------------------------------------- 1 | from PyQt5 import QtCore, QtGui, QtWidgets 2 | 3 | 4 | class MainWindow(object): 5 | def setupUi(self, MainWindow): 6 | MainWindow.setObjectName("MainWindow") 7 | MainWindow.resize(800, 600) 8 | self.centralwidget = QtWidgets.QWidget(MainWindow) 9 | self.centralwidget.setObjectName("centralwidget") 10 | self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget) 11 | self.verticalLayoutWidget.setGeometry(QtCore.QRect(150, 40, 331, 121)) 12 | self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") 13 | self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget) 14 | self.verticalLayout.setContentsMargins(0, 0, 0, 0) 15 | self.verticalLayout.setObjectName("verticalLayout") 16 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout() 17 | self.horizontalLayout_2.setObjectName("horizontalLayout_2") 18 | self.label_method = QtWidgets.QLabel(self.verticalLayoutWidget) 19 | self.label_method.setObjectName("label_method") 20 | self.horizontalLayout_2.addWidget(self.label_method) 21 | self.combo_method = QtWidgets.QComboBox(self.verticalLayoutWidget) 22 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) 23 | sizePolicy.setHorizontalStretch(0) 24 | sizePolicy.setVerticalStretch(0) 25 | sizePolicy.setHeightForWidth(self.combo_method.sizePolicy().hasHeightForWidth()) 26 | self.combo_method.setSizePolicy(sizePolicy) 27 | self.combo_method.setObjectName("combo_method") 28 | self.combo_method.addItem("") 29 | self.combo_method.addItem("") 30 | self.combo_method.addItem("") 31 | self.horizontalLayout_2.addWidget(self.combo_method) 32 | self.verticalLayout.addLayout(self.horizontalLayout_2) 33 | self.horizontalLayout = QtWidgets.QHBoxLayout() 34 | self.horizontalLayout.setObjectName("horizontalLayout") 35 | self.label_model = QtWidgets.QLabel(self.verticalLayoutWidget) 36 | self.label_model.setObjectName("label_model") 37 | self.horizontalLayout.addWidget(self.label_model) 38 | self.combo_model = QtWidgets.QComboBox(self.verticalLayoutWidget) 39 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) 40 | sizePolicy.setHorizontalStretch(0) 41 | sizePolicy.setVerticalStretch(0) 42 | sizePolicy.setHeightForWidth(self.combo_model.sizePolicy().hasHeightForWidth()) 43 | self.combo_model.setSizePolicy(sizePolicy) 44 | self.combo_model.setObjectName("combo_model") 45 | self.combo_model.addItem("") 46 | self.combo_model.addItem("") 47 | self.combo_model.addItem("") 48 | self.horizontalLayout.addWidget(self.combo_model) 49 | self.verticalLayout.addLayout(self.horizontalLayout) 50 | self.horizontalLayout_3 = QtWidgets.QHBoxLayout() 51 | self.horizontalLayout_3.setObjectName("horizontalLayout_3") 52 | self.label_key = QtWidgets.QLabel(self.verticalLayoutWidget) 53 | self.label_key.setObjectName("label_key") 54 | self.horizontalLayout_3.addWidget(self.label_key) 55 | self.edit_key = QtWidgets.QLineEdit(self.verticalLayoutWidget) 56 | self.edit_key.setObjectName("edit_key") 57 | self.horizontalLayout_3.addWidget(self.edit_key) 58 | self.verticalLayout.addLayout(self.horizontalLayout_3) 59 | self.horizontalLayout_6 = QtWidgets.QHBoxLayout() 60 | self.horizontalLayout_6.setObjectName("horizontalLayout_6") 61 | self.label_vi = QtWidgets.QLabel(self.verticalLayoutWidget) 62 | self.label_vi.setObjectName("label_vi") 63 | self.horizontalLayout_6.addWidget(self.label_vi) 64 | self.edit_vi = QtWidgets.QLineEdit(self.verticalLayoutWidget) 65 | self.edit_vi.setObjectName("edit_vi") 66 | self.horizontalLayout_6.addWidget(self.edit_vi) 67 | self.verticalLayout.addLayout(self.horizontalLayout_6) 68 | self.verticalLayoutWidget_4 = QtWidgets.QWidget(self.centralwidget) 69 | self.verticalLayoutWidget_4.setGeometry(QtCore.QRect(70, 220, 511, 121)) 70 | self.verticalLayoutWidget_4.setObjectName("verticalLayoutWidget_4") 71 | self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_4) 72 | self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) 73 | self.verticalLayout_2.setObjectName("verticalLayout_2") 74 | self.horizontalLayout_5 = QtWidgets.QHBoxLayout() 75 | self.horizontalLayout_5.setObjectName("horizontalLayout_5") 76 | self.label_src = QtWidgets.QLabel(self.verticalLayoutWidget_4) 77 | self.label_src.setObjectName("label_src") 78 | self.horizontalLayout_5.addWidget(self.label_src) 79 | self.edit_src = QtWidgets.QLineEdit(self.verticalLayoutWidget_4) 80 | self.edit_src.setObjectName("edit_src") 81 | self.horizontalLayout_5.addWidget(self.edit_src) 82 | self.btn_src = QtWidgets.QPushButton(self.verticalLayoutWidget_4) 83 | self.btn_src.setObjectName("btn_src") 84 | self.horizontalLayout_5.addWidget(self.btn_src) 85 | self.verticalLayout_2.addLayout(self.horizontalLayout_5) 86 | self.horizontalLayout_4 = QtWidgets.QHBoxLayout() 87 | self.horizontalLayout_4.setObjectName("horizontalLayout_4") 88 | self.label_dist = QtWidgets.QLabel(self.verticalLayoutWidget_4) 89 | self.label_dist.setObjectName("label_dist") 90 | self.horizontalLayout_4.addWidget(self.label_dist) 91 | self.edit_dist = QtWidgets.QLineEdit(self.verticalLayoutWidget_4) 92 | self.edit_dist.setObjectName("edit_dist") 93 | self.horizontalLayout_4.addWidget(self.edit_dist) 94 | self.btn_dist = QtWidgets.QPushButton(self.verticalLayoutWidget_4) 95 | self.btn_dist.setObjectName("btn_dist") 96 | self.horizontalLayout_4.addWidget(self.btn_dist) 97 | self.verticalLayout_2.addLayout(self.horizontalLayout_4) 98 | self.verticalLayoutWidget_5 = QtWidgets.QWidget(self.centralwidget) 99 | self.verticalLayoutWidget_5.setGeometry(QtCore.QRect(640, 180, 131, 211)) 100 | self.verticalLayoutWidget_5.setObjectName("verticalLayoutWidget_5") 101 | self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_5) 102 | self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) 103 | self.verticalLayout_3.setObjectName("verticalLayout_3") 104 | self.btn_encrypt = QtWidgets.QPushButton(self.verticalLayoutWidget_5) 105 | self.btn_encrypt.setObjectName("btn_encrypt") 106 | self.verticalLayout_3.addWidget(self.btn_encrypt) 107 | self.btn_decrypt = QtWidgets.QPushButton(self.verticalLayoutWidget_5) 108 | self.btn_decrypt.setObjectName("btn_decrypt") 109 | self.verticalLayout_3.addWidget(self.btn_decrypt) 110 | self.btn_close = QtWidgets.QPushButton(self.verticalLayoutWidget_5) 111 | self.btn_close.setObjectName("btn_close") 112 | self.verticalLayout_3.addWidget(self.btn_close) 113 | self.line_top = QtWidgets.QFrame(self.centralwidget) 114 | self.line_top.setGeometry(QtCore.QRect(29, 12, 571, 16)) 115 | self.line_top.setFrameShape(QtWidgets.QFrame.HLine) 116 | self.line_top.setFrameShadow(QtWidgets.QFrame.Sunken) 117 | self.line_top.setObjectName("line_top") 118 | self.line_bottom = QtWidgets.QFrame(self.centralwidget) 119 | self.line_bottom.setGeometry(QtCore.QRect(29, 570, 571, 20)) 120 | self.line_bottom.setFrameShape(QtWidgets.QFrame.HLine) 121 | self.line_bottom.setFrameShadow(QtWidgets.QFrame.Sunken) 122 | self.line_bottom.setObjectName("line_bottom") 123 | self.line_left = QtWidgets.QFrame(self.centralwidget) 124 | self.line_left.setGeometry(QtCore.QRect(20, 20, 20, 560)) 125 | self.line_left.setFrameShape(QtWidgets.QFrame.VLine) 126 | self.line_left.setFrameShadow(QtWidgets.QFrame.Sunken) 127 | self.line_left.setObjectName("line_left") 128 | self.line_center = QtWidgets.QFrame(self.centralwidget) 129 | self.line_center.setGeometry(QtCore.QRect(30, 170, 571, 16)) 130 | self.line_center.setFrameShape(QtWidgets.QFrame.HLine) 131 | self.line_center.setFrameShadow(QtWidgets.QFrame.Sunken) 132 | self.line_center.setObjectName("line_center") 133 | self.line_right = QtWidgets.QFrame(self.centralwidget) 134 | self.line_right.setGeometry(QtCore.QRect(594, 19, 15, 561)) 135 | self.line_right.setFrameShape(QtWidgets.QFrame.VLine) 136 | self.line_right.setFrameShadow(QtWidgets.QFrame.Sunken) 137 | self.line_right.setObjectName("line_right") 138 | self.label_tips = QtWidgets.QLabel(self.centralwidget) 139 | self.label_tips.setGeometry(QtCore.QRect(80, 380, 481, 161)) 140 | font = QtGui.QFont() 141 | font.setFamily("宋体") 142 | font.setPointSize(10) 143 | self.label_tips.setFont(font) 144 | self.label_tips.setStyleSheet("border: 1px solid #c2c2c2;") 145 | self.label_tips.setTextFormat(QtCore.Qt.AutoText) 146 | self.label_tips.setIndent(-1) 147 | self.label_tips.setObjectName("label_tips") 148 | # MainWindow.setCentralWidget(self.centralwidget) 149 | 150 | self.retranslateUi(MainWindow) 151 | QtCore.QMetaObject.connectSlotsByName(MainWindow) 152 | MainWindow.setTabOrder(self.combo_method, self.combo_model) 153 | MainWindow.setTabOrder(self.combo_model, self.edit_key) 154 | MainWindow.setTabOrder(self.edit_key, self.edit_vi) 155 | MainWindow.setTabOrder(self.edit_vi, self.edit_src) 156 | MainWindow.setTabOrder(self.edit_src, self.btn_src) 157 | MainWindow.setTabOrder(self.btn_src, self.edit_dist) 158 | MainWindow.setTabOrder(self.edit_dist, self.btn_dist) 159 | MainWindow.setTabOrder(self.btn_dist, self.btn_encrypt) 160 | MainWindow.setTabOrder(self.btn_encrypt, self.btn_decrypt) 161 | MainWindow.setTabOrder(self.btn_decrypt, self.btn_close) 162 | 163 | def retranslateUi(self, MainWindow): 164 | _translate = QtCore.QCoreApplication.translate 165 | MainWindow.setWindowTitle(_translate("MainWindow", "文件加密")) 166 | self.label_method.setText(_translate("MainWindow", "加密算法:")) 167 | self.combo_method.setItemText(0, _translate("MainWindow", "请选择")) 168 | self.combo_method.setItemText(1, _translate("MainWindow", "DES")) 169 | self.combo_method.setItemText(2, _translate("MainWindow", "AES")) 170 | self.label_model.setText(_translate("MainWindow", "加密模式:")) 171 | self.combo_model.setItemText(0, _translate("MainWindow", "请选择")) 172 | self.combo_model.setItemText(1, _translate("MainWindow", "ECB")) 173 | self.combo_model.setItemText(2, _translate("MainWindow", "CBC")) 174 | self.label_key.setText(_translate("MainWindow", "加密密钥:")) 175 | self.label_vi.setText(_translate("MainWindow", "偏 移 量:")) 176 | self.label_src.setText(_translate("MainWindow", "输入路径:")) 177 | self.btn_src.setText(_translate("MainWindow", "...")) 178 | self.label_dist.setText(_translate("MainWindow", "输出路径:")) 179 | self.btn_dist.setText(_translate("MainWindow", "...")) 180 | self.btn_encrypt.setText(_translate("MainWindow", "加 密")) 181 | self.btn_decrypt.setText(_translate("MainWindow", "解 密")) 182 | self.btn_close.setText(_translate("MainWindow", "退 出")) 183 | self.label_tips.setText(_translate("MainWindow", "Tips\n""\n" 184 | "输入路径为要加密的文件\n" 185 | "输出路径为加密后文件的存储位置\n""\n" 186 | "DES加密密钥为8位,CBC模式下偏移量为8位\n" 187 | "AES加密密钥位16/24/32位,CBC模式下偏移量长度为16位")) 188 | -------------------------------------------------------------------------------- /student/ScreenShot/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weingxing/python-assigment/5726f3a6bb4f71e91a5802d1670a726c9671362a/student/ScreenShot/login.png -------------------------------------------------------------------------------- /student/ScreenShot/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weingxing/python-assigment/5726f3a6bb4f71e91a5802d1670a726c9671362a/student/ScreenShot/main.png -------------------------------------------------------------------------------- /student/ScreenShot/main1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weingxing/python-assigment/5726f3a6bb4f71e91a5802d1670a726c9671362a/student/ScreenShot/main1.png -------------------------------------------------------------------------------- /student/ScreenShot/register.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weingxing/python-assigment/5726f3a6bb4f71e91a5802d1670a726c9671362a/student/ScreenShot/register.png -------------------------------------------------------------------------------- /student/dist/config.ini: -------------------------------------------------------------------------------- 1 | [DataBase] 2 | host = 127.0.0.1 3 | port = 3306 4 | user = root 5 | password = 123456 6 | db = student -------------------------------------------------------------------------------- /student/dist/login.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weingxing/python-assigment/5726f3a6bb4f71e91a5802d1670a726c9671362a/student/dist/login.exe -------------------------------------------------------------------------------- /student/dist/welcome.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weingxing/python-assigment/5726f3a6bb4f71e91a5802d1670a726c9671362a/student/dist/welcome.gif -------------------------------------------------------------------------------- /student/src/config.ini: -------------------------------------------------------------------------------- 1 | [DataBase] 2 | host = 127.0.0.1 3 | port = 3306 4 | user = root 5 | password = 123456 6 | db = student -------------------------------------------------------------------------------- /student/src/database.py: -------------------------------------------------------------------------------- 1 | import pymysql as mysql 2 | import configparser 3 | 4 | 5 | class DB: 6 | """ 7 | 数据库类,提供 增删改查 操作 8 | @author weingxing 9 | @since 2020/04/02 10 | """ 11 | def __init__(self): 12 | try: 13 | # 解析配置文件 14 | configer = configparser.ConfigParser() 15 | configer.read('config.ini') 16 | database = configer['DataBase'] 17 | # 取得数据库连接 18 | self.conn = mysql.connect(host=database['host'], port=int(database['port']), 19 | user=database['user'], password=database['password'], 20 | database=database['db'], charset='utf8') 21 | # 获取游标 22 | self.cursor = self.conn.cursor() 23 | except mysql.Error: 24 | raise Exception('数据库连接失败') 25 | except configparser.Error: 26 | raise Exception('配置文件解析失败') 27 | 28 | # SQL 语句 29 | self._select = 'SELECT * FROM info;' 30 | self._insert = 'INSERT INTO info(sno, name, sex, college, clazz) VALUES(%s, %s, %s, %s, %s);' 31 | self._delete = 'DELETE FROM info WHERE sno=%s;' 32 | self._update = 'UPDATE info SET name=%s, sex=%s, college=%s, clazz=%s WHERE sno=%s;' 33 | self._search = 'SELECT * FROM info WHERE sno LIKE %s OR name LIKE %s OR sex LIKE %s' \ 34 | 'OR college LIKE %s OR clazz LIKE %s;' 35 | self._login = 'SELECT * FROM user;' 36 | self._register = 'INSERT INTO user(username, password) VALUES(%s, %s);' 37 | 38 | def __del__(self): 39 | try: 40 | self.cursor.close() 41 | self.conn.close() 42 | except mysql.Error: 43 | pass 44 | 45 | # 添加信息 46 | def insert(self, **kwargs): 47 | try: 48 | param = [kwargs['sno'], kwargs['name'], kwargs['sex'], kwargs['college'], kwargs['clazz']] 49 | self.cursor.execute(self._insert, param) 50 | self.conn.commit() 51 | return True 52 | except mysql.Error: 53 | return False 54 | 55 | # 查找全部信息 56 | def select(self): 57 | try: 58 | self.cursor.execute(self._select) 59 | result = self.cursor.fetchall() 60 | return result 61 | except mysql.Error: 62 | return None 63 | 64 | # 按学号删除信息 65 | def delete(self, sno): 66 | try: 67 | self.cursor.execute(self._delete, [sno]) 68 | self.conn.commit() 69 | effect_row = self.cursor.rowcount 70 | if effect_row > 0: 71 | return True 72 | return False 73 | except mysql.Error: 74 | return False 75 | 76 | # 按学号修改信息 77 | def update(self, **kwargs): 78 | try: 79 | param = [kwargs['name'], kwargs['sex'], kwargs['college'], kwargs['clazz'], kwargs['sno']] 80 | self.cursor.execute(self._update, param) 81 | self.conn.commit() 82 | return True 83 | except mysql.Error: 84 | return False 85 | 86 | # 按关键词查找信息 87 | def search(self, key): 88 | try: 89 | param = ['%' + str(key) + '%' for i in range(5)] 90 | self.cursor.execute(self._search, param) 91 | result = self.cursor.fetchall() 92 | return result 93 | except mysql.Error: 94 | return None 95 | 96 | def login(self): 97 | try: 98 | self.cursor.execute(self._login) 99 | result = self.cursor.fetchall() 100 | return result 101 | except mysql.Error: 102 | return None 103 | 104 | def register(self, **kwargs): 105 | try: 106 | param = [kwargs['username'], kwargs['password']] 107 | self.cursor.execute(self._register, param) 108 | self.conn.commit() 109 | return True 110 | except mysql.Error: 111 | return False 112 | 113 | 114 | if __name__ == '__main__': 115 | # 测试 116 | db = DB() 117 | db.insert(sno='1', name='张三', sex='男', college='信息科学技术学院', clazz='计科181') 118 | print(db.select()) 119 | db.update(sno='1', name='张三', sex='女', college='信息科学技术学院', clazz='计科181') 120 | print(db.search('张')) 121 | print(db.search('123')) 122 | -------------------------------------------------------------------------------- /student/src/gui.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | import tkinter.messagebox 3 | import _thread 4 | from view import View 5 | from database import DB 6 | 7 | 8 | class GUI: 9 | """ 10 | 用户接口类,将视图与逻辑对接 11 | @author weingxing 12 | @since 2020/04/02 13 | """ 14 | 15 | def __init__(self): 16 | self.db = DB() 17 | self.view = View() 18 | self.view.set_ui() 19 | 20 | self.sno = tk.StringVar() 21 | self.name = tk.StringVar() 22 | self.sex = tk.StringVar() 23 | self.college = tk.StringVar() 24 | self.clazz = tk.StringVar() 25 | 26 | self.connect() 27 | 28 | # 添加信息 29 | def add(self, event): 30 | sno = self.view._entry_sno.get() 31 | name = self.view._entry_name.get() 32 | sex = self.view._entry_sex.get() 33 | college = self.view._entry_college.get() 34 | clazz = self.view._entry_clazz.get() 35 | # 判断是否输入数据 36 | if sno != '' and name != '' and sex != '' and college != '' and clazz != '': 37 | if self.db.insert(sno=sno, name=name, sex=sex, college=college, clazz=clazz): 38 | values = (sno, name, sex, college, clazz) 39 | self.view.data_list.insert('', 'end', values=values) 40 | # 新建线程进行弹窗,否则按钮按点击后无法弹起来 41 | _thread.start_new_thread(lambda: tk.messagebox.showinfo(message='添加成功'), ()) 42 | else: 43 | _thread.start_new_thread(lambda: tk.messagebox.showinfo(message='添加失败'), ()) 44 | # 清空输入框 45 | self.view._entry_sno.delete(0, len(sno)) 46 | self.view._entry_name.delete(0, len(name)) 47 | self.view._entry_sex.delete(0, len(sex)) 48 | self.view._entry_college.delete(0, len(college)) 49 | self.view._entry_clazz.delete(0, len(clazz)) 50 | else: 51 | _thread.start_new_thread(lambda: tk.messagebox.showinfo(message='请输入信息'), ()) 52 | 53 | def update(self, event): 54 | sno = self.view.entry_sno.get() 55 | name = self.view.entry_name.get() 56 | sex = self.view.entry_sex.get() 57 | college = self.view.entry_college.get() 58 | clazz = self.view.entry_clazz.get() 59 | if sno != '' and name != '' and sex != '' and college != '' and clazz != '': 60 | if self.db.update(sno=sno, name=name, sex=sex, college=college, clazz=clazz): 61 | # 从数据库成功更新后更新界面,要传入event,这里使用 '' 代替 62 | self.refresh('') 63 | _thread.start_new_thread(lambda: tk.messagebox.showinfo(message='更新成功'), ()) 64 | else: 65 | _thread.start_new_thread(lambda: tk.messagebox.showinfo(message='更新失败'), ()) 66 | else: 67 | _thread.start_new_thread(lambda: tk.messagebox.showinfo(message='请补全信息'), ()) 68 | 69 | def delete(self, event): 70 | sno = self.view.entry_sno.get() 71 | if sno != '': 72 | if self.db.delete(sno): 73 | # 从数据库成功删除后更新界面 74 | self.refresh('') 75 | # 清空信息 76 | self.sno.set('') 77 | self.view.entry_sno.configure(textvariable=self.sno) 78 | self.view.entry_name.delete(0, len(self.name.get())) 79 | self.view.entry_sex.delete(0, len(self.sex.get())) 80 | self.view.entry_college.delete(0, len(self.college.get())) 81 | self.view.entry_clazz.delete(0, len(self.clazz.get())) 82 | _thread.start_new_thread(lambda: tk.messagebox.showinfo(message='删除成功'), ()) 83 | else: 84 | _thread.start_new_thread(lambda: tk.messagebox.showinfo(message='删除失败'), ()) 85 | else: 86 | _thread.start_new_thread(lambda: tk.messagebox.showinfo(message='未选中数据'), ()) 87 | 88 | def search(self, event): 89 | key = self.view.entry_search.get() 90 | try: 91 | assert not key == '' 92 | # 清空现有数据 93 | x = self.view.data_list.get_children() 94 | for item in x: 95 | self.view.data_list.delete(item) 96 | 97 | result = self.db.search(key) 98 | assert not result is None 99 | i = 0 100 | for d in result: 101 | self.view.data_list.insert('', i, value=d) 102 | i += 1 103 | except AssertionError: 104 | _thread.start_new_thread(lambda: tk.messagebox.showinfo(message='搜索失败'), ()) 105 | 106 | def refresh(self, event): 107 | try: 108 | # 清空现有数据 109 | x = self.view.data_list.get_children() 110 | for item in x: 111 | self.view.data_list.delete(item) 112 | 113 | result = self.db.select() 114 | assert not result is None 115 | i = 0 116 | for d in result: 117 | self.view.data_list.insert('', i, value=d) 118 | i += 1 119 | except AssertionError: 120 | _thread.start_new_thread(lambda: tk.messagebox.showinfo(message='刷新失败'), ()) 121 | 122 | def detail(self, event): 123 | if len(self.view.data_list.selection()) != 0: 124 | item = self.view.data_list.selection()[0] 125 | values = self.view.data_list.item(item, "values") 126 | self.sno.set(values[0]) 127 | self.name.set(values[1]) 128 | self.sex.set(values[2]) 129 | self.college.set(values[3]) 130 | self.clazz.set(values[4]) 131 | 132 | self.view.entry_sno.configure(textvariable=self.sno) 133 | self.view.entry_name.configure(textvariable=self.name) 134 | self.view.entry_sex.configure(textvariable=self.sex) 135 | self.view.entry_college.configure(textvariable=self.college) 136 | self.view.entry_clazz.configure(textvariable=self.clazz) 137 | 138 | def connect(self): 139 | self.view.btn_add.bind('', self.add) 140 | self.view.btn_update.bind('', self.update) 141 | self.view.btn_delete.bind('', self.delete) 142 | self.view.btn_search.bind('', self.search) 143 | self.view.btn_refresh.bind('', self.refresh) 144 | self.view.data_list.bind('', self.detail) 145 | 146 | def start(self): 147 | data = self.db.select() 148 | i = 0 149 | for d in data: 150 | self.view.data_list.insert('', i, value=d) 151 | i += 1 152 | self.view.window.mainloop() 153 | 154 | 155 | if __name__ == '__main__': 156 | gui = GUI() 157 | gui.start() 158 | -------------------------------------------------------------------------------- /student/src/login.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | import _tkinter 3 | from tkinter import messagebox 4 | from database import DB 5 | from gui import GUI 6 | 7 | 8 | class Login: 9 | """ 10 | 登录界面及逻辑,程序以此文件为入口 11 | @author weingxing 12 | @since 2020/04/02 13 | """ 14 | 15 | def __init__(self): 16 | self.db = DB() 17 | 18 | self.window = tk.Tk() 19 | self.window.title('学生管理系统') 20 | self.window.geometry('450x300+500+200') 21 | 22 | # 加载图片 23 | try: 24 | self.canvas = tk.Canvas(self.window, height=200, width=500) 25 | self.image_file = tk.PhotoImage(file='welcome.gif') 26 | self.image = self.canvas.create_image(0, 0, anchor='nw', image=self.image_file) 27 | self.canvas.pack(side='top') 28 | except _tkinter.TclError: 29 | tk.messagebox.showinfo(message='图片丢失') 30 | 31 | # 以下定义登录控件 32 | # 定义用户名和密码标签,并直接布局到界面 33 | tk.Label(self.window, text='用户名:').place(x=100, y=150) 34 | tk.Label(self.window, text='密 码:').place(x=100, y=190) 35 | # 定义用户名和密码输入框并布局到界面 36 | self.entry_user = tk.Entry(self.window) 37 | self.entry_user.place(x=160, y=150) 38 | self.entry_pwd = tk.Entry(self.window, show='*') 39 | self.entry_pwd.place(x=160, y=190) 40 | # 定义登录和注册按钮并布局到界面 41 | tk.Button(self.window, text='登 录', width=5, 42 | command=self.login, relief=tk.GROOVE).place(x=170, y=230) 43 | tk.Button(self.window, text='注 册', width=5, 44 | command=self.register, relief=tk.GROOVE).place(x=270, y=230) 45 | 46 | self.flag = False 47 | 48 | def login(self): 49 | _user = self.entry_user.get() 50 | _pwd = self.entry_pwd.get() 51 | users = self.db.login() 52 | # 校验用户名和密码 53 | if len(users) > 0: 54 | for user in users: 55 | if _user == user[1] and _pwd == user[2]: 56 | # 销毁登录界面 57 | self.window.destroy() 58 | # 加载主界面 59 | GUI().start() 60 | self.flag = True 61 | break 62 | 63 | if not self.flag: 64 | tk.messagebox.showinfo(message='用户名或密码错误') 65 | else: 66 | tk.messagebox.showinfo(message='无法登录') 67 | 68 | def register(self): 69 | # 向数据库添加用户 70 | def add_user(): 71 | # 获取输入 72 | username = self._entry_username.get() 73 | password = self._entry_pwd.get() 74 | confirm_password = self._entry_pwd_confirm.get() 75 | # 判断输入 76 | if username != '' and password != '' and confirm_password != '': 77 | if password == confirm_password: 78 | if self.db.register(username=username, password=password): 79 | tk.messagebox.showinfo(message='注册成功') 80 | else: 81 | tk.messagebox.showinfo(message='注册失败') 82 | else: 83 | tk.messagebox.showinfo(message='两次输入的密码不一致') 84 | else: 85 | tk.messagebox.showinfo(message='请补全信息') 86 | 87 | # 定义注册界面 88 | self.window_register = tk.Toplevel(self.window) 89 | self.window_register.geometry('350x200+560+260') 90 | self.window_register.title('账号注册') 91 | # 输入框定义 92 | self._entry_username = tk.Entry(self.window_register) 93 | self._entry_pwd = tk.Entry(self.window_register, show='*') 94 | self._entry_pwd_confirm = tk.Entry(self.window_register, show='*') 95 | # 布局 96 | tk.Label(self.window_register, text='用户名: ').place(x=80, y=10) 97 | self._entry_username.place(x=140, y=10) 98 | tk.Label(self.window_register, text='密 码: ').place(x=85, y=50) 99 | self._entry_pwd.place(x=140, y=50) 100 | tk.Label(self.window_register, text='确认密码: ').place(x=70, y=90) 101 | self._entry_pwd_confirm.place(x=140, y=90) 102 | # 按钮 103 | self.btn_register = tk.Button(self.window_register, text='注 册', 104 | width=15, command=add_user, relief=tk.GROOVE).place(x=140, y=130) 105 | 106 | def start(self): 107 | self.window.mainloop() 108 | 109 | 110 | if __name__ == '__main__': 111 | login = Login() 112 | login.start() 113 | -------------------------------------------------------------------------------- /student/src/student.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Data Transfer 3 | 4 | Source Server : 127.0.0.1 5 | Source Server Type : MySQL 6 | Source Server Version : 50647 7 | Source Host : 127.0.0.1:3306 8 | Source Schema : student 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 50647 12 | File Encoding : 65001 13 | 14 | Date: 02/04/2020 20:46:33 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for info 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `info`; 24 | CREATE TABLE `info` ( 25 | `sno` int(11) NOT NULL, 26 | `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 27 | `sex` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 28 | `college` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 29 | `clazz` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 30 | PRIMARY KEY (`sno`) USING BTREE 31 | ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; 32 | 33 | -- ---------------------------- 34 | -- Records of info 35 | -- ---------------------------- 36 | INSERT INTO `info` VALUES (1, '张三', '女', '信息科学技术学院', '计科181'); 37 | INSERT INTO `info` VALUES (1001, 'a', '男', 'a', 'a'); 38 | INSERT INTO `info` VALUES (1002, 'b', '男', 'b', 'b'); 39 | INSERT INTO `info` VALUES (1003, 'c', '男', 'c', 'c'); 40 | INSERT INTO `info` VALUES (1004, 'd', '女', 'd', 'd'); 41 | INSERT INTO `info` VALUES (1005, 'e', '男', 'e', 'e'); 42 | INSERT INTO `info` VALUES (1006, 'f', '女', 'f', 'f'); 43 | INSERT INTO `info` VALUES (1007, 'g', '男', 'g', 'g'); 44 | INSERT INTO `info` VALUES (1008, 'h', '男', 'h', 'h'); 45 | INSERT INTO `info` VALUES (1009, 'i', '男', 'i', 'i'); 46 | INSERT INTO `info` VALUES (1010, 'j', '女', 'j', 'j'); 47 | INSERT INTO `info` VALUES (1011, 'k', '女', 'k', 'k'); 48 | INSERT INTO `info` VALUES (1012, 'l', '男', 'l', 'l'); 49 | INSERT INTO `info` VALUES (1013, 'm', '男', 'm', 'm'); 50 | INSERT INTO `info` VALUES (1014, 'n', '男', 'n', 'n'); 51 | INSERT INTO `info` VALUES (1015, 'o', '女', 'o', 'o'); 52 | INSERT INTO `info` VALUES (1016, 'p', '男', 'p', 'p'); 53 | INSERT INTO `info` VALUES (1017, 'q', '女', 'q', 'q'); 54 | INSERT INTO `info` VALUES (1018, 'r', '女', 'r', 'r'); 55 | INSERT INTO `info` VALUES (1019, 's', '女', 's', 's'); 56 | INSERT INTO `info` VALUES (1020, 't', '女', 't', 't'); 57 | 58 | -- ---------------------------- 59 | -- Table structure for user 60 | -- ---------------------------- 61 | DROP TABLE IF EXISTS `user`; 62 | CREATE TABLE `user` ( 63 | `uid` int(11) NOT NULL AUTO_INCREMENT, 64 | `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 65 | `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 66 | PRIMARY KEY (`uid`) USING BTREE 67 | ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; 68 | 69 | -- ---------------------------- 70 | -- Records of user 71 | -- ---------------------------- 72 | INSERT INTO `user` VALUES (1, 'admin', '123456'); 73 | 74 | SET FOREIGN_KEY_CHECKS = 1; 75 | -------------------------------------------------------------------------------- /student/src/view.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk 3 | 4 | 5 | class View: 6 | """ 7 | 视图类,定义软件界面布局 8 | @author weingxing 9 | @since 2020/04/02 10 | """ 11 | 12 | def __init__(self): 13 | self.window = tk.Tk() 14 | self.window.title('学生管理系统') 15 | # 大小 840x540,窗口移动到 距屏幕左侧350,上侧100 的位置 16 | self.window.geometry('880x600+350+100') 17 | # 定义 frame,便于布局 18 | self.frame = tk.Frame(self.window) 19 | 20 | self.frame_left = tk.Frame(self.frame) 21 | self.frame_right = tk.Frame(self.frame) 22 | self.frame_add = tk.Frame(self.frame) 23 | 24 | self.frame_search = tk.Frame(self.frame_left) 25 | self.frame_list = tk.Frame(self.frame_left) 26 | self.frame_detail = tk.Frame(self.frame_right) 27 | 28 | # 定义控件 29 | # 搜索按钮和输入框 30 | self.btn_search = tk.Button(self.frame_search, text='搜 索', 31 | width=10, height=1, relief=tk.GROOVE) 32 | self.entry_search = tk.Entry(self.frame_search, width=25) 33 | self.btn_refresh = tk.Button(self.frame_search, text='刷 新', 34 | width=10, height=1, relief=tk.GROOVE) 35 | # 信息列表显示控件 36 | self.lab_title = tk.Label(self.frame_list, text='学生信息') 37 | columns = ('sno', 'name', 'sex', 'college', 'clazz') 38 | self.data_list = ttk.Treeview(self.frame_list, height=18, show='headings', columns=columns) 39 | # 滚动条 40 | self.VScroll1 = tk.Scrollbar(self.frame_left, orient='vertical', command=self.data_list.yview) 41 | self.VScroll1.place(relx=0.964, rely=0.190, relwidth=0.034, relheight=0.766) 42 | # 给treeview添加配置 43 | self.data_list.configure(yscrollcommand=self.VScroll1.set) 44 | 45 | # 信息更改显示控件 46 | self.lab_sno = tk.Label(self.frame_detail, text='学号') 47 | self.entry_sno = tk.Entry(self.frame_detail, state=tk.DISABLED) 48 | self.lab_name = tk.Label(self.frame_detail, text='姓名') 49 | self.entry_name = tk.Entry(self.frame_detail) 50 | self.lab_sex = tk.Label(self.frame_detail, text='性别') 51 | self.entry_sex = tk.Entry(self.frame_detail) 52 | self.lab_college = tk.Label(self.frame_detail, text='学院') 53 | self.entry_college = tk.Entry(self.frame_detail) 54 | self.lab_clazz = tk.Label(self.frame_detail, text='班级') 55 | self.entry_clazz = tk.Entry(self.frame_detail) 56 | self.btn_update = tk.Button(self.frame_detail, text='更 新', relief=tk.GROOVE) 57 | self.btn_delete = tk.Button(self.frame_detail, text='删 除', relief=tk.GROOVE) 58 | # 添加信息控件 59 | self._lab_sno = tk.Label(self.frame_add, text='学号') 60 | self._entry_sno = tk.Entry(self.frame_add) 61 | self._lab_name = tk.Label(self.frame_add, text='姓名') 62 | self._entry_name = tk.Entry(self.frame_add) 63 | self._lab_sex = tk.Label(self.frame_add, text='性别') 64 | self._entry_sex = tk.Entry(self.frame_add) 65 | self._lab_college = tk.Label(self.frame_add, text='学院') 66 | self._entry_college = tk.Entry(self.frame_add) 67 | self._lab_clazz = tk.Label(self.frame_add, text='班级') 68 | self._entry_clazz = tk.Entry(self.frame_add) 69 | self.btn_add = tk.Button(self.frame_add, text='添 加', relief=tk.GROOVE) 70 | 71 | def set_ui(self): 72 | # 表示列, 不显示 73 | self.data_list.column('sno', width=100, anchor='center') 74 | self.data_list.column('name', width=100, anchor='center') 75 | self.data_list.column('sex', width=100, anchor='center') 76 | self.data_list.column('college', width=150, anchor='center') 77 | self.data_list.column('clazz', width=100, anchor='center') 78 | # 显示表头 79 | self.data_list.heading('sno', text='学号') 80 | self.data_list.heading('name', text='姓名') 81 | self.data_list.heading('sex', text='性别') 82 | self.data_list.heading('college', text='学院') 83 | self.data_list.heading('clazz', text='班级') 84 | # 放置 frame 85 | self.frame.grid(row=0, column=0) 86 | self.frame_left.grid(row=0, column=0, padx=20) 87 | self.frame_right.grid(row=0, column=1) 88 | self.frame_add.grid(row=1, column=0) 89 | self.frame_search.grid(row=0, column=0, pady=10) 90 | self.frame_list.grid(row=1, column=0, pady=20) 91 | self.frame_detail.grid(row=0, column=0) 92 | 93 | # 放置搜索、刷新 94 | self.entry_search.grid(row=0, column=0, padx=5) 95 | self.btn_search.grid(row=0, column=1, padx=5) 96 | self.btn_refresh.grid(row=0, column=2, padx=5) 97 | 98 | # 放置数据表格 99 | self.lab_title.grid(row=1, column=0) 100 | self.data_list.grid(row=2, column=0) 101 | 102 | # 放置详细内容控件 103 | self.lab_sno.grid(row=0, column=2, pady=5) 104 | self.entry_sno.grid(row=0, column=3, pady=5) 105 | self.lab_name.grid(row=1, column=2, pady=5) 106 | self.entry_name.grid(row=1, column=3, pady=5) 107 | self.lab_sex.grid(row=2, column=2, pady=5) 108 | self.entry_sex.grid(row=2, column=3, pady=5) 109 | self.lab_college.grid(row=3, column=2, pady=5) 110 | self.entry_college.grid(row=3, column=3, pady=5) 111 | self.lab_clazz.grid(row=4, column=2, pady=5) 112 | self.entry_clazz.grid(row=4, column=3, pady=5) 113 | self.btn_delete.grid(row=5, column=2, pady=15, ipadx=15) 114 | self.btn_update.grid(row=5, column=3, pady=15, ipadx=15) 115 | 116 | # 放置添加内容控件 117 | self._lab_sno.grid(row=0, column=0, padx=15) 118 | self._entry_sno.grid(row=0, column=1) 119 | self._lab_name.grid(row=0, column=2, padx=15) 120 | self._entry_name.grid(row=0, column=3) 121 | self._lab_sex.grid(row=0, column=4, padx=15) 122 | self._entry_sex.grid(row=0, column=5) 123 | self._lab_college.grid(row=1, column=0, pady=10) 124 | self._entry_college.grid(row=1, column=1) 125 | self._lab_clazz.grid(row=1, column=2) 126 | self._entry_clazz.grid(row=1, column=3) 127 | self.btn_add.grid(row=1, column=4, ipadx=20, columnspan=2) 128 | 129 | 130 | if __name__ == '__main__': 131 | # 测试 132 | view = View() 133 | view.set_ui() 134 | view.window.mainloop() 135 | -------------------------------------------------------------------------------- /student/src/welcome.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weingxing/python-assigment/5726f3a6bb4f71e91a5802d1670a726c9671362a/student/src/welcome.gif --------------------------------------------------------------------------------