From 29dc7b5562abf2cc0b29f77c17857d917ed99c2b Mon Sep 17 00:00:00 2001 From: JesterHey <144512889+JesterHey@users.noreply.github.com> Date: Thu, 7 Dec 2023 14:42:32 +0800 Subject: [PATCH] Add files via upload --- AES.py | 45 +++ cloud.py | 4 +- demo.py | 24 +- demo2.py | 232 ++++++++------ get_params.py | 283 ++++++++++-------- login_ui.py | 259 +++++++++------- main.py | 99 +++++- show_welcome.py | 85 +++--- ...嗨,无任何不良引导和倾向).md | 2 +- 9 files changed, 613 insertions(+), 420 deletions(-) create mode 100644 AES.py diff --git a/AES.py b/AES.py new file mode 100644 index 0000000..1da347d --- /dev/null +++ b/AES.py @@ -0,0 +1,45 @@ +from Crypto.Cipher import AES +from Crypto.Random import get_random_bytes +from Crypto.Util.Padding import pad, unpad + +def encrypt_message(message, key): + """ + 加密函数 + :param message: 要加密的明文消息 + :param key: 加密密钥 + :return: 密文 + """ + # 创建 AES 对象 + cipher = AES.new(key, AES.MODE_CBC) + + # 对明文进行填充,然后加密 + ct_bytes = cipher.encrypt(pad(message.encode(), AES.block_size)) + + # 将 IV 和密文返回 + return cipher.iv, ct_bytes + +def decrypt_message(iv, ct, key): + """ + 解密函数 + :param iv: 初始化向量 + :param ct: 密文 + :param key: 加密密钥 + :return: 解密后的明文 + """ + # 创建一个新的 AES 对象 + cipher = AES.new(key, AES.MODE_CBC, iv) + + # 解密并去除填充 + pt = unpad(cipher.decrypt(ct), AES.block_size).decode() + + return pt + +# 测试 +key = get_random_bytes(16) # 生成随机密钥 +message = "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" + +iv, ct = encrypt_message(message, key) +print("Encrypted:", ct) + +pt = decrypt_message(iv, ct, key) +print("Decrypted:", pt) diff --git a/cloud.py b/cloud.py index 848a964..86a534b 100644 --- a/cloud.py +++ b/cloud.py @@ -32,4 +32,6 @@ def upload(name): bucket.put_object_from_file(name, name) def delete(name): - bucket.delete_object(name) \ No newline at end of file + bucket.delete_object(name) + +delete('18503.json') \ No newline at end of file diff --git a/demo.py b/demo.py index 824cd4f..41b05df 100644 --- a/demo.py +++ b/demo.py @@ -1,19 +1,5 @@ -from openai import OpenAI -import openai -import os -openai.api_base = "https://api.openai-proxy.com" -openai.api_key = "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" -# os.environ['http_proxy'] = 'http://127.0.0.1:10809' -# os.environ['https_proxy'] = 'http://127.0.0.1:10809' -client = OpenAI( - api_key='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', - base_url='https://api.openai-proxy.com' -) - -resp = client.chat.completions.create( - model='gpt-3.5-turbo', - messages=[ - {'role': 'user', 'content': '你好!'} - ] -) -print(resp.choices[0].message.content) \ No newline at end of file +import json +with open('demo.json','r',encoding='utf-8') as f: + data = json.load(f) +for i,j in enumerate(data.items()): + print(i,j[-1]) \ No newline at end of file diff --git a/demo2.py b/demo2.py index 924a8db..cada1eb 100644 --- a/demo2.py +++ b/demo2.py @@ -1,98 +1,134 @@ -import sys -from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, QLabel -from PyQt5.QtCore import Qt -from PyQt5.QtGui import QPixmap, QPainter - -class MyApp(QWidget): - def __init__(self): - super().__init__() - self.initUI() - - def initUI(self): - # 加载背景图片 - self.background = QPixmap('hnu.jpg') - - # 创建一个垂直布局 - mainLayout = QVBoxLayout() - - # 创建三个文本框并设置占位符 - self.nameEdit = QLineEdit() - self.pwdEdit = QLineEdit() - self.urlEdit = QLineEdit() - self.nameEdit.setPlaceholderText('Name') - self.pwdEdit.setPlaceholderText('Password') - self.urlEdit.setPlaceholderText('URL') - - # 创建一个按钮并连接信号 - btn = QPushButton('提交') - btn.clicked.connect(self.onSubmit) - - # 对于每个输入框,创建一个水平布局以保持居中 - for label_text, edit_widget in [("用户名", self.nameEdit), - ("密码", self.pwdEdit), - ("实训网址", self.urlEdit)]: - hbox = QHBoxLayout() - hbox.addStretch() - vbox_inner = QVBoxLayout() - vbox_inner.addWidget(QLabel(label_text)) - vbox_inner.addWidget(edit_widget) - hbox.addLayout(vbox_inner) - hbox.addStretch() - mainLayout.addLayout(hbox) - - # 添加按钮到布局 - mainLayout.addWidget(btn, 0, Qt.AlignCenter) - - # 设置窗口的布局 - self.setLayout(mainLayout) - self.setWindowTitle('') - self.setGeometry(300, 300, 400, 300) - self.centerWindow() - - # 应用样式 - self.applyStyles() - - def paintEvent(self, event): - painter = QPainter(self) - - # 计算背景图片绘制的起始坐标,使其位于窗口中心 - bgWidth = self.background.width() - bgHeight = self.background.height() - startX = (self.width() - bgWidth) // 2 - startY = (self.height() - bgHeight) // 2 - - painter.drawPixmap(startX, startY, self.background) - - def centerWindow(self): - qr = self.frameGeometry() - cp = QApplication.desktop().availableGeometry().center() - qr.moveCenter(cp) - self.move(qr.topLeft()) - - def applyStyles(self): - self.setStyleSheet(""" - QLineEdit { - border: 1px solid gray; - border-radius: 10px; - padding: 5px; - background: transparent; - } - QLineEdit:focus { - border: 1px solid LightSeaGreen; - } - """) - - def onSubmit(self): - name = self.nameEdit.text() - pwd = self.pwdEdit.text() - url = self.urlEdit.text() - print(f"Name: {name}, Password: {pwd}, URL: {url}") - - # 关闭窗口 - self.close() - -if __name__ == '__main__': - app = QApplication(sys.argv) - ex = MyApp() - ex.show() - sys.exit(app.exec_()) +''' +本模块用于创建登录界面并保存用户信息 +''' +import sys +from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, QLabel +from PyQt5.QtCore import Qt,QTimer +from PyQt5.QtGui import QPixmap, QPainter +import os +import platform +import json + +class MyApp(QWidget): + def __init__(self): + super().__init__() + self.initUI() + + def initUI(self): + # 加载背景图片 + self.background = QPixmap() + # 创建一个垂直布局 + mainLayout = QVBoxLayout() + + # 创建三个文本框并设置占位符 + self.nameEdit = QLineEdit() + self.pwdEdit = QLineEdit() + self.urlEdit = QLineEdit() + + # 创建一个按钮并连接信号 + btn = QPushButton('提交') + btn.clicked.connect(self.onSubmit) + + # 创建一个用于显示错误信息的标签 + self.errorLabel = QLabel('', self) + self.errorLabel.hide() # 初始时隐藏该标签 + + self.setWindowTitle('头歌助手登录') + + # 对于每个输入框,创建一个水平布局以保持居中 + for label_text, edit_widget in [("用户名", self.nameEdit), + ("密 码", self.pwdEdit), + ("实训网址", self.urlEdit)]: + hbox = QHBoxLayout() + hbox.addStretch() + vbox_inner = QVBoxLayout() + vbox_inner.addWidget(QLabel(label_text)) + vbox_inner.addWidget(edit_widget) + hbox.addLayout(vbox_inner) + hbox.addStretch() + mainLayout.addLayout(hbox) + + # 添加按钮和错误信息标签到布局 + mainLayout.addWidget(btn, 0, Qt.AlignCenter) + mainLayout.addWidget(self.errorLabel, 0, Qt.AlignCenter) + + # 设置窗口的布局 + self.setLayout(mainLayout) + self.setGeometry(300, 300, 650, 550) + self.centerWindow() + + # 应用样式 + self.applyStyles() + + def paintEvent(self, event): + painter = QPainter(self) + painter.drawPixmap(self.rect(), self.background) + + def centerWindow(self): + qr = self.frameGeometry() + cp = QApplication.desktop().availableGeometry().center() + qr.moveCenter(cp) + self.move(qr.topLeft()) + + def applyStyles(self): + self.setStyleSheet(""" + QLineEdit { + border: 1px solid gray; + border-radius: 10px; + padding: 5px; + background: rgba(255, 255, 255, 100); + color: black; + width: 300px; + height: 30px; + } + QLineEdit:focus { + border: 3px solid black; + } + QLabel { + color: black; + font-size: 20pt; + font-family: "Microsoft YaHei"; + } + QPushButton { + border: 1px solid gray; + border-radius: 10px; + background: rgba(255, 255, 255, 100); + color: black; + width: 100px; + height: 27px; + } + /* 样式用于错误信息标签 */ + QLabel#errorLabel { + color: red; + font-size: 16pt; + } + """) + + def onSubmit(self): + name = self.nameEdit.text() + pwd = self.pwdEdit.text() + url = self.urlEdit.text() + + if name == '' or pwd == '' or url == '': + self.showError("非法输入!") + else: + with open('userinfo.json', 'w') as f: + json.dump({'name': name, 'pwd': pwd, 'url': url}, f) + self.close() + + def showError(self, message): + self.errorLabel.setText(message) + self.errorLabel.show() + QTimer.singleShot(2000, self.hideError) + + def hideError(self): + self.errorLabel.hide() + +def main(): + app = QApplication(sys.argv) + ex = MyApp() + ex.show() + sys.exit(app.exec_()) + +if __name__ == '__main__': + main() diff --git a/get_params.py b/get_params.py index 2b990bf..e2f1528 100644 --- a/get_params.py +++ b/get_params.py @@ -10,6 +10,7 @@ ''' #导入所需模块 +import platform import os import json import time @@ -25,147 +26,167 @@ from cloud import is_exist,download #配置参数 opt = Options() opt.add_experimental_option('detach', True) -opt.add_argument('--headless') +#opt.add_argument('--headless') chrome_driver = 'D:\ChromeDownload\chromedriver-win64\chromedriver-win64' -#以下部分在发行版本需要优化UI -# url = input('请输入作业中任意一关的网址:') -# user_name = input('请输入用户名:') -# password = input('请输入密码:') +# 发行版时改为调用userinfo.json文件中的用户名和密码 url = 'https://www.educoder.net/tasks/27V4D95N/1191515/vmxpzae734bj?coursesId=27V4D95N' user_name = 'hnu202311020126' password = 'hzy123456' -# 另外,目前好像只有实训作业有这些参数,其他的作业例如编程作业就没有,所以先判断一下是否为实训作业,可以通过用户输入的url判断 -# 主要是看educoder.net/后面是否有tasks,如果有,则是实训作业,否则,不是实训作业 +platf = platform.platform() def is_practice(url:str) -> bool: obj=re.compile(r'www.educoder.net/tasks') if obj.search(url): return True else: return False -if is_practice(url): - #构造selenium对象 - safari = Chrome(options=opt) - safari.get(url) - #模拟登录 - safari.implicitly_wait(10) - safari.find_element(By.ID, 'login').send_keys(user_name) - safari.find_element(By.ID, 'password').send_keys(password) - safari.find_element(By.ID, 'password').send_keys(Keys.ENTER) - time.sleep(2) - #获取cookie,User-Agent - Cookie = safari.get_cookies() - User_Agent = safari.execute_script('return navigator.userAgent') - cookie = f'autologin_trustie={Cookie[1]["value"]}; _educoder_session={Cookie[0]["value"]}' - #先获取到shixun_id便于先判断云端文件是否存在 - cur_url = url - identity = cur_url.split('/')[-1].split('?')[0] - id_url = f'https://data.educoder.net/api/tasks/{identity}.json?' - headers = { - 'Cookie':cookie, - 'User-Agent':User_Agent, - 'Referer':cur_url - } - response = requests.get(url=id_url, headers=headers) - shixun_id = dict(response.json())['challenge']['shixun_id'] - #判断云端文件是否存在 - try: - exist = is_exist(f'{shixun_id}.json') - if exist: #存在,则跳转到云端下载并终止本程序 - print('云端文件已存在,正在下载') - download(f'{shixun_id}.json') - # 检测本地文件是否下载完成 - while True: - try: - if os.path.exists(f'{shixun_id}.json'): - print('下载完成') - safari.quit() - exit() - break - except Exception as e: - print(e) - except Exception as e: - print(e) - finally: #不存在,则继续执行本程序 - #获取关卡数 - #点击展开关卡页面 - safari.find_element(By.XPATH,'//*[@id="task-left-panel"]/div[1]/a[1]').click() +# 另外,目前好像只有实训作业有这些参数,其他的作业例如编程作业就没有,所以先判断一下是否为实训作业,可以通过用户输入的url判断 +# 主要是看educoder.net/后面是否有tasks,如果有,则是实训作业,否则,不是实训作业 +#为方便main.py调用,将判断函数写入函数中,以下部分封装为函数 +def get_parameters(url:str,user_name:str,password:str): + url = url + user_name = user_name + password = password + if is_practice(url): + #构造selenium对象 + safari = Chrome(options=opt) + safari.get(url) + #模拟登录 + safari.implicitly_wait(10) + safari.find_element(By.ID, 'login').send_keys(user_name) + safari.find_element(By.ID, 'password').send_keys(password) + safari.find_element(By.ID, 'password').send_keys(Keys.ENTER) time.sleep(2) - #关卡数量由 class = "flex-container challenge-title space-between" 的元素数量决定 - htmltxt = safari.page_source - html = etree.HTML(htmltxt) - task_num = html.xpath('count(//*[@class="flex-container challenge-title space-between"])') - task_num = int(task_num) - #关闭关卡页面 - safari.find_element(By.XPATH,'//*[@id="task-left-panel"]/div[3]/div[1]').click() - #对于每一关,获取参数 - #每一关的参数由以下元素组成: - ''' - /html/body/div[1]/div/div/div/div[2]/section[1]/div[3]/div[3]/div/div/div/div/div[3]/div[1]/a - /html/body/div[1]/div/div/div/div[2]/section[1]/div[3]/div[3]/div/div/div/div/div[4]/div[1]/a - ''' - obj1 = re.compile(r'

任务描述

(?P.*?)

',re.S) - obj2 = re.compile(r'

编程要求

(?P.*?)

',re.S) - #初始化一个字典,用于存放所有关卡的参数 - total = {} - i=1 + #判断是否登录成功 try: - while i <= task_num: - safari.implicitly_wait(10) - safari.find_element(By.XPATH, '//*[@id="task-left-panel"]/div[1]/a[1]').click() - safari.implicitly_wait(10) - safari.find_element(By.XPATH,f'/html/body/div[1]/div/div/div/div[2]/section[1]/div[3]/div[3]/div/div/div/div/div[{i}]/div[1]/a').click() - time.sleep(3) - #获取课程id -> 根据url中?前面的,最后一个/后面的那部分参数构造请求,同时,似乎还需要用到cookie,User-Agent和Referer参数,这些统一用selenium在登陆后获取并组装成headers - #获取cookie,User-Agent和Referer - cur_url=Referer = safari.current_url - identity = cur_url.split('/')[-1].split('?')[0] - id_url = f'https://data.educoder.net/api/tasks/{identity}.json?' - #获取课程id - headers = { - 'Cookie':cookie, - 'User-Agent':User_Agent, - 'Referer':Referer - } - try: - response = requests.get(url=id_url,headers=headers) - challenge_id = dict(response.json())['challenge']['id'] - shixun_id = dict(response.json())['challenge']['shixun_id'] - except BaseException: - print('获取课程id失败') - #获取任务描述(如果存在的话) - page_source = safari.page_source - describe = obj1.findall(page_source) - #获取编程要求(如果存在的话) - require = obj2.findall(page_source) - #获取编辑器中的代码,由于代码都是class = "view-line"的div,先找到所有class = "view-line"的div,获取其中的所有文本,再把不同行的代码用\n连接起来 - code = safari.find_elements(By.CLASS_NAME,'view-line') - code = '\n'.join([i.text for i in code]).lstrip('\n') - #把参数存入字典,再转换为json格式 - task = { - 'describe':describe[0] if len(describe) != 0 else '', - 'require':require[0] if len(require) != 0 else '', - 'code':code, - 'verified': False, #这个参数是用来标记答案是否被用户认证为正确答案的,初始值为False - 'last_modified': time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())) #这个参数是用来标记答案最后一次被修改的时间,初始值为当前时间 - } - #把每一关的参数存入总的字典中 - total[challenge_id] = task - #去往下一关 - i += 1 + safari.find_element(By.XPATH,'//*[@id="task-left-panel"]/div[1]/a[1]') except BaseException: - print(f'{challenge_id}参数获取参数失败') - #判断爬取到的代码是否存在空值,如果存在空值,则重新爬取 - for value in total.values(): - if value['code'] == '': - print('检测到代码参数为空值,重新爬取') - safari.close() - os.system('python get_params.py') - exit() - #把参数写入本地json文件中,文件名字与shixun_name相同键为course_id,值为一个列表,列表中每个元素为一个字典,字典中包含每一关的参数 - with open(f'{shixun_id}.json','w',encoding='utf-8') as f: - json.dump(total,f,ensure_ascii=False,indent=4) - #关闭浏览器 - safari.quit() -else: - print('这不是一个实训作业') - exit() + print('登录失败 请检查输入信息是否正确') + # 关闭浏览器 + safari.quit() + #重新调用login_ui.py + if 'Windows' in platf: + os.system('python login_ui.py') + else: + os.system('python3 login_ui.py') + #获取cookie,User-Agent + Cookie = safari.get_cookies() + User_Agent = safari.execute_script('return navigator.userAgent') + cookie = f'autologin_trustie={Cookie[1]["value"]}; _educoder_session={Cookie[0]["value"]}' + #先获取到shixun_id便于先判断云端文件是否存在 + cur_url = url + identity = cur_url.split('/')[-1].split('?')[0] + id_url = f'https://data.educoder.net/api/tasks/{identity}.json?' + headers = { + 'Cookie':cookie, + 'User-Agent':User_Agent, + 'Referer':cur_url + } + response = requests.get(url=id_url, headers=headers) + shixun_id = dict(response.json())['challenge']['shixun_id'] + #判断云端文件是否存在 + try: + exist = is_exist(f'{shixun_id}.json') + if exist: #存在,则跳转到云端下载并终止本程序 + print('云端文件已存在,正在下载') + download(f'{shixun_id}.json') + # 检测本地文件是否下载完成 + while True: + try: + if os.path.exists(f'{shixun_id}.json'): + print('下载完成') + safari.quit() + exit() + break + except Exception as e: + print(e) + except Exception as e: + print(e) + finally: #不存在,则继续执行本程序 + print('云端文件不存在,正在爬取') + #获取关卡数 + #点击展开关卡页面 + safari.find_element(By.XPATH,'//*[@id="task-left-panel"]/div[1]/a[1]').click() + time.sleep(2) + #关卡数量由 class = "flex-container challenge-title space-between" 的元素数量决定 + htmltxt = safari.page_source + html = etree.HTML(htmltxt) + task_num = html.xpath('count(//*[@class="flex-container challenge-title space-between"])') + task_num = int(task_num) + #关闭关卡页面 + safari.find_element(By.XPATH,'//*[@id="task-left-panel"]/div[3]/div[1]').click() + #对于每一关,获取参数 + #每一关的参数由以下元素组成: + ''' + /html/body/div[1]/div/div/div/div[2]/section[1]/div[3]/div[3]/div/div/div/div/div[3]/div[1]/a + /html/body/div[1]/div/div/div/div[2]/section[1]/div[3]/div[3]/div/div/div/div/div[4]/div[1]/a + ''' + obj1 = re.compile(r'

任务描述

(?P.*?)

',re.S) + obj2 = re.compile(r'

编程要求

(?P.*?)

',re.S) + #初始化一个字典,用于存放所有关卡的参数 + total = {} + i=1 + try: + while i <= task_num: + safari.implicitly_wait(10) + safari.find_element(By.XPATH, '//*[@id="task-left-panel"]/div[1]/a[1]').click() + safari.implicitly_wait(10) + safari.find_element(By.XPATH,f'/html/body/div[1]/div/div/div/div[2]/section[1]/div[3]/div[3]/div/div/div/div/div[{i}]/div[1]/a').click() + time.sleep(3) + #获取课程id -> 根据url中?前面的,最后一个/后面的那部分参数构造请求,同时,似乎还需要用到cookie,User-Agent和Referer参数,这些统一用selenium在登陆后获取并组装成headers + #获取cookie,User-Agent和Referer + cur_url=Referer = safari.current_url + identity = cur_url.split('/')[-1].split('?')[0] + id_url = f'https://data.educoder.net/api/tasks/{identity}.json?' + #获取课程id + headers = { + 'Cookie':cookie, + 'User-Agent':User_Agent, + 'Referer':Referer + } + try: + response = requests.get(url=id_url,headers=headers) + challenge_id = dict(response.json())['challenge']['id'] + shixun_id = dict(response.json())['challenge']['shixun_id'] + except BaseException: + print('获取课程id失败') + #获取任务描述(如果存在的话) + page_source = safari.page_source + describe = obj1.findall(page_source) + #获取编程要求(如果存在的话) + require = obj2.findall(page_source) + #获取编辑器中的代码,由于代码都是class = "view-line"的div,先找到所有class = "view-line"的div,获取其中的所有文本,再把不同行的代码用\n连接起来 + code = safari.find_elements(By.CLASS_NAME,'view-line') + code = '\n'.join([i.text for i in code]).lstrip('\n') + #把参数存入字典,再转换为json格式 + task = { + 'describe':describe[0] if len(describe) != 0 else '', + 'require':require[0] if len(require) != 0 else '', + 'code':code, + 'verified': False, #这个参数是用来标记答案是否被用户认证为正确答案的,初始值为False + 'last_modified': time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())) #这个参数是用来标记答案最后一次被修改的时间,初始值为当前时间 + } + #把每一关的参数存入总的字典中 + total[challenge_id] = task + #去往下一关 + i += 1 + except BaseException: + print(f'{challenge_id}参数获取参数失败') + #判断爬取到的代码是否存在空值,如果存在空值,则重新爬取 + for value in total.values(): + if value['code'] == '': + print('检测到代码参数为空值,重新爬取') + safari.close() + # 再次执行本程序 + if 'Windows' in platf: + os.system('python get_params.py') + else: + os.system('python3 get_params.py') + #把参数写入本地json文件中,文件名字与shixun_name相同键为course_id,值为一个列表,列表中每个元素为一个字典,字典中包含每一关的参数 + with open(f'{shixun_id}.json','w',encoding='utf-8') as f: + json.dump(total,f,ensure_ascii=False,indent=4) + print('参数爬取完成') + #关闭浏览器 + safari.quit() + else: + print('这不是一个实训作业') + exit() diff --git a/login_ui.py b/login_ui.py index 5a5508a..fa07d82 100644 --- a/login_ui.py +++ b/login_ui.py @@ -1,116 +1,143 @@ -import sys -from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, QLabel -from PyQt5.QtCore import Qt -from PyQt5.QtGui import QPixmap, QPainter -import os -class MyApp(QWidget): - def __init__(self): - super().__init__() - self.initUI() - - def initUI(self): - # 加载背景图片 - self.background = QPixmap('b3o.png') - # 创建一个垂直布局 - mainLayout = QVBoxLayout() - - # 创建三个文本框并设置占位符 - self.nameEdit = QLineEdit() - self.pwdEdit = QLineEdit() - self.urlEdit = QLineEdit() - # self.nameEdit.setPlaceholderText('Name') - # self.pwdEdit.setPlaceholderText('Password') - # self.urlEdit.setPlaceholderText('URL') - - # 创建一个按钮并连接信号 - btn = QPushButton('提交') - btn.clicked.connect(self.onSubmit) - - self.setWindowTitle('头歌助手登录') - # 对于每个输入框,创建一个水平布局以保持居中 - for label_text, edit_widget in [("用户名", self.nameEdit), - ("密 码", self.pwdEdit), - ("实训网址", self.urlEdit)]: - hbox = QHBoxLayout() #创建一个水平布局 - hbox.addStretch() #添加一个伸缩因子,使得文本框和按钮位于窗口中心 - vbox_inner = QVBoxLayout() #创建一个垂直布局 - vbox_inner.addWidget(QLabel(label_text)) #添加一个标签 - vbox_inner.addWidget(edit_widget) #添加一个文本框 - hbox.addLayout(vbox_inner) #将垂直布局添加到水平布局中 - hbox.addStretch() #添加一个伸缩因子,使得文本框和按钮位于窗口中心 - mainLayout.addLayout(hbox) #将水平布局添加到垂直布局中 - - # 添加按钮到布局 - mainLayout.addWidget(btn, 0, Qt.AlignCenter) - - # 设置窗口的布局 - self.setLayout(mainLayout) - self.setWindowTitle('') - self.setGeometry(300, 300, 650,550) # 设置窗口大小,其中300,300为窗口左上角坐标,1000,600为窗口大小 - self.centerWindow() - - # 应用样式 - self.applyStyles() - self.show() - - def paintEvent(self, event): - painter = QPainter(self) - painter.drawPixmap(self.rect(), self.background) - - def centerWindow(self): - qr = self.frameGeometry() - cp = QApplication.desktop().availableGeometry().center() - qr.moveCenter(cp) - self.move(qr.topLeft()) - - def applyStyles(self): - # css 用于设置窗口样式 - self.setStyleSheet(""" - QLineEdit { - border: 1px solid gray; - border-radius: 10px; - padding: 5px; - background: rgba(255, 255, 255, 100); - } - QLineEdit:focus { - border: 1px solid black; - } - QLabel { - color: black; - font-size: 23pt; - margin-top: 50px; /* 顶部外边距 */ - margin-left: 20px; /* 左侧外边距 */ - } - QLineEdit { - border: 1px solid gray; - border-radius: 10px; - padding: 5px; - background: rgba(255, 255, 255, 100); - color: black; /* 设置输入文本颜色为黑色 */ - width: 300px; /* 设置输入框宽度 */ - height: 30px; /* 设置输入框高度 */ - } - QPushButton { - border: 1px solid gray; - border-radius: 10px; - padding: 5px; - background: rgba(255, 255, 255, 100); - color: black; /* 设置按钮文本颜色为黑色 */ - width: 100px; /* 设置按钮宽度 */ - height: 27px; /* 设置按钮高度 */ - } - """) - - def onSubmit(self): - name = self.nameEdit.text() - pwd = self.pwdEdit.text() - url = self.urlEdit.text() - print(f"Name: {name}, Password: {pwd}, URL: {url}") - - # 关闭窗口 - self.close() -os.system('python3 show_welcome.py') -app = QApplication(sys.argv) -ex = MyApp() -ex.show() -sys.exit(app.exec_()) \ No newline at end of file +''' +本模块用于创建登录界面并保存用户信息 +''' +import sys +from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, QLabel +from PyQt5.QtCore import Qt,QTimer +from PyQt5.QtGui import QPixmap, QPainter +import os +import platform +import json + +class MyApp(QWidget): + def __init__(self): + super().__init__() + self.initUI() + + def initUI(self): + # 加载背景图片 + self.background = QPixmap('picture\\b2.png') + # 创建一个垂直布局 + mainLayout = QVBoxLayout() + + # 创建三个文本框并设置占位符 + self.nameEdit = QLineEdit() + self.pwdEdit = QLineEdit() + self.urlEdit = QLineEdit() + + # 创建一个按钮并连接信号 + btn = QPushButton('提交') + btn.clicked.connect(self.onSubmit) + + # 创建一个用于显示错误信息的标签 + self.errorLabel = QLabel('', self) + self.errorLabel.hide() # 初始时隐藏该标签 + + self.setWindowTitle('头歌助手登录') + + # 对于每个输入框,创建一个水平布局以保持居中 + for label_text, edit_widget in [("用户名", self.nameEdit), + ("密 码", self.pwdEdit), + ("实训网址", self.urlEdit)]: + hbox = QHBoxLayout() + hbox.addStretch() + vbox_inner = QVBoxLayout() + vbox_inner.addWidget(QLabel(label_text)) + vbox_inner.addWidget(edit_widget) + hbox.addLayout(vbox_inner) + hbox.addStretch() + mainLayout.addLayout(hbox) + + # 添加按钮和错误信息标签到布局 + mainLayout.addWidget(btn, 0, Qt.AlignCenter) + mainLayout.addWidget(self.errorLabel, 0, Qt.AlignCenter) + + # 设置窗口的布局 + self.setLayout(mainLayout) + self.setGeometry(350, 350, 1200, 700) + self.centerWindow() + + # 应用样式 + self.applyStyles() + + def paintEvent(self, event): + painter = QPainter(self) + painter.drawPixmap(self.rect(), self.background) + + def centerWindow(self): + qr = self.frameGeometry() + cp = QApplication.desktop().availableGeometry().center() + qr.moveCenter(cp) + self.move(qr.topLeft()) + + def applyStyles(self): + self.setStyleSheet(""" + QLineEdit { + border: 1px solid gray; + border-radius: 10px; + padding: 5px; + background: rgba(255, 255, 255, 100); + color: black; + width: 300px; + height: 50px; + font-size: 14pt; /* 设置字号为14磅 */ + font-family: 'Microsoft YaHei'; /* 设置字体为微软雅黑 */ + } + QLineEdit:focus { + border: 3px solid black; + } + QLabel { + color: black; + font-size: 20pt; + font-family: "Microsoft YaHei"; + margin-top: 50px; /* 顶部外边距 */ + margin-left: 200px; /* 左侧外边距 */ + } + QPushButton { + border: 1px solid gray; + border-radius: 10px; + background: rgba(255, 255, 255, 100); + color: black; + width: 100px; + height: 27px; + } + /* 样式用于错误信息标签 */ + QLabel#errorLabel { + color: red; + font-size: 16pt; + } + """) + + def onSubmit(self): + name = self.nameEdit.text() + pwd = self.pwdEdit.text() + url = self.urlEdit.text() + + if name == '' or pwd == '' or url == '' or ~url.startswith('https') or ~url.startswith('www'): + self.showError("非法输入!") + else: + with open('userinfo.json', 'w') as f: + json.dump({'name': name, 'pwd': pwd, 'url': url}, f) + self.close() + + def showError(self, message): + self.errorLabel.setText(message) + self.errorLabel.show() + QTimer.singleShot(1200, self.hideError) + + def hideError(self): + self.errorLabel.hide() + # 关闭窗口 + + + +# 判断当前操作系统(基础太渣,只能这样判断了,哈哈) +platf = platform.platform() +if platf.startswith('Windows'): + os.system('python show_welcome.py') +else: + os.system('python3 show_welcome.py') +app = QApplication(sys.argv) # 创建应用程序对象 +ex = MyApp() # 创建窗口对象 +ex.show() # 显示窗口 +sys.exit(app.exec_()) # 保证程序完整退出 \ No newline at end of file diff --git a/main.py b/main.py index 886f5bd..5958e92 100644 --- a/main.py +++ b/main.py @@ -1,15 +1,84 @@ -''' -主程序:整合各个模块 -1、ui文件调用相应ui模块 -2、get_params.py获取参数 -3、get_answer.py获取答案 -4、cloud.py将json文件存入云端 -''' - -# 生成图形化界面,引导用户登陆并输入实训网址 -# 调用get_params.py获取参数,这一步同时隐含了云端获取答案的过程 -# 如果云端答案不存在,则调用get_answer.py获取答案,并展示给用户 -# 用户verif选项确认后,调用cloud.py将json文件存入云端 - - -import login_ui \ No newline at end of file +''' +主程序:整合各个模块 +1、ui文件调用相应ui模块 +2、get_params.py获取参数 +3、get_answer.py获取答案 +4、cloud.py将json文件存入云端 +''' + +# 生成图形化界面,引导用户登陆并输入实训网址 +# 调用get_params.py获取参数,这一步同时隐含了云端获取答案的过程 +# 如果云端答案不存在,则调用get_answer.py获取答案,并展示给用户 +# 用户verif选项确认后,调用cloud.py将json文件存入云端 + +# 导入所需模块 +import platform +from get_params import get_parameters +from get_answer import get_answer_from_api,promot,client +import json +import os + +# 判断当前操作系统 +platf = platform.platform() +# 调用login_ui获得用户输入的用户名、密码和实训网址 +if platf.startswith('Windows'): + os.system('python login_ui.py') +else: + os.system('python3 login_ui.py') + +# 检查userinfo.json文件是否存在,存在,则程序继续 +assert os.path.exists('userinfo.json'), 'userinfo.json文件不存在,请检查' + +# 先读取userinfo.json文件,获得用户名、密码和实训网址 +with open('userinfo.json', 'r') as f: + userinfo = json.load(f) +user_name = userinfo['name'] +password = userinfo['pwd'] +url = userinfo['url'] + +# 调用get_params.py获得参数,完成后本地应该有一个json文件,里面有参数 +get_parameters(url,user_name,password) +def get_json(file): + return [i for i in os.listdir(file) if i.endswith('.json')] + +#将file指定为当前目录 +file = os.getcwd() +json_names = get_json(file) + +# 遍历查找以数字开头的json文件,即为获得到的json文件,可能是云端下载的,也可能是爬取的 +global j_name +for j in json_names: + if j[0].isdigit(): + # 将该文件名赋值给json_name + j_name = j + break + +# 判断json_name文件中是否有answer,若有提取answer并格式化或展现 +with open(j_name,'r',encoding='utf-8') as f: + json_data = json.load(f) +def is_exist_answer(data:dict) -> bool: + for i,j in data.items(): + if 'answer' in j.keys(): + return True + return False +if is_exist_answer(json_data): + pass +else: + # 调用API获取答案 + new_data = get_answer_from_api(jsonfile=json_data,client=client,promot=promot) + # 重写本地json文件 + with open(j_name,'w',encoding='utf-8') as f: + json.dump(new_data,f,ensure_ascii=False,indent=4) + +# 上面的判断执行完后,本地的json文件中已经有answer了,下面实现信息展示 +# release版本要优化UI,beta版本先用print代替 + +# 读取json文件,获得answer +with open(j_name,'r',encoding='utf-8') as f: + data = json.load(f) +# 由于python3.6之后字典是有序的,所以可以直接遍历字典,显示关卡序号和答案 +# 要将key在data中对应的索引值+1,因为索引值是从0开始的 +for i,j in enumerate(data.items()): + print(f'第{i+1}关的答案是:{j[-1]["answer"]}') + +# 询问用户是否认证答案正确(下午新开一个模块实现) \ No newline at end of file diff --git a/show_welcome.py b/show_welcome.py index d232daa..0466650 100644 --- a/show_welcome.py +++ b/show_welcome.py @@ -1,39 +1,46 @@ -import sys -from PyQt5.QtWidgets import QApplication, QLabel, QWidget -from PyQt5.QtGui import QPixmap -from PyQt5.QtCore import QTimer, Qt - -class ImageWindow(QWidget): - def __init__(self, image_path): - super().__init__() - self.initUI(image_path) - - def initUI(self, image_path): - self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) - self.setAttribute(Qt.WA_TranslucentBackground) - - label = QLabel(self) - pixmap = QPixmap(image_path) - - # 缩放图片到期望的大小 - scaled_pixmap = pixmap.scaled(700, 400, Qt.KeepAspectRatio) # 设置图片大小为400x300,并保持纵横比 - label.setPixmap(scaled_pixmap) - self.resize(scaled_pixmap.width(), scaled_pixmap.height()) - - # 居中显示窗口 - qr = self.frameGeometry() - cp = QApplication.desktop().availableGeometry().center() - qr.moveCenter(cp) - self.move(qr.topLeft()) - - QTimer.singleShot(3000, self.close) - -def show(): - app = QApplication(sys.argv) - ex = ImageWindow('b2.png') - ex.show() - sys.exit(app.exec_()) - -if __name__ == '__main__': - show() - +import sys +from PyQt5.QtWidgets import QApplication, QLabel, QWidget, QDesktopWidget +from PyQt5.QtGui import QPixmap +from PyQt5.QtCore import QTimer, Qt + +class ImageWindow(QWidget): + def __init__(self, image_path): + super().__init__() + self.initUI(image_path) + + def initUI(self, image_path): + self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) + self.setAttribute(Qt.WA_TranslucentBackground) + + label = QLabel(self) + pixmap = QPixmap(image_path) + + # 获取屏幕尺寸 + screen = QDesktopWidget().screenGeometry() + + # 缩放图片大小 + # 过时警告 scaled_pixmap = pixmap.scaledint(screen.width() * 0.5, screen.height() * 0.5, Qt.KeepAspectRatio) + scaled_pixmap = pixmap.scaled(int(screen.width() * 0.5), int(screen.height() * 0.5), Qt.KeepAspectRatio) + label.setPixmap(scaled_pixmap) + self.resize(scaled_pixmap.width(), scaled_pixmap.height()) + + # 居中显示窗口 + self.centerWindow() + + QTimer.singleShot(4500, self.close) + + def centerWindow(self): + # 居中窗口 + qr = self.frameGeometry() + cp = QDesktopWidget().availableGeometry().center() # 获取屏幕中心点 + qr.moveCenter(cp) + self.move(qr.topLeft()) + +def show_image(): + app = QApplication(sys.argv) + ex = ImageWindow('picture\\b2txt.png') # 图片路径 + ex.show() + sys.exit(app.exec_()) + + +show_image() \ No newline at end of file diff --git a/发泄(纯属口嗨,无任何不良引导和倾向).md b/发泄(纯属口嗨,无任何不良引导和倾向).md index 0c8b70c..075e38b 100644 --- a/发泄(纯属口嗨,无任何不良引导和倾向).md +++ b/发泄(纯属口嗨,无任何不良引导和倾向).md @@ -1 +1 @@ -# 我真甜蜜地服了互联网上一群傻逼到处TMD跟喷史一样到处拉史一样的垃圾信息,就TM配置个代理一帮牛马讲得TMD的天花乱坠,什么TM云函数,Cloudfare代理,Njanx反向代理真NMSL的一群人。好不容易TND有几个人直接给TM代理地址又不说明要加/v1,官方文档也TM是一堆若至写的吗,API用法更新了就TM给几个示例代码是怕GitHub存储费用太高吗?社区和和论坛也是一帮彼阳的晚意,TM跟东晋士大夫一样搁哪清谈半天NM一个给代码示例的人都没有,屈指可数的几个代码又是API1.2版本之前的,用不了一点捏。我TM找整整2天就差冲OpenAI官网的时候无意间看到CSDN上的[这篇帖子](https://blog.csdn.net/qq_36265860/article/details/130111351)。WC,什么叫清晰,什么叫对比,什么叫做程序员的自我修养,TM我那么简单的一个问题就只有这个老哥给出解答。我NM,真的,当时真的想嫁给这个哥了。我宣布CSDN就是世界第一技术论坛,那些说看CSDN是屎里淘食的人下赛季池史。以后多看书,再上网查这种技术问题我也是SB。暂时就想到那么多,哈哈。 +# 我真甜蜜地服了互联网上一群傻逼到处TMD跟喷史一样到处拉史一样的垃圾信息,就TM配置个代理一帮牛马讲得TMD的天花乱坠,什么TM云函数,Cloudfare代理,Njanx反向代理真NMSL的一群人。好不容易TND有几个人直接给TM代理地址又不说明要加/v1,官方文档也TM是一堆SB写的吗,API用法更新了就TM给几个示例代码是怕GitHub存储费用太高吗?社区和和论坛也是一帮勾石东西,TM跟东晋士大夫一样搁哪清谈半天NM一个给代码示例的人都没有,屈指可数的几个代码又是API1.2版本之前的,用不了一点捏。我TM找整整2天就差冲OpenAI官网的时候无意间看到CSDN上的[这篇帖子](https://blog.csdn.net/qq_36265860/article/details/130111351)。WC,什么叫清晰,什么叫对比,什么叫做程序员的自我修养,TM我那么简单的一个问题就只有这个老哥给出解答。我NM,真的,当时真的想嫁给这个哥了。我宣布CSDN就是世界第一技术论坛,那些说看CSDN是屎里淘食的人下赛季池史。以后多看书,再上网查这种技术问题我也是SB。暂时就想到那么多,哈哈。