重构项目

This commit is contained in:
yuruo
2024-06-24 11:06:05 +08:00
parent 8a9b4e4867
commit 28b5987e97
155 changed files with 92 additions and 2454 deletions

24
.gitignore vendored
View File

@@ -1,19 +1,5 @@
# Ignore IDE and editor files
.idea/
.vscode/
dist/
# Ignore environment-specific files
.venv
config.yaml
build/
cache
**.lock
# Ignore Python cache files
**/__pycache__/
/session
*.spec
agent/model
node_modules
dist
out
.DS_Store
*.log*

3
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"recommendations": ["dbaeumer.vscode-eslint"]
}

39
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,39 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Main Process",
"type": "node",
"request": "launch",
"cwd": "${workspaceRoot}",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite",
"windows": {
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd"
},
"runtimeArgs": ["--sourcemap"],
"env": {
"REMOTE_DEBUGGING_PORT": "9222"
}
},
{
"name": "Debug Renderer Process",
"port": 9222,
"request": "attach",
"type": "chrome",
"webRoot": "${workspaceFolder}/src/renderer",
"timeout": 60000,
"presentation": {
"hidden": true
}
}
],
"compounds": [
{
"name": "Debug All",
"configurations": ["Debug Main Process", "Debug Renderer Process"],
"presentation": {
"order": 1
}
}
]
}

11
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,11 @@
{
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}

127
LICENSE
View File

@@ -1,127 +0,0 @@
木兰宽松许可证, 第2版
木兰宽松许可证, 第2版
2020年1月 http://license.coscl.org.cn/MulanPSL2
您对“软件”的复制、使用、修改及分发受木兰宽松许可证第2版“本许可证”的如下条款的约束
0. 定义
“软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。
“贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。
“贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。
“法人实体”是指提交贡献的机构及其“关联实体”。
“关联实体”是指对“本许可证”下的行为方而言控制、受控制或与其共同受控制的机构此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。
1. 授予版权许可
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。
2. 授予专利许可
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。
3. 无商标许可
“本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可但您为满足第4条规定的声明义务而必须使用除外。
4. 分发限制
您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。
5. 免责声明与责任限制
“软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。
6. 语言
“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。
条款结束
如何将木兰宽松许可证第2版应用到您的软件
如果您希望将木兰宽松许可证第2版应用到您的新软件为了方便接收者查阅建议您完成如下三步
1 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字;
2 请您在软件包的一级目录下创建以“LICENSE”为名的文件将整个许可证文本放入该文件中
3 请将如下声明文本放入每个源文件的头部注释中。
Copyright (c) [Year] [name of copyright holder]
[Software Name] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
Mulan Permissive Software LicenseVersion 2
Mulan Permissive Software LicenseVersion 2 (Mulan PSL v2)
January 2020 http://license.coscl.org.cn/MulanPSL2
Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions:
0. Definition
Software means the program and related documents which are licensed under this License and comprise all Contribution(s).
Contribution means the copyrightable work licensed by a particular Contributor under this License.
Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License.
Legal Entity means the entity making a Contribution and all its Affiliates.
Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, control means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity.
1. Grant of Copyright License
Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not.
2. Grant of Patent License
Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken.
3. No Trademark License
No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4.
4. Distribution Restriction
You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software.
5. Disclaimer of Warranty and Limitation of Liability
THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW ITS CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
6. Language
THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL.
END OF THE TERMS AND CONDITIONS
How to Apply the Mulan Permissive Software LicenseVersion 2 (Mulan PSL v2) to Your Software
To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps:
i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner;
ii Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package;
iii Attach the statement to the appropriate annotated syntax at the beginning of each source file.
Copyright (c) [Year] [name of copyright holder]
[Software Name] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.

View File

@@ -1,40 +1,34 @@
<div align="center">
# automate
<a ><img src="./source/logo.svg" width="120" height="120" alt="autoMate logo"></a>
An Electron application with React and TypeScript
<div style="font-size:24px;">autoMate</div>
## Recommended IDE Setup
</div>
<b>autoMate 就像出行中的共享单车,帮你完成软件的最后一个操作,只需 3 分钟就能将 AI 大脑植入任意一个软件。</b>
- [VSCode](https://code.visualstudio.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
<div align="center">
## Project Setup
### Install
📚[文档地址](https://s0soyusc93k.feishu.cn/wiki/JhhIwAUXJiBHG9kmt3YcXisWnec?from=from_copylink)
```bash
$ npm install
```
</div>
各产品优势对比如下。
### Development
![alt text](./source/github/diff.png)
```bash
$ npm run dev
```
## 功能介绍
### Build
autoMate 可以生成自动化代码帮助用户减少复性劳动,他做了三件事:
1. 了解、分析用户需求,将需求转换为人人都能看懂的用例;
2. 将用例转换成可执行的代码;
3. 运行代码并调试,解决异常问题。
使用 autoMate你可以
- 随时与AI进行沟通打开界面就能直接与智子进行沟通任何问题都可以交流
- 自动化自成代码:沟通过程中,智子会跟您对齐自动化需求,生成可执行的代码。
## 感谢以下人员提交的宝贵代码!
<a href="https://github.com/yuruotong1/autoMate/graphs/contributors">
<img src="https://contrib.rocks/image?repo=yuruotong1/autoMate" />
</a>
```bash
# For windows
$ npm run build:win
# For macOS
$ npm run build:mac
# For Linux
$ npm run build:linux
```

View File

View File

@@ -1,67 +0,0 @@
import inspect
import re
def action(description):
def class_decorator(func):
func.description = description
return func
return class_decorator
class ActionBase:
description=""
actions = []
def get_action_description(self, action_name):
action = getattr(self, action_name, None)
descriontion = action.__getattribute__("description")
return descriontion
def camel_to_snake(self, name):
name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()
def get_actions(self):
for func_str in dir(self):
func = self.__getattribute__(func_str)
if callable(func) and getattr(func, "description", False):
self.actions.append(func)
return self.actions
def package_actions_description(self):
res = []
for action in self.get_actions():
signature = inspect.signature(action)
parameters = signature.parameters
params = []
for name, param in parameters.items():
if name != "self":
params.append(f"{name}:{param._annotation.__name__}")
res.append(f"{self.camel_to_snake(__class__.__name__)}.{action.__name__}({','.join(params)}) #{action.description}")
return "\n".join(res)

View File

@@ -1,10 +0,0 @@
from actions.word_action import WordAction
class ActionUtil:
_actions = [WordAction]
@classmethod
def get_actions(cls):
return cls._actions

View File

@@ -1,13 +0,0 @@
from actions.action_base import action, ActionBase
class WordAction(ActionBase):
actions_description = "word基本操作"
def __init__(self):
pass
@action(description="批量修改表格中的内容,将表格的行和列修改为新内容")
def change_content_for_table(self, word_path: str, start_table_number: int, end_table_number: int, table_cell: int, table_row: int, new_content: str):
return True
@action(description="批量修改表格中的内容,将表格的行和列修改为新内容")
def tmp(self, tmp: object):
return True

View File

@@ -1,19 +0,0 @@
import subprocess
from pydantic import BaseModel, Field
from actions.action_base import ActionBase
class OpenApplicationInput(BaseModel):
path: str = Field(description="要查询的关键词", title="应用路径", default="")
class TableAction(ActionBase):
name: str = "打开应用"
description: str = "打开指定目录的应用"
args: OpenApplicationInput
def run(self, path):
print("运行了这里", path)
subprocess.Popen(path)

View File

@@ -1,21 +0,0 @@
from transformers import AutoTokenizer, AutoModelForCausalLM
import os
import torch
from utils.qt_util import QtUtil
def gemma():
print(torch.version)
# 项目根目录
model_path = os.path.join(QtUtil.get_root_path(), "agent", "model")
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoModelForCausalLM.from_pretrained(model_path, device_map="auto")
while True:
input_text = input("请输入问题?")
input_ids = tokenizer(input_text, return_tensors="pt").to("cuda")
outputs = model.generate(**input_ids, max_length=512)
print(tokenizer.decode(outputs[0]))
if __name__ == "__main__":
gemma()

View File

@@ -1,26 +0,0 @@
from actions.action_util import ActionUtil
from agent.prompt import programmer_prompt
from self_utils.llm_util import LLM_Util
class ProgrammerAgent:
def __init__(self):
action_descriptions = ""
for action_class in ActionUtil.get_actions():
action = action_class()
action_descriptions += action.package_actions_description() + "\n"
self.messages = [{"content": programmer_prompt.substitute(python_code=action_descriptions), "role": "system"}]
self.content = ""
def run(self, question):
self.messages.append({"content": question, "role": "user"})
res = ""
for text in LLM_Util().invoke(self.messages):
res += text
return res

View File

@@ -1,93 +0,0 @@
from string import Template
require_alignment_prompt="""# 上下文 #
你是一位资深的自动化产品经理,你不懂代码,与用户对齐需求,根据用户的需求编写自动化用例。
#############
# 目标 #
我希望你能分析用户的需求,与用户进行对齐,你记忆短暂容易忘输出内容前要先说一下你在做什么,请一步一步执行下面的过程,不要跳过任何一个步骤。
1. 确认用户需求【可省略】:如果需求明确可以省略这一步,如果用户需求中如果有不清楚的地方,请不要自己猜测,而是要向用户询问清楚,比如用户说打开桌面文件,你要问清楚是哪一个桌面文件;
2. 生成自动化用例:根据用户需求,按[步骤]生成自动化用例这是口语化的用例不要出现代码比如1. 获取桌面路径 2. 打开桌面文件a.text
3. 如果用例生成完毕,请在内容最开始加上“[自动化方案]”。
#############
# 风格 #
严谨认真
#############
# 语气 #
与用户对需求时要保持尊重
#############
# 受众 #
有自动化需求,想让你给一个自动化方案
#############
# 回复 #
[自动化方案]
1. [步骤1]
2. [步骤2]
3. [步骤3]。
#############
"""
programmer_prompt=Template("""# 上下文 #
你是一位高级python程序员根据产品经理的需求编写python代码。
#############
# 目标 #
我希望你够根据产品经理的自动化需求返回可执行的python代码。你返回的所有内容会直接传递到exec()的参数。
你可以直接使用以下封装好的【函数】
$python_code
#############
# 风格 #
请你编写python代码时要遵循PEP8规范代码简单易懂每一行代码都要用#编写注释并且在关键地方用#给出修改建议。
#############
# 语气 #
只有代码,不要有任何其他信息
#############
# 受众 #
会写python但是不太熟悉
#############
# 约束 #
返回的代码不要被```python```包裹只返回python代码以下是错误的例子
1. ```python
print("abc")
```
2. ```python
c = [i in range(10)]
print(c)
```
代码中的函数一定是python内置函数或第三方库或我提供的【函数】不要假想不存在的函数。
############
# 回复格式 #
[python代码]
#############
# 例子 #
只返回python代码不要返回任何其他信息比如
1. print("abc")
2. c = [i in range(10)]\nprint(c)
#############
""")
tools = [
{
"type": "function",
"function": {
"name": "execute",
"description": "execute python code",
"parameters": {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "python code",
},
},
"required": ["code"],
},
},
}]

View File

@@ -1,23 +0,0 @@
from actions.action_util import ActionUtil
from agent.prompt import require_alignment_prompt
from self_utils.llm_util import LLM_Util
# 需求对齐Agent负责与用户对齐需求
class RequireAlignmentAgent:
def __init__(self):
action_descriptions = ""
for action_class in ActionUtil.get_actions():
action = action_class()
action_descriptions += action.package_actions_description() + "\n"
self.messages = [{"content": require_alignment_prompt, "role": "system"}]
def run(self, question):
self.messages.append({"content": question, "role": "user"})
yield from LLM_Util().invoke(self.messages)

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
</dict>
</plist>

BIN
build/icon.icns Normal file

Binary file not shown.

BIN
build/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

View File

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -1,23 +0,0 @@
base:
数据存储位置:
description: 数据存储位置
config:
数据存储位置: local
components:
llm:
description: 用于配置大模型相关信息目前只支持openai
config:
api_key: 请输入api_key
api_url: "https://api.chatgpt.com/v1"
model: gpt-4-1106-preview
proxy: ""
browser:
description: 配置本电脑的浏览器信息,用于浏览器自动化
config:
浏览器类型: edge

View File

View File

@@ -1,6 +0,0 @@
class SearchData:
def __init__(self, title, url, short_description):
self.title = title
self.url = url
self.short_description = short_description

34
main.py
View File

@@ -1,34 +0,0 @@
import logging
import sys
import traceback
from PyQt6.QtWidgets import QApplication
from pages.chat_page import ChatPage
from self_utils.config import Config
# 设置日志
logging.basicConfig(level=logging.INFO)
class AutoMate:
def main(self):
Config()
self.page = ChatPage()
self.page.show_window()
def excepthook(exc_type, exc_value, exc_tb):
tb = "".join(traceback.format_exception(exc_type, exc_value, exc_tb))
print("catch exception:", tb)
if __name__ == "__main__":
try:
sys.excepthook = excepthook
app = QApplication(sys.argv)
automate = AutoMate()
automate.main()
sys.exit(app.exec())
except Exception as e:
traceback.print_exc(e)

View File

@@ -1,11 +0,0 @@
echo Y | pyinstaller -D -w --add-data=pages/*.ui:pages --add-data=source/*:source -i source/logo.ico --hidden-import=tiktoken_ext.openai_public --hidden-import=tiktoken_ext --name=autoMate main.py
mkdir dist/autoMate/_internal/self_utils
@REM move dist/autoMate/_internal/pages dist/autoMate/pages
@REM move dist/autoMate/_internal/source dist/autoMate/source
copy "config_tmp.yaml" "dist/autoMate/_internal/config.yaml"
cd dist
del "autoMate.zip"
powershell Compress-Archive -Path autoMate -DestinationPath autoMate.zip
cd ..

View File

@@ -1,145 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>586</width>
<height>447</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>20</x>
<y>60</y>
<width>531</width>
<height>211</height>
</rect>
</property>
<layout class="QVBoxLayout" name="config_list"/>
</widget>
<widget class="QScrollBar" name="verticalScrollBar">
<property name="geometry">
<rect>
<x>560</x>
<y>40</y>
<width>20</width>
<height>321</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
<widget class="QPushButton" name="saveButton">
<property name="geometry">
<rect>
<x>460</x>
<y>380</y>
<width>51</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>保存</string>
</property>
</widget>
<widget class="QPushButton" name="cancelButton">
<property name="geometry">
<rect>
<x>520</x>
<y>380</y>
<width>51</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>取消</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>10</x>
<y>30</y>
<width>54</width>
<height>12</height>
</rect>
</property>
<property name="text">
<string>输入</string>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>54</width>
<height>12</height>
</rect>
</property>
<property name="text">
<string>description</string>
</property>
</widget>
<widget class="Line" name="line">
<property name="geometry">
<rect>
<x>0</x>
<y>10</y>
<width>581</width>
<height>16</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>10</x>
<y>280</y>
<width>54</width>
<height>12</height>
</rect>
</property>
<property name="text">
<string>输出</string>
</property>
</widget>
<widget class="QWidget" name="verticalLayoutWidget_2">
<property name="geometry">
<rect>
<x>20</x>
<y>300</y>
<width>531</width>
<height>71</height>
</rect>
</property>
<layout class="QHBoxLayout" name="output_config"/>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>586</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -1,10 +0,0 @@
class BasePage:
def __init__(self):
self.ui = None
self.setup_up()
def show(self):
self.ui.show()
def setup_up(self):
pass

View File

@@ -1,201 +0,0 @@
import traceback
from PyQt6.QtCore import Qt, QThread, pyqtSignal
from PyQt6.QtWidgets import QApplication, QTextEdit, QPushButton, QLabel,QAbstractItemView, QListWidget, QSpacerItem, QSizePolicy, QListWidgetItem
from PyQt6.QtGui import QPixmap
from PyQt6 import QtGui, QtCore, QtWidgets
from agent.programmer_agent import ProgrammerAgent
from pages.python_execute import PythonExecute
from self_utils.qt_util import QtUtil
from pages.python_code_edit import PythonHighlighter, QCodeEditor
class ChatList(QListWidget):
def __init__(self, parent=None, chat_page=None):
super().__init__(parent)
self.chat_page = chat_page
self.setGeometry(QtCore.QRect(40, 0, 561, 550))
self.setObjectName("chat_list")
# 设置 QListWidget 的背景为透明
self.setStyleSheet("""background: transparent;border: none;""")
# 设置 QListWidget 的选择模式为 NoSelection
self.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
# 设置 QListWidget 的焦点策略为 NoFocus
self.setFocusPolicy(Qt.FocusPolicy.NoFocus)
# 隐藏垂直滚动条
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
# 隐藏水平滚动条
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.programmer_agent = ProgrammerAgent()
def mousePressEvent(self, event):
self.chat_page.action_list.set_visibility(False)
def _sender_render(self, role):
h_box = QtWidgets.QHBoxLayout()
if role == "system":
role_pic = QtUtil.get_icon("logo.png")
role_name = "智子"
else:
role_pic = QtUtil.get_icon("vip.png")
role_name = ""
# 创建 QPixmap 对象并加图片
pixmap = QPixmap(role_pic)
pixmap = pixmap.scaled(30, 30, QtCore.Qt.AspectRatioMode.KeepAspectRatio)
# 创建 QLabel 对象并设置其 pixmap
image_label = QLabel()
image_label.setPixmap(pixmap)
# 将 QLabel 对象添加到布局中
h_box.addWidget(image_label)
label = QLabel()
label.setText(role_name)
# 将 QLabel 对象添加到布局中
h_box.addWidget(label)
# 占位符
spacer = QSpacerItem(20, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
h_box.addItem(spacer)
# 设置每个子元素所占的比例
h_box.setStretch(0, 1)
h_box.setStretch(1, 1)
h_box.setStretch(2, 10)
return h_box
def _code_response_render(self, text, conversation_widget, conversation_box, conversation_item):
self.text_edit = QCodeEditor(display_line_numbers=True,
highlight_current_line=True,
syntax_high_lighter=PythonHighlighter,
)
self.text_edit.setStyleSheet("""
background-color: white;
border-radius: 10px;
font-size:16px;
""")
text = text.strip('```python').rstrip('```')
self.text_edit.setPlainText(text)
# 设置 widget、v_box 和 item 的大小
conversation_box.addWidget(self.text_edit)
conversation_item.setSizeHint(conversation_widget.size())
run_button_h_box= QtWidgets.QHBoxLayout()
spacer = QSpacerItem(20, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
run_button_h_box.addItem(spacer)
run_button_h_box.setStretch(0, 6)
# save_button = QPushButton("保存")
# save_button.setStyleSheet("background-color: grey; color: white;")
# run_button_h_box.addWidget(save_button)
# run_button_h_box.setStretch(1, 2)
run_button = QPushButton("运行")
run_button.clicked.connect(lambda:self.run_button_clicked(self.text_edit.toPlainText()))
run_button.setStyleSheet("background-color: green; color: white;")
run_button_h_box.addWidget(run_button)
run_button_h_box.setStretch(1, 4)
conversation_box.addLayout(run_button_h_box)
# 获取 QTextEdit 的文档的大小
doc_size = self.text_edit.document().size().toSize()
conversation_widget.setFixedHeight(doc_size.height()*25 + 20)
conversation_item.setSizeHint(conversation_widget.size()) # # 设置 QTextEdit 的背景为白色,边角为椭圆
# 获取 QTextEdit 的文档的大小
doc_size = self.text_edit.document().size().toSize()
conversation_widget.setFixedHeight(doc_size.height()*25 + 20)
conversation_item.setSizeHint(conversation_widget.size())
def run_button_clicked(self, text):
self.new_response("执行代码中...")
res = PythonExecute().run(text)
self.takeItem(self.count()-1)
self.new_response(f"<p style='color:green;'>代码执行完成,执行结果</p><br><code>{res}</code>")
def _text_response_render(self, text, conversation_widget, conversation_box, conversation_item):
self.text_edit = QTextEdit()
self.text_edit.setReadOnly(True)
self.text_edit.setHtml(text)
def update_size(widget, item):
# 获取 QTextEdit 的文档的大小
doc_size = self.text_edit.document().size().toSize()
print("doc_size", doc_size.height())
# 设置 widget、v_box 和 item 的大小
widget.setFixedHeight(doc_size.height() + 55)
item.setSizeHint(widget.size())
self.text_edit.setStyleSheet("""
background-color: white;
border-radius: 10px;
font-size:14px;
""")
self.text_edit.document().documentLayout().documentSizeChanged.connect(lambda: update_size(conversation_widget, conversation_item))
conversation_box.addWidget(self.text_edit)
def new_response(self, text, type="text", role="system"):
conversation_widget = QtWidgets.QWidget()
conversation_widget.setGeometry(QtCore.QRect(110, 100, 160, 80))
conversation_box = QtWidgets.QVBoxLayout(conversation_widget)
conversation_box.addLayout(self._sender_render(role))
conversation_item = QListWidgetItem()
self.insertItem(self.count(), conversation_item)
self.setItemWidget(conversation_item, conversation_widget)
if type=="text":
self._text_response_render(text, conversation_widget, conversation_box, conversation_item)
elif type=="code":
self._code_response_render(text, conversation_widget, conversation_box, conversation_item)
self.text_edit.document().setDocumentMargin(10) # 将 item 添加到 QListWidget
self.scrollToBottom()
def stream_response(self, stream):
conversation_widget = QtWidgets.QWidget()
conversation_widget.setGeometry(QtCore.QRect(110, 100, 160, 80))
conversation_box = QtWidgets.QVBoxLayout(conversation_widget)
conversation_box.addLayout(self._sender_render("system"))
conversation_item = QListWidgetItem()
self._text_response_render("我在思考中...", conversation_widget, conversation_box, conversation_item)
self.text_edit.setStyleSheet("""
background-color: white;
border-radius: 10px;
font-size:14px;
""")
self.text_edit.document().setDocumentMargin(10) # 将 item 添加到 QListWidget
self.insertItem(self.count(), conversation_item)
self.setItemWidget(conversation_item, conversation_widget)
self.scrollToBottom()
self.stream_thread = StreamOutput(stream, self.programmer_agent)
self.first_call = True
self.stream_thread.stream_signal.connect(self.append_text)
self.stream_thread.code_generate_before_signal.connect(lambda : self.new_response("我将根据以上用例生成代码,思考中..."))
self.stream_thread.code_generate_after_signal.connect(self.code_generate_after)
self.stream_thread.start()
def code_generate_after(self, code):
self.takeItem(self.count()-1)
self.new_response(code, type="code")
def append_text(self, text):
if self.first_call:
self.text_edit.setPlainText("")
self.first_call = False
cursor = self.text_edit.textCursor() # 获取当前的文本光标
cursor.movePosition(QtGui.QTextCursor.MoveOperation.End) # 移动光标到文本末尾
cursor.insertText(text) # 插入文本但不新起一行
class StreamOutput(QThread):
stream_signal = pyqtSignal(str)
code_generate_before_signal = pyqtSignal()
code_generate_after_signal = pyqtSignal(str)
def __init__(self, stream, programmer_agent):
QThread.__init__(self)
self.stream = stream
self.programmer_agent = programmer_agent
def run(self):
response = ""
for text in self.stream:
response += text
self.stream_signal.emit(text)
if "[自动化方案]" in response:
self.code_generate_before_signal.emit()
content = response.split("[自动化方案]")[1]
code = self.programmer_agent.run(content)
self.code_generate_after_signal.emit(code)

View File

@@ -1,257 +0,0 @@
import traceback
from PyQt6 import QtCore
from PyQt6.QtCore import QThread, pyqtSignal, Qt
from PyQt6.QtGui import QIcon
from PyQt6.QtWidgets import QApplication, QSystemTrayIcon, QMainWindow, QLabel, QTextEdit, QListWidgetItem, QSpacerItem, QSizePolicy, QAbstractItemView, QListWidget, QMenu, QPushButton
from actions.action_util import ActionUtil
from agent.require_alignment_agent import RequireAlignmentAgent
from pages.chat_list import ChatList
from pages.config_page import ConfigPage
from pages.plugin_page import PluginPage
from self_utils.global_keyboard_listen import GlobalKeyboardListen
from self_utils.qt_util import QtUtil
class ActionItems(QListWidgetItem):
def __init__(self, action, chat_page):
super().__init__()
self.action = action
self.chat_page = chat_page
self.action.get_signal().finish_run_signal.connect(self.save_output)
self.action.get_signal().start_run_signal.connect(self.start_run)
# 创建一个 QLabel 作为列表项的小部件
self.label = QLabel()
text = f"<p style='font-size:15px;color:blue;margin-bottom:0;'>{self.action.name}</p><p style='font-size:11px;color:gray;margin-top:0;'>{self.action.description}</p>"
self.label.setText(text)
self.setSizeHint(self.label.sizeHint())
def save_output(self, res):
self.chat_page.chat_list.new_response(f"执行成功,执行结果:{str(res)}", "system")
def start_run(self):
self.chat_page.action_list.set_visibility(False)
self.chat_page.chat_input.clear()
self.chat_page.chat_list.new_response(f"执行{self.action.name}动作中:\n执行动作描述:{self.action.description}\n执行参数:{self.action.args}", "system")
class ActionList(QListWidget):
def __init__(self, parent=None, chat_page=None):
super().__init__(parent)
self.chat_page = chat_page
self.setVisible(False)
self.setFocusPolicy(Qt.FocusPolicy.TabFocus)
self.setSpacing(3)
self.setGeometry(QtCore.QRect(40, 390, 251, 181))
self.setStyleSheet("border: none;")
self.setObjectName("action_list")
def filter_action(self, text):
self.clear()
actions = [i(args={}) for i in ActionUtil.get_funcs()]
for action in actions:
if text=="" or text in action.name:
item = ActionItems(action, chat_page=self.chat_page)
self.addItem(item)
self.setItemWidget(item, item.label)
if self.count()==0:
label = QLabel("<h2>没找到可用的行为</h2><br><p style='color:gray;'>欢迎进群提需求,我们将火速更新!</p>")
item = QListWidgetItem()
self.addItem(item)
self.setItemWidget(item, label)
item.setSizeHint(label.sizeHint())
def mousePressEvent(self, event):
super(QListWidget, self).mousePressEvent(event)
# 获取双击的项
item = self.itemAt(event.pos())
if item:
item.action.config_page_show()
event.accept()
def set_visibility(self, visible: bool):
self.setVisible(visible)
class RequireAligenmentThread(QThread):
finished_signal = pyqtSignal(object)
def __init__(self, text, require_alignment_agent):
QThread.__init__(self)
self.text = text
self.require_alignment_agent = require_alignment_agent
def run(self):
try:
content_stream = self.require_alignment_agent.run(self.text)
self.finished_signal.emit(content_stream)
except Exception as e:
traceback.print_exc(e)
class ChatInput(QTextEdit):
def __init__(self, parent=None, chat_page=None):
super().__init__(parent)
self.worker_thread = None
self.chat_page = chat_page
self.textChanged.connect(self.on_text_changed)
self.previous_text = ""
self.setFixedWidth(560)
self.setGeometry(QtCore.QRect(40, 580, 600, 50))
self.setStyleSheet("border-radius: 30px")
self.setObjectName("chat_input")
self.setPlaceholderText("请输入“/”,选择运行的指令")
def keyPressEvent(self, event):
if event.key() == Qt.Key.Key_Return:
self.chat_page.chat_list.new_response(f"{self.toPlainText()}", role="user")
self.require_alignment_thread = RequireAligenmentThread(self.toPlainText(), self.chat_page.require_alignment_agent)
# 清空输入框
self.clear()
# 连接线程的 finished 信号到槽函数增加对话UI
self.require_alignment_thread.finished_signal.connect(lambda x: self.chat_page.chat_list.stream_response(x))
self.require_alignment_thread.start()
event.accept()
else:
super().keyPressEvent(event)
def code_finish(self, code):
self.chat_page.delete_last_conversation()
self.chat_page.new_conversation(text=code, type="code")
def on_text_changed(self):
current_text = self.toPlainText()
# 当输入中文不选择具体的文字时,也会进入到这里
if self.previous_text == current_text:
return
# 当输入"/"时显示action_list
if current_text == "/":
self.chat_page.action_list.set_visibility(True)
self.chat_page.action_list.filter_action("")
# 当删除/时隐藏action_list
elif "/" not in current_text and self.chat_page.action_list.isVisible():
self.chat_page.action_list.set_visibility(False)
# 当输入"/"并且追加内容时会对list内容进行过滤
elif current_text.startswith("/") and self.chat_page.action_list.isVisible():
current_text_without_slash = current_text[1:]
self.chat_page.action_list.filter_action(current_text_without_slash)
self.previous_text = current_text
def mousePressEvent(self, event):
self.chat_page.action_list.set_visibility(False)
# # 窗口激活时,输入框的焦点设置到这里
# def event(self, event):
# if event.type() == QEvent.Type.WindowActivate:
# # 当窗口激活时,智子根据上下文推送合适的工具
# self.setFocus()
# return True
# return super().event(event)
interface_ui = QtUtil.load_ui_type("chat_page.ui")
class ChatPage(QMainWindow, interface_ui):
def __init__(self):
super().__init__()
self.setupUi(self)
self.setting_page = None
self.action_list = None
self.require_alignment_agent = RequireAlignmentAgent()
self.setup_up()
self.chat_list.new_response(
"<b>你好我叫智子你的智能Agent助手</b><br><br>你可以输入“/”搜索行为,或者可有什么要求可以随时吩咐!"
)
# self.chat_list.new_response("""
# import os # 导入os库以进行操作系统相关操作
# # [步骤1] 获取当前操作系统用户的主目录路径
# home_path = os.path.expanduser('~')
# # [步骤2] 在用户主目录路径中定位到桌面路径
# if os.name == 'nt': # Windows系统
# desktop_path = os.path.join(home_path, 'Desktop')
# else: # macOS或Linux系统
# desktop_path = os.path.join(home_path, 'Desktop')
# # [步骤3] 在桌面路径中查找文件名为“a.txt”的文件
# file_path = os.path.join(desktop_path, 'a.txt')
# # [步骤4] 打开找到的“a.txt”文件
# try:
# with open(file_path, 'r') as file: # 使用with语句安全打开文件
# # [步骤5] 读取文件“a.txt”的内容并将其展示给用户
# content = file.read() # 读取文件内容
# print(content) # 打印读取的内容
# except FileNotFoundError:
# print("文件未找到请确保文件a.txt在桌面上。")
# except Exception as e:
# print(f"打开文件时遇到错误: {e}")
# """, type="code")
def setup_up(self):
self.chat_input = ChatInput(parent=self.centralwidget, chat_page=self)
self.chat_list = ChatList(parent=self.centralwidget, chat_page=self)
self.action_list = ActionList(parent=self.centralwidget, chat_page=self)
setting_action = self.setting_action
setting_action.triggered.connect(self.open_setting_page)
self.plugin = self.plugin
self.plugin.triggered.connect(self.open_plugin_page)
# 设置托盘
self.trayIcon = QSystemTrayIcon(self) # 创建一个系统托盘图标对象
self.trayIcon.setIcon(QIcon(QtUtil.get_icon("logo.ico"))) # 设置托盘图标为指定的图片文件
self.trayIcon.show() # 显示托盘图标
self.trayIcon.setToolTip('任意位置按下鼠标中键显示窗口')
# 由于我们创建的是无边框的工具窗口,所以这里设置一个托盘的激活功能
self.trayIcon.activated.connect(self.onTrayIconActivated) # 当托盘图标被激活时调用onTrayIconActivated函数
# 添加右键菜单
self.contextMenu = QMenu(self) # 创建自定义的上下文菜单
self.contextMenu.addAction("显示窗口", self.show_window) # 添加动作:显示窗口
self.contextMenu.addSeparator() # 添加分线
self.contextMenu.addAction("退出", QApplication.quit) # 添加动作:关闭程序
# 将上下文菜单与托盘图标关联起来
self.trayIcon.setContextMenu(self.contextMenu)
# self.setWindowFlag(Qt.WindowType.WindowStaysOnTopHint, True)
# 监听鼠标中键,然后启动窗口
self.global_keyboard_listen = GlobalKeyboardListen()
self.global_keyboard_listen.mouse_middle_signal.connect(self.show_window)
self.global_keyboard_listen.start()
def show_window(self):
self.showMinimized()
self.showNormal()
self.activateWindow()
def onTrayIconActivated(self, reason): # 当托盘图标被激活时,这个函数会被调用
if reason == QSystemTrayIcon.ActivationReason.DoubleClick: # 如果激活原因是双击托盘图标
self.show_window()
def open_plugin_page(self):
self.plugin_page = PluginPage()
self.plugin_page.show()
def open_setting_page(self):
self.setting_page = ConfigPage()
self.setting_page.show()
def mousePressEvent(self, event):
self.action_list.setVisible(False)

View File

@@ -1,55 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>autoMate</class>
<widget class="QMainWindow" name="autoMate">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>647</width>
<height>678</height>
</rect>
</property>
<property name="windowTitle">
<string>autoMate</string>
</property>
<property name="styleSheet">
<string notr="true">QTextEdit {border-radius: 10px;}</string>
</property>
<widget class="QWidget" name="centralwidget">
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>647</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="file_menu">
<property name="title">
<string>文件</string>
</property>
<addaction name="setting_action"/>
<addaction name="plugin"/>
<addaction name="separator"/>
</widget>
<addaction name="file_menu"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="setting_action">
<property name="text">
<string>设置</string>
</property>
</action>
<widget class="QStatusBar" name="statusbar"/>
<action name="plugin">
<property name="text">
<string>插件</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -1,72 +0,0 @@
from self_utils.config import Config
from self_utils.qt_util import QtUtil
from PyQt6.QtWidgets import QMainWindow, QVBoxLayout, QLabel, QLineEdit, QWidget, QGridLayout, QSpacerItem, QSizePolicy
interface_ui = QtUtil.load_ui_type("config_page.ui")
class ConfigPage(QMainWindow, interface_ui):
def __init__(self):
super().__init__()
self.setupUi(self)
self.config = Config()
self.tmp_config = self.config.config
self.setup_up()
self.save_button.clicked.connect(self.save_setting)
self.cancel_button.clicked.connect(self.cancel_btn)
def setup_up(self):
for type_key, type_value in self.tmp_config.items():
widget = QWidget()
widget.setObjectName(type_key)
self.config_list.addTab(widget, type_key)
all_settings = QVBoxLayout()
widget.setLayout(all_settings)
for sub_title, sub_value in type_value.items():
'''
components:
feishu:
description: 配置飞书相关信息,用于与飞书进行交互
config:
app_id: 请输入api_id
app_secret: 请输入app_secret
'''
# feishu
sub_title_label = QLabel(text=sub_title)
# 配置飞书相关信息,用于与飞书进行交互
sub_description_label = QLabel(text=sub_value["description"])
grid_layout = QGridLayout()
grid_layout.addWidget(sub_title_label, 0, 0)
grid_layout.addWidget(sub_description_label, 1, 0)
all_settings.addLayout(grid_layout)
row = 2
# app_id, 请输入api_id
# app_secret, 请输入app_secret
for config_key, config_value in sub_value["config"].items():
config_key_label = QLabel()
# app_id
config_key_label.setText(config_key)
line_edit = QLineEdit()
# 请输入api_id
line_edit.setText(config_value)
line_edit.textChanged.connect(lambda x, k=config_key, c=sub_value["config"]: self.text_changed(k, x, c))
grid_layout.addWidget(config_key_label, row, 0)
grid_layout.addWidget(line_edit, row, 1)
row += 1
spacer = QSpacerItem(10, 10, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
grid_layout.addItem(spacer, row, 0)
spacer = QSpacerItem(10, 10, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
all_settings.addItem(spacer)
def text_changed(self, key, value, config):
config[key] = value
def save_setting(self):
self.config.config = self.tmp_config
self.config.update_config()
self.close()
def cancel_btn(self):
self.hide()

View File

@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>586</width>
<height>447</height>
</rect>
</property>
<property name="windowTitle">
<string>设置</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QPushButton" name="save_button">
<property name="geometry">
<rect>
<x>460</x>
<y>380</y>
<width>51</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>保存</string>
</property>
</widget>
<widget class="QPushButton" name="cancel_button">
<property name="geometry">
<rect>
<x>520</x>
<y>380</y>
<width>51</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>取消</string>
</property>
</widget>
<widget class="QTabWidget" name="config_list">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>571</width>
<height>351</height>
</rect>
</property>
<property name="tabPosition">
<enum>QTabWidget::North</enum>
</property>
<property name="tabShape">
<enum>QTabWidget::Rounded</enum>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<property name="elideMode">
<enum>Qt::ElideNone</enum>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>586</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -1,91 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>508</width>
<height>424</height>
</rect>
</property>
<property name="windowTitle">
<string>autoMate</string>
</property>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>60</x>
<y>120</y>
<width>54</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>用户</string>
</property>
</widget>
<widget class="QTextEdit" name="textEdit">
<property name="geometry">
<rect>
<x>130</x>
<y>110</y>
<width>251</width>
<height>41</height>
</rect>
</property>
</widget>
<widget class="QTextEdit" name="textEdit_2">
<property name="geometry">
<rect>
<x>130</x>
<y>210</y>
<width>251</width>
<height>41</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>60</x>
<y>220</y>
<width>54</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>密码</string>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>190</x>
<y>280</y>
<width>121</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>没有账户?点击注册</string>
</property>
</widget>
<widget class="QPushButton" name="pushButton">
<property name="geometry">
<rect>
<x>200</x>
<y>320</y>
<width>81</width>
<height>31</height>
</rect>
</property>
<property name="text">
<string>登陆</string>
</property>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -1,73 +0,0 @@
# Form implementation generated from reading pages file 'login.pages'
#
# Created by: PyQt6 pages code generator 6.4.2
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
import leancloud
from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt6.QtGui import QKeyEvent
from PyQt6.QtWidgets import QWidget, QLineEdit
from pages.edit_page import EditPage
class LoginPage(QWidget):
def __init__(self):
super().__init__()
self.edit_page = None
self.password_text_edit = None
self.username_text_edit = None
self.label = None
self.label_3 = None
self.password_label = None
self.pushButton = None
self.setupUi(self)
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(508, 424)
self.label = QtWidgets.QLabel(parent=Form)
self.label.setGeometry(QtCore.QRect(60, 120, 54, 16))
self.label.setObjectName("username_label")
self.username_text_edit = QtWidgets.QLineEdit(parent=Form)
self.username_text_edit.setGeometry(QtCore.QRect(130, 110, 251, 41))
self.username_text_edit.setObjectName("username_input")
self.password_text_edit = QtWidgets.QLineEdit(parent=Form)
self.password_text_edit.setGeometry(QtCore.QRect(130, 210, 251, 41))
self.password_text_edit.setObjectName("password_input")
self.password_text_edit.setEchoMode(QLineEdit.EchoMode.Password)
self.password_label = QtWidgets.QLabel(parent=Form)
self.password_label.setGeometry(QtCore.QRect(60, 220, 54, 16))
self.password_label.setObjectName("password_label")
self.label_3 = QtWidgets.QLabel(parent=Form)
self.label_3.setGeometry(QtCore.QRect(190, 280, 121, 16))
self.label_3.setObjectName("register_label")
self.pushButton = QtWidgets.QPushButton(parent=Form)
self.pushButton.setGeometry(QtCore.QRect(200, 320, 81, 31))
self.pushButton.setObjectName("login_button")
self.pushButton.clicked.connect(self.login_button_click)
self.translate_ui(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def login_button_click(self):
username = self.username_text_edit.text()
password = self.password_text_edit.text()
leancloud.User().login(username, password)
authenticated = leancloud.User.get_current().is_authenticated()
if authenticated:
# 写入文件
tmp_file = "./session"
with open(tmp_file, 'wb') as file:
session = leancloud.User().get_current().get_session_token()
file.write(session.encode())
self.hide()
self.edit_page = EditPage()
def translate_ui(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "autoMate"))
self.label.setText(_translate("Form", "用户"))
self.password_label.setText(_translate("Form", "密码"))
self.label_3.setText(_translate("Form", "没有账户?点击注册"))
self.pushButton.setText(_translate("Form", "登陆"))

View File

@@ -1,24 +0,0 @@
from PyQt6.QtWidgets import QMainWindow
from PyQt6 import QtWidgets
from self_utils.qt_util import QtUtil
interface_ui = QtUtil.load_ui_type("plugin_page.ui")
class PluginPage(QMainWindow, interface_ui):
def __init__(self, parent=None):
super(PluginPage, self).__init__(parent)
self.setupUi(self)
self.init_ui()
def init_ui(self):
self.add_plugin_button.clicked.connect(self.add_plugin)
self.delete_plugin_button.clicked.connect(self.delete_plugin)
def add_plugin(self):
file_dialog = QtWidgets.QFileDialog(self)
file_dialog.setFileMode(QtWidgets.QFileDialog.FileMode.Directory) # 设置为目录选择模式
if file_dialog.exec():
selected_folder = file_dialog.selectedFiles() # 这将返回选中的文件夹列表
print("Selected folder:", selected_folder[0])
def delete_plugin(self):
print("delete_plugin")

View File

@@ -1,68 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>802</width>
<height>587</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QListWidget" name="listWidget">
<property name="geometry">
<rect>
<x>60</x>
<y>80</y>
<width>661</width>
<height>451</height>
</rect>
</property>
</widget>
<widget class="QPushButton" name="add_plugin_button">
<property name="geometry">
<rect>
<x>60</x>
<y>50</y>
<width>75</width>
<height>24</height>
</rect>
</property>
<property name="text">
<string>+</string>
</property>
</widget>
<widget class="QPushButton" name="delete_plugin_button">
<property name="geometry">
<rect>
<x>140</x>
<y>50</y>
<width>75</width>
<height>24</height>
</rect>
</property>
<property name="text">
<string>-</string>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>802</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -1,329 +0,0 @@
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QTextEdit
from PyQt6.QtCore import QRect, pyqtSignal
from PyQt6.QtGui import QFont, QColor, QPainter, QTextFormat
from PyQt6.QtWidgets import QWidget, QPlainTextEdit, QTextEdit
from PyQt6 import QtGui, QtCore
class LineNumberArea(QWidget):
def __init__(self, editor):
QWidget.__init__(self, editor)
self.editor = editor
self.editor.blockCountChanged.connect(self.update_width)
self.editor.updateRequest.connect(self.update_contents)
self.font = QFont()
self.numberBarColor = QColor("#e8e8e8")
def paintEvent(self, event):
# Override paintEvent to draw the line numbers
painter = QPainter(self)
painter.fillRect(event.rect(), self.numberBarColor)
block = self.editor.firstVisibleBlock()
# Iterate over all visible text blocks in the document.
while block.isValid():
block_number = block.blockNumber()
block_top = self.editor.blockBoundingGeometry(block).translated(self.editor.contentOffset()).top()
# Check if the position of the block is outside the visible area.
if not block.isVisible() or block_top >= event.rect().bottom():
break
# We want the line number for the selected line to be bold.
if block_number == self.editor.textCursor().blockNumber():
self.font.setBold(True)
painter.setPen(QColor("#000000"))
else:
self.font.setBold(False)
painter.setPen(QColor("#717171"))
painter.setFont(self.font)
# Draw the line number right justified at the position of the line.
paint_rect = QRect(0, int(block_top), self.width(), self.editor.fontMetrics().height())
painter.drawText(paint_rect, Qt.AlignmentFlag.AlignRight, str(block_number + 1))
block = block.next()
painter.end()
QWidget.paintEvent(self, event)
# 根据文档的总行数来计算宽度
def get_width(self):
count = self.editor.blockCount()
# width = self.fontMetrics().width(str(count)) + 10
width = self.fontMetrics().horizontalAdvance(str(count)) + 5
return width
# 设置宽度
def update_width(self):
width = self.get_width()
if self.width() != width:
self.setFixedWidth(width)
self.editor.setViewportMargins(width, 0, 0, 0);
# 更行内容
def update_contents(self, rect, scroll):
if scroll:
self.scroll(0, scroll)
else:
self.update(0, rect.y(), self.width(), rect.height())
if rect.contains(self.editor.viewport().rect()):
font_size = self.editor.currentCharFormat().font().pointSize()
self.font.setPointSize(font_size)
self.font.setStyle(QFont.Style.StyleNormal)
self.update_width()
class QCodeEditor(QPlainTextEdit):
def __init__(self, display_line_numbers=True, highlight_current_line=True,
syntax_high_lighter=None, *args):
"""
Parameters
----------
display_line_numbers : bool
switch on/off the presence of the lines number bar
highlight_current_line : bool
switch on/off the current line highlighting
syntax_high_lighter : QSyntaxHighlighter
should be inherited from QSyntaxHighlighter
"""
super(QCodeEditor, self).__init__(*args)
self.setFont(QFont("Microsoft YaHei UI Light", 12))
self.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap)
self.DISPLAY_LINE_NUMBERS = display_line_numbers
if display_line_numbers:
self.number_bar = LineNumberArea(self)
if highlight_current_line:
self.currentLineNumber = None
self.currentLineColor = self.palette().alternateBase()
self.cursorPositionChanged.connect(self.highlight_current_line)
if syntax_high_lighter is not None: # add highlighter to text document
self.highlighter = syntax_high_lighter(self.document())
# 默认选中第一行
cursor = self.textCursor()
cursor.movePosition(QtGui.QTextCursor.MoveOperation.Start)
self.setTextCursor(cursor)
self.highlight_current_line() # 确保第一行高亮
def resizeEvent(self, *e):
"""overload resizeEvent handler"""
if self.DISPLAY_LINE_NUMBERS: # resize LineNumberArea widget
cr = self.contentsRect()
rec = QRect(cr.left(), cr.top(), self.number_bar.get_width(), cr.height())
self.number_bar.setGeometry(rec)
QPlainTextEdit.resizeEvent(self, *e)
# 获取代码
def text(self):
return self.toPlainText()
def highlight_current_line(self):
new_current_line_number = self.textCursor().blockNumber()
if new_current_line_number != self.currentLineNumber:
self.currentLineNumber = new_current_line_number
hi_selection = QTextEdit.ExtraSelection()
hi_selection.format.setBackground(self.currentLineColor)
hi_selection.format.setProperty(QTextFormat.Property.FullWidthSelection, True)
hi_selection.cursor = self.textCursor()
hi_selection.cursor.clearSelection()
self.setExtraSelections([hi_selection])
def format_syn(color, style=''):
"""Return a QTextCharFormat with the given attributes.
"""
_color = QtGui.QColor()
_color.setNamedColor(color)
_format = QtGui.QTextCharFormat()
_format.setForeground(_color)
if 'bold' in style:
# QtGui.QFont.setBold(True)
_format.setFontWeight(QtGui.QFont.bold.__sizeof__())
if 'italic' in style:
_format.setFontItalic(True)
return _format
# Syntax styles that can be shared by all languages
STYLES = {
'keyword': format_syn('blue'),
'operator': format_syn('red'),
'brace': format_syn('darkGray'),
'defclass': format_syn('black', 'bold'),
'string': format_syn('magenta'),
'string2': format_syn('darkMagenta'),
'comment': format_syn('darkGreen', 'italic'),
'self': format_syn('black', 'italic'),
'numbers': format_syn('brown'),
}
class PythonHighlighter(QtGui.QSyntaxHighlighter):
"""Syntax highlighter for the Python language.
"""
# Python keywords
keywords = [
'and', 'assert', 'break', 'class', 'continue', 'def',
'del', 'elif', 'else', 'except', 'exec', 'finally',
'for', 'from', 'global', 'if', 'import', 'in',
'is', 'lambda', 'not', 'or', 'pass', 'print',
'raise', 'return', 'try', 'while', 'yield',
'None', 'True', 'False',
]
# Python operators
operators = [
r'=',
# Comparison
r'==', r'!=', r'<', r'<=', r'>', r'>=',
# Arithmetic
r'\+', r'-', r'\*', r'/', r'//', r'\%', r'\*\*',
# In-place
r'\+=', r'-=', r'\*=', r'/=', r'\%=',
# Bitwise
r'\^', r'\|', r'\&', r'\~', r'>>', r'<<',
]
# Python braces
braces = [
r'\{', r'\}', r'\(', r'\)', r'\[', r'\]',
]
def __init__(self, parent: QtGui.QTextDocument) -> None:
super().__init__(parent)
# Multi-line strings (expression, flag, style)
self.tri_single = (QtCore.QRegularExpression("'''"), 1, STYLES['string2'])
self.tri_double = (QtCore.QRegularExpression('"""'), 2, STYLES['string2'])
rules = []
# Keyword, operator, and brace rules
rules += [(r'\b%s\b' % w, 0, STYLES['keyword'])
for w in PythonHighlighter.keywords]
rules += [(r'%s' % o, 0, STYLES['operator'])
for o in PythonHighlighter.operators]
rules += [(r'%s' % b, 0, STYLES['brace'])
for b in PythonHighlighter.braces]
# All other rules
rules += [
# 'self'
(r'\bself\b', 0, STYLES['self']),
# 'def' followed by an identifier
(r"\bdef\b\s*(\w+)", 1, STYLES['defclass']),
# 'class' followed by an identifier
(r'\bclass\b\s*(\w+)', 1, STYLES['defclass']),
# Numeric literals
(r'\b[+-]?[0-9]+[lL]?\b', 0, STYLES['numbers']),
(r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b', 0, STYLES['numbers']),
(r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', 0, STYLES['numbers']),
# Double-quoted string, possibly containing escape sequences
(r'"[^"\\]*(\\.[^"\\]*)*"', 0, STYLES['string']),
# Single-quoted string, possibly containing escape sequences
(r"'[^'\\]*(\\.[^'\\]*)*'", 0, STYLES['string']),
# From '#' until a newline
(r'#[^\n]*', 0, STYLES['comment']),
]
# Build a QRegExp for each pattern
self.rules = [(QtCore.QRegularExpression(pat), index, fmt)
for (pat, index, fmt) in rules]
def highlightBlock(self, text):
"""Apply syntax highlighting to the given block of text.
"""
self.tripleQuoutesWithinStrings = []
# Do other syntax formatting
for expression, nth, format in self.rules:
index = expression.match(text, 0).capturedStart(0)
if index >= 0:
# if there is a string we check
# if there are some triple quotes within the string
# they will be ignored if they are matched again
if expression.pattern() in [r'"[^"\\]*(\\.[^"\\]*)*"', r"'[^'\\]*(\\.[^'\\]*)*'"]:
# 匹配到三个引号
innerIndex = self.tri_single[0].match(text, index + 1).capturedStart(0)
if innerIndex == -1:
innerIndex = self.tri_double[0].match(text, index + 1).capturedStart(0)
if innerIndex != -1:
tripleQuoteIndexes = range(innerIndex, innerIndex + 3)
self.tripleQuoutesWithinStrings.extend(tripleQuoteIndexes)
while index >= 0:
# 跳过三引号
if index in self.tripleQuoutesWithinStrings:
index += 1
continue
# We actually want the index of the nth match
index = expression.match(text, index).capturedStart(nth)
length = expression.match(text, index).capturedLength(nth)
self.setFormat(index, length, format)
index = expression.match(text, index + length).capturedStart(0)
self.setCurrentBlockState(0)
# Do multi-line strings
in_multiline = self.match_multiline(text, *self.tri_single)
if not in_multiline:
in_multiline = self.match_multiline(text, *self.tri_double)
def match_multiline(self, text, delimiter, in_state, style):
"""Do highlight of multi-line strings. ``delimiter`` should be a
``QRegExp`` for triple-single-quotes or triple-double-quotes, and
``in_state`` should be a unique integer to represent the corresponding
state changes when inside those strings. Returns True if we're still
inside a multi-line string when this function is finished.
"""
# If inside triple-single quotes, start at 0
if self.previousBlockState() == in_state:
start = 0
add = 0
# Otherwise, look for the delimiter on this line
else:
start = delimiter.match(text).capturedStart(0)
# skipping triple quotes within strings
if start in self.tripleQuoutesWithinStrings:
return False
# Move past this match
add = delimiter.match(text).capturedLength()
# As long as there's a delimiter match on this line...
while start >= 0: # Look for the ending delimiter
end = delimiter.match(text, start + add).capturedStart(0)
# Ending delimiter on this line?
if end >= add:
length = end - start + add + delimiter.match(text).capturedLength()
self.setCurrentBlockState(0)
# No; multi-line string
else:
self.setCurrentBlockState(in_state)
length = len(text) - start + add
# Apply formatting
self.setFormat(start, length, style)
# Look for the next match
start = delimiter.match(text, start + length).capturedStart()
# Return True if still inside a multi-line string, False otherwise
if self.currentBlockState() == in_state:
return True
else:
return False

View File

@@ -1,24 +0,0 @@
import io
import sys
import traceback
class PythonExecute:
def run(self, code):
# 创建一个 StringIO 对象来捕获输出
output = io.StringIO()
# 保存当前的 stdout
old_stdout = sys.stdout
sys.stdout = output
try:
# 执行代码
exec(code)
except Exception as e:
# 如果执行出错,返回错误的堆栈跟踪
sys.stdout = old_stdout
return traceback.format_exc()
finally:
# 恢复原来的 stdout
sys.stdout = old_stdout
# 获取捕获的输出并返回
return output.getvalue()

View File

@@ -1,120 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>setting_window</class>
<widget class="QMainWindow" name="setting_window">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>606</width>
<height>443</height>
</rect>
</property>
<property name="windowTitle">
<string>设置</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>160</x>
<y>70</y>
<width>341</width>
<height>181</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>openai_key</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="openai_key"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>openai_url</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="openai_url">
<property name="text">
<string>https://api.fast-tunnel.one/v1/</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>model</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="openai_model">
<property name="text">
<string>gpt-4-1106-preview</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QPushButton" name="save_btn">
<property name="geometry">
<rect>
<x>380</x>
<y>330</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>保存</string>
</property>
</widget>
<widget class="QPushButton" name="cancel_btn">
<property name="geometry">
<rect>
<x>480</x>
<y>330</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>取消</string>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>606</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -1,52 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>508</width>
<height>205</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<widget class="QWidget" name="widget" native="true">
<property name="geometry">
<rect>
<x>90</x>
<y>40</y>
<width>211</width>
<height>121</height>
</rect>
</property>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>30</x>
<y>10</y>
<width>54</width>
<height>12</height>
</rect>
</property>
<property name="text">
<string>name</string>
</property>
</widget>
<widget class="QListWidget" name="listWidget">
<property name="geometry">
<rect>
<x>20</x>
<y>30</y>
<width>191</width>
<height>81</height>
</rect>
</property>
</widget>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -1,42 +0,0 @@
[tool.poetry]
name = "automate"
version = "0.1.0"
description = ""
authors = ["yuruotong"]
readme = "README.md"
[tool.poetry.dependencies]
python = ">=3.9,<3.13"
PyYAML = "6.0.1"
requests = "2.31.0"
selenium = "4.16.0"
webdriver-manager = "4.0.1"
pytest = "^8.0.1"
pyqt6 = "^6.6.1"
pyautogui = "^0.9.54"
pyinstaller = "^6.5.0"
pygments = "^2.17.2"
python-docx = "^1.1.2"
pynput = "^1.7.7"
open-interpreter = "^0.2.5"
openai = "^1.30.5"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[[tool.poetry.source]]
name = "default"
url = 'https://pypi.org/'
priority = "primary"
[[tool.poetry.source]]
name = "tsinghua"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
priority = "supplemental"
[[tool.poetry.source]]
name = "aliyun"
url = "https://mirrors.aliyun.com/pypi/simple"
priority = "supplemental"

View File

@@ -1,5 +0,0 @@
[pytest]
log_cli = True
log_cli_level = info
log_cli_format = %(asctime)s [%(levelname)s] | %(filename)s:%(lineno)s | %(message)s
log_cli_date_format = %Y-%m-%d %H:%M:%S

BIN
resources/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

View File

@@ -1,35 +0,0 @@
import os
import shutil
import yaml
from self_utils.qt_util import QtUtil
class Config:
def __init__(self):
# 项目根目录
self.config_path = os.path.join(QtUtil.get_root_path(), "config.yaml")
# 上一层目录
self.config = self.load_config()
def get_config_from_base(self, title, key):
return self.config["base"][title]["config"][key]
def get_config_from_component(self, title, key):
return self.config["components"][title]["config"][key]
def load_config(self):
# 如果文件不存在则生成一个yaml文件
if not os.path.exists(self.config_path):
# 将 config_tmp.yaml 复制并改名为 config.yaml
shutil.copyfile(os.path.join(QtUtil.get_root_path(), "config_tmp.yaml"), self.config_path)
with open(self.config_path, 'r', encoding='utf-8') as file:
config = yaml.safe_load(file)
return config
def update_config(self):
# 将 self.config 写入 yaml 文件,并允许 Unicode 字符
with open(self.config_path, 'w', encoding='utf-8') as file:
yaml.dump(self.config, file, allow_unicode=True, sort_keys=False)

View File

@@ -1,19 +0,0 @@
from pynput import mouse
from PyQt6.QtCore import pyqtSignal, QThread
class GlobalKeyboardListen(QThread):
mouse_middle_signal = pyqtSignal()
def __init__(self):
super().__init__()
def run(self):
def on_click(x, y, button, pressed):
if button == mouse.Button.middle:
if pressed:
self.mouse_middle_signal.emit()
print("鼠标中键按下")
return True
# 创建鼠标监听器
with mouse.Listener(on_click=on_click) as mouse_listener:
mouse_listener.join()

View File

@@ -1,41 +0,0 @@
import httpx
from self_utils.config import Config
from openai import DefaultHttpxClient, OpenAI
class LLM_Util:
def __init__(self):
super().__init__()
self.config = Config()
self.api_key = self.config.get_config_from_component("llm", "api_key")
self.base_url = self.config.get_config_from_component("llm", "api_url")
self.model = self.config.get_config_from_component("llm", "model")
self.proxy = self.config.get_config_from_component("llm", "proxy")
self.client = OpenAI(api_key=self.api_key, base_url=self.base_url,
http_client=DefaultHttpxClient(
proxies=self.proxy,
transport=httpx.HTTPTransport(local_address="0.0.0.0"),
),
)
self.res = ""
# messages = [{ "content": message, "role": "user"}]
def invoke(self, messages):
r = self.client.chat.completions.create(
model=self.model,
messages=messages,
stream=True # 流式请求参数值设置为True
)
for i in r:
stream_res = i.choices[0].delta.content or ""
self.res += stream_res
yield stream_res
messages.append({"content": self.res, "role": "assistant"})
# response = completion(model=self.model, base_url=self.base_url, api_key=self.api_key,
# messages=messages)
# return response.json()["choices"][0]["message"]

View File

@@ -1,31 +0,0 @@
import os
import sys
from PyQt6 import uic
from PyQt6.uic import loadUiType
class QtUtil:
@classmethod
def get_root_path(cls):
# project_root_path = os.path.dirname(os.path.realpath(sys.argv[0]))
project_root_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
return project_root_path
@classmethod
def load_ui(cls, *path):
# 项目根目录
path = os.path.join(cls.get_root_path(), "pages", *path)
return uic.loadUi(path)
@classmethod
def load_ui_type(cls, *path):
path = os.path.join(cls.get_root_path(), "pages", *path)
interface_ui, _ = loadUiType(path)
return interface_ui
@classmethod
def get_icon(cls, *path):
path = os.path.join(cls.get_root_path(), "source", *path)
return path

View File

@@ -1,50 +0,0 @@
import os
from selenium import webdriver
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
from webdriver_manager.microsoft import EdgeChromiumDriverManager
from selenium.webdriver.edge.service import Service as EdgeService
from selenium.webdriver.chrome.service import Service as ChromeService
from self_utils.config import Config
class SeleniumUtil:
def __init__(self):
config = Config()
browser_type = config.get_config_from_component("browser", "浏览器类型")
if browser_type == "chrome":
options = webdriver.ChromeOptions()
options.add_argument("--headless") # Enable headless mode
webdriver_manager = ChromeDriverManager()
driver = webdriver.Chrome(service=ChromeService(webdriver_manager.install()), options=options)
elif browser_type == "edge":
options = webdriver.EdgeOptions()
# options.add_argument("--headless") # Enable headless mode
webdriver_manager = EdgeChromiumDriverManager()
driver = webdriver.Edge(service=EdgeService(webdriver_manager.install()), options=options)
else:
return
driver.implicitly_wait(10)
self.driver = driver
def get_url(self, url):
self.driver.get(url)
def click(self, xpath):
self.driver.find_element(By.XPATH, xpath).click()
def send(self, xpath, text):
self.driver.find_element(By.XPATH, xpath).send_keys(text)
def quit(self):
self.driver.quit()
def get_text(self, xpath):
return self.driver.find_element(xpath).text
def get_attribute(self, xpath, name):
return self.driver.find_element(xpath).get_attribute(name)
def get_xpath_elements(self, xpath):
return self.driver.find_elements(By.XPATH, xpath)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

View File

@@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1710121848900" class="icon" viewBox="0 0 1036 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5986" xmlns:xlink="http://www.w3.org/1999/xlink" width="202.34375" height="200"><path d="M938.746762 69.161909l-45.29294-16.324979a67.263911 67.263911 0 1 0-20.006974 64.317916l42.469944 15.465979c28.720962 10.432986 53.515929 49.097935 53.51593 83.46589v598.869211c0 33.999955-24.548968 72.663904-53.639929 82.728891l-38.29595 13.501982a67.263911 67.263911 0 1 0 14.115982 66.649912l46.396938-16.447978c55.602927-19.515974 98.195871-83.71089 98.195871-146.310808V216.209715c0-62.353918-42.959943-126.917833-98.195871-147.292806z m-716.580055 819.808919a66.895912 66.895912 0 0 0-52.165931 24.547968l-48.483936-16.079979a89.111883 89.111883 0 0 1-54.252929-74.995901V208.722725a89.971881 89.971881 0 0 1 54.252929-76.1019l51.919931-18.656975a67.631911 67.631911 0 1 0-15.464979-65.913913L98.194871 69.284909A157.357793 157.357793 0 0 0 0 208.722725v613.719191a156.252794 156.252794 0 0 0 99.299869 138.332817l60.266921 21.234972a67.509911 67.509911 0 1 0 62.599917-93.039877z" fill="#1296db" p-id="5987"></path><path d="M426.289438 572.413245l86.779886-205.105729 91.935878 205.105729z m282.311628 230.758696h119.798842L560.817261 210.317723l-98.194871 1.104998-251.625668 592.11722 118.079844-1.349999 51.429932-122.007839H652.999139l55.111927 122.743838z" fill="#1296db" p-id="5988"></path></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Some files were not shown because too many files have changed in this diff Show More