重构项目
24
.gitignore
vendored
@@ -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
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["dbaeumer.vscode-eslint"]
|
||||
}
|
||||
39
.vscode/launch.json
vendored
Normal 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
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
||||
127
LICENSE
@@ -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 License,Version 2
|
||||
|
||||
Mulan Permissive Software License,Version 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 IT’S 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 License,Version 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.
|
||||
50
README.md
@@ -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
|
||||
|
||||

|
||||
```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
|
||||
```
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
from actions.word_action import WordAction
|
||||
|
||||
|
||||
class ActionUtil:
|
||||
_actions = [WordAction]
|
||||
|
||||
@classmethod
|
||||
def get_actions(cls):
|
||||
return cls._actions
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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"],
|
||||
|
||||
},
|
||||
},
|
||||
}]
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
12
build/entitlements.mac.plist
Normal 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
BIN
build/icon.ico
Normal file
|
After Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
11
package.bat
@@ -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 ..
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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()
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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", "登陆"))
|
||||
@@ -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")
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
@@ -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
|
After Width: | Height: | Size: 35 KiB |
@@ -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)
|
||||
@@ -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()
|
||||
@@ -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"]
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
BIN
source/VIP.png
|
Before Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 2.8 MiB |
|
Before Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 114 KiB |
BIN
source/logo.ico
|
Before Width: | Height: | Size: 261 KiB |
BIN
source/logo.png
|
Before Width: | Height: | Size: 8.3 KiB |
@@ -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 |
BIN
source/功能.png
|
Before Width: | Height: | Size: 3.5 KiB |
BIN
source/添加.png
|
Before Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |