Compare commits
12 Commits
da3da80b17
...
main
Author | SHA1 | Date | |
---|---|---|---|
f33d7bbec8 | |||
bf400ca9c2 | |||
24c1a94f36 | |||
fd67663868 | |||
27d7ffe4bf | |||
89c8b34550 | |||
fdf2e1a3ff | |||
a7b0373425 | |||
890543e3c1 | |||
bce75a1727 | |||
551ed9a4ce | |||
28986de6ea |
@@ -12,7 +12,7 @@ venv.bak/
|
|||||||
*.egg-info/
|
*.egg-info/
|
||||||
dist/
|
dist/
|
||||||
build/
|
build/
|
||||||
|
.tmp
|
||||||
# IDEs and editors
|
# IDEs and editors
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
|
27
README.md
@@ -1,2 +1,27 @@
|
|||||||
# AiraPulsar 飞行控制系统
|
# AiraPulsar 飞行控制系统
|
||||||
AiraPulsar (空中脉冲星, 简称 APSR) 是一套基于 WebSocket 的三点架构的轻量级网络飞行控制系统
|
AiraPulsar 是一套适用于固定翼无人机/航模的轻量级通用网络飞行控制系统
|
||||||
|
|
||||||
|
(待)支持功能:
|
||||||
|
- 影像实时回传/录制
|
||||||
|
- HUD 支持
|
||||||
|
- 多驾驶员/操作员链接支持 & 同步
|
||||||
|
- 鼠标/键盘飞行姿态控制
|
||||||
|
- 计算机辅助姿态修正 & 改出
|
||||||
|
- 自动辅助降落控制
|
||||||
|
- 物理断链情况下的自降落/修正/重新连接
|
||||||
|
- 飞行数据/机载设备监控
|
||||||
|
- 机体状态图像式监控
|
||||||
|
- 垂直载荷投放计算 (持续计算打击点)
|
||||||
|
|
||||||
|
特色:
|
||||||
|
- 通用机载计算机部署
|
||||||
|
- 模块化设计
|
||||||
|
- 数据中转服务器
|
||||||
|
|
||||||
|
目录说明:
|
||||||
|
- comet: 飞行器侧载端
|
||||||
|
- pulsar: 控制器客户端
|
||||||
|
- moon: 数据中转服务器端
|
||||||
|
- vgl: 矢量图形层 (库)
|
||||||
|
- testfield: 开发中组件
|
||||||
|
- auxiliary: 辅助程序
|
@@ -1,52 +0,0 @@
|
|||||||
import pygame
|
|
||||||
|
|
||||||
|
|
||||||
def selector():
|
|
||||||
pygame.init()
|
|
||||||
try:
|
|
||||||
width = int(input("Frame Width: "))
|
|
||||||
height = int(input("Frame Height: "))
|
|
||||||
except:
|
|
||||||
width = 800
|
|
||||||
height = 600
|
|
||||||
screen = pygame.display.set_mode((width, height))
|
|
||||||
pygame.display.set_caption("UI Design Auxiliary Tool")
|
|
||||||
selecting = False
|
|
||||||
start_pos = None
|
|
||||||
end_pos = None
|
|
||||||
running = True
|
|
||||||
screen.fill((0,0,0))
|
|
||||||
while running:
|
|
||||||
screen.fill((0,0,0))
|
|
||||||
for event in pygame.event.get():
|
|
||||||
if event.type == pygame.QUIT:
|
|
||||||
running = False
|
|
||||||
elif event.type == pygame.MOUSEBUTTONDOWN:
|
|
||||||
if event.button == 1: # 左键
|
|
||||||
selecting = True
|
|
||||||
start_pos = event.pos
|
|
||||||
elif event.type == pygame.MOUSEMOTION:
|
|
||||||
if selecting:
|
|
||||||
end_pos = event.pos
|
|
||||||
elif event.type == pygame.MOUSEBUTTONUP:
|
|
||||||
if event.button == 1:
|
|
||||||
selecting = False
|
|
||||||
if start_pos and end_pos:
|
|
||||||
rect = pygame.Rect(
|
|
||||||
start_pos,
|
|
||||||
(end_pos[0] - start_pos[0], end_pos[1] - start_pos[1]),
|
|
||||||
)
|
|
||||||
|
|
||||||
if selecting and start_pos and end_pos:
|
|
||||||
rect = pygame.Rect(
|
|
||||||
start_pos, (end_pos[0] - start_pos[0], end_pos[1] - start_pos[1])
|
|
||||||
)
|
|
||||||
pygame.draw.rect(screen, (255, 0, 0), rect, 2) # 绘制选区矩形
|
|
||||||
|
|
||||||
pygame.display.flip()
|
|
||||||
|
|
||||||
pygame.quit()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
@@ -1,28 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>图形参数生成器</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>图形参数生成器</h1>
|
|
||||||
<label for="shape">图形类型:</label>
|
|
||||||
<select id="shape">
|
|
||||||
<option value="rect">矩形</option>
|
|
||||||
<option value="line">线条</option>
|
|
||||||
<option value="circle">圆形</option>
|
|
||||||
<option value="ellipse">椭圆</option>
|
|
||||||
<option value="polygon">多边形</option>
|
|
||||||
<option value="arc">弧形</option>
|
|
||||||
<option value="point">点</option>
|
|
||||||
<option value="lines">线段</option>
|
|
||||||
</select><br><br>
|
|
||||||
|
|
||||||
<div id="params"></div><br>
|
|
||||||
|
|
||||||
<button id="generate">生成图形参数</button><br><br>
|
|
||||||
|
|
||||||
<div id="result"></div>
|
|
||||||
|
|
||||||
<script src="script.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@@ -1,152 +0,0 @@
|
|||||||
function generateShapeDict(shape, params) {
|
|
||||||
if (shape === 'rect') {
|
|
||||||
return {
|
|
||||||
method: 'rect',
|
|
||||||
pos: params.pos || [0, 0],
|
|
||||||
size: params.size || [1, 1],
|
|
||||||
color: params.color || [255, 255, 255],
|
|
||||||
width: params.width || 0
|
|
||||||
};
|
|
||||||
} else if (shape === 'line') {
|
|
||||||
return {
|
|
||||||
method: 'line',
|
|
||||||
start_pos: params.start_pos || [0, 0],
|
|
||||||
end_pos: params.end_pos || [1, 1],
|
|
||||||
color: params.color || [255, 255, 255]
|
|
||||||
};
|
|
||||||
} else if (shape === 'circle') {
|
|
||||||
return {
|
|
||||||
method: 'circle',
|
|
||||||
center: params.center || [0, 0],
|
|
||||||
radius: params.radius || 1,
|
|
||||||
color: params.color || [255, 255, 255]
|
|
||||||
};
|
|
||||||
} else if (shape === 'ellipse') {
|
|
||||||
return {
|
|
||||||
method: 'ellipse',
|
|
||||||
pos: params.pos || [0, 0],
|
|
||||||
size: params.size || [1, 1],
|
|
||||||
color: params.color || [255, 255, 255]
|
|
||||||
};
|
|
||||||
} else if (shape === 'polygon') {
|
|
||||||
return {
|
|
||||||
method: 'polygon',
|
|
||||||
pointlist: params.pointlist || [],
|
|
||||||
color: params.color || [255, 255, 255]
|
|
||||||
};
|
|
||||||
} else if (shape === 'arc') {
|
|
||||||
return {
|
|
||||||
method: 'arc',
|
|
||||||
pos: params.pos || [0, 0],
|
|
||||||
size: params.size || [1, 1],
|
|
||||||
color: params.color || [255, 255, 255],
|
|
||||||
start_angle: params.start_angle || 0,
|
|
||||||
stop_angle: params.stop_angle || 3.14
|
|
||||||
};
|
|
||||||
} else if (shape === 'point') {
|
|
||||||
return {
|
|
||||||
method: 'point',
|
|
||||||
pos: params.pos || [0, 0],
|
|
||||||
color: params.color || [255, 255, 255]
|
|
||||||
};
|
|
||||||
} else if (shape === 'lines') {
|
|
||||||
return {
|
|
||||||
method: 'lines',
|
|
||||||
pointlist: params.pointlist || [],
|
|
||||||
color: params.color || [255, 255, 255]
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
throw new Error("Unsupported shape type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const shapeSelect = document.getElementById('shape');
|
|
||||||
const paramsDiv = document.getElementById('params');
|
|
||||||
const generateButton = document.getElementById('generate');
|
|
||||||
const resultDiv = document.getElementById('result');
|
|
||||||
|
|
||||||
function createInput(label, id, type = 'text') {
|
|
||||||
const input = document.createElement('input');
|
|
||||||
input.type = type;
|
|
||||||
input.id = id;
|
|
||||||
const labelElement = document.createElement('label');
|
|
||||||
labelElement.textContent = label + ': ';
|
|
||||||
labelElement.setAttribute('for', id);
|
|
||||||
paramsDiv.appendChild(labelElement);
|
|
||||||
paramsDiv.appendChild(input);
|
|
||||||
paramsDiv.appendChild(document.createElement('br'));
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateParamsInputs() {
|
|
||||||
paramsDiv.innerHTML = '';
|
|
||||||
const shape = shapeSelect.value;
|
|
||||||
|
|
||||||
if (shape === 'rect') {
|
|
||||||
createInput('矩形位置 (x y)', 'pos');
|
|
||||||
createInput('矩形大小 (width height)', 'size');
|
|
||||||
createInput('矩形颜色 (R G B)', 'color');
|
|
||||||
createInput('矩形边框宽度', 'width', 'number');
|
|
||||||
} else if (shape === 'line') {
|
|
||||||
createInput('起始位置 (x y)', 'start_pos');
|
|
||||||
createInput('结束位置 (x y)', 'end_pos');
|
|
||||||
createInput('线条颜色 (R G B)', 'color');
|
|
||||||
} else if (shape === 'circle') {
|
|
||||||
createInput('圆心位置 (x y)', 'center');
|
|
||||||
createInput('圆的半径', 'radius', 'number');
|
|
||||||
createInput('圆的颜色 (R G B)', 'color');
|
|
||||||
} else if (shape === 'ellipse') {
|
|
||||||
createInput('椭圆位置 (x y)', 'pos');
|
|
||||||
createInput('椭圆大小 (width height)', 'size');
|
|
||||||
createInput('椭圆颜色 (R G B)', 'color');
|
|
||||||
} else if (shape === 'polygon') {
|
|
||||||
createInput('多边形的点 (x1 y1 x2 y2 ...)', 'pointlist');
|
|
||||||
createInput('多边形颜色 (R G B)', 'color');
|
|
||||||
} else if (shape === 'arc') {
|
|
||||||
createInput('弧的位置 (x y)', 'pos');
|
|
||||||
createInput('弧的大小 (width height)', 'size');
|
|
||||||
createInput('弧的颜色 (R G B)', 'color');
|
|
||||||
createInput('弧的起始角度 (弧度制)', 'start_angle', 'number');
|
|
||||||
createInput('弧的结束角度 (弧度制)', 'stop_angle', 'number');
|
|
||||||
} else if (shape === 'point') {
|
|
||||||
createInput('点的位置 (x y)', 'pos');
|
|
||||||
createInput('点的颜色 (R G B)', 'color');
|
|
||||||
} else if (shape === 'lines') {
|
|
||||||
createInput('线段的点 (x1 y1 x2 y2 ...)', 'pointlist');
|
|
||||||
createInput('线段颜色 (R G B)', 'color');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shapeSelect.addEventListener('change', updateParamsInputs);
|
|
||||||
updateParamsInputs();
|
|
||||||
|
|
||||||
generateButton.addEventListener('click', function() {
|
|
||||||
const shape = shapeSelect.value;
|
|
||||||
const params = {};
|
|
||||||
const inputs = paramsDiv.querySelectorAll('input');
|
|
||||||
|
|
||||||
inputs.forEach(input => {
|
|
||||||
if (input.id === 'pointlist') {
|
|
||||||
const points = input.value.split(' ').map(Number);
|
|
||||||
params.pointlist = [];
|
|
||||||
for (let i = 0; i < points.length; i += 2) {
|
|
||||||
params.pointlist.push([points[i], points[i + 1]]);
|
|
||||||
}
|
|
||||||
} else if (input.id === 'pos' || input.id === 'size' || input.id === 'start_pos' || input.id === 'end_pos' || input.id === 'center' || input.id === 'color') {
|
|
||||||
params[input.id] = input.value.split(' ').map(Number);
|
|
||||||
} else if (input.type === 'number') {
|
|
||||||
params[input.id] = parseFloat(input.value);
|
|
||||||
} else {
|
|
||||||
params[input.id] = input.value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const shapeDict = generateShapeDict(shape, params);
|
|
||||||
resultDiv.innerHTML = '<pre>' + JSON.stringify(shapeDict, null, 2) + '</pre>';
|
|
||||||
} catch (error) {
|
|
||||||
resultDiv.innerHTML = '<p>错误: ' + error.message + '</p>';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
1
comet/.python-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3.13
|
0
comet/agent_logs.log
Normal file
6
comet/main.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
def main():
|
||||||
|
print("Hello from comet!")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
7
comet/pyproject.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[project]
|
||||||
|
name = "Comet"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "A onboard controller implementation of AiraPulsar Flight Control System"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
dependencies = []
|
0
lib/__init__.py
Normal file
BIN
lib/__pycache__/__init__.cpython-313.pyc
Normal file
4
lib/agency/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
from .main import *
|
||||||
|
|
||||||
|
version = '0.1.0'
|
||||||
|
print(f"Powered by Moons Agency, version {version}")
|
BIN
lib/agency/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
lib/agency/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
lib/agency/__pycache__/main.cpython-312.pyc
Normal file
BIN
lib/agency/__pycache__/main.cpython-313.pyc
Normal file
1
moon/.python-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3.13
|
119
moon/logs.log
@@ -1,119 +0,0 @@
|
|||||||
2025-03-07T08:44:59.049714+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T08:54:33.168394+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T08:56:35.504311+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T08:57:12.572151+0800 - INFO - 客户端断开连接: 1
|
|
||||||
2025-03-07T08:57:13.906086+0800 - INFO - 新客户端连接: 2
|
|
||||||
2025-03-07T08:57:18.478193+0800 - INFO - 客户端断开连接: 2
|
|
||||||
2025-03-07T08:58:55.383088+0800 - INFO - 新客户端连接: 3
|
|
||||||
2025-03-07T08:59:04.634280+0800 - INFO - 客户端断开连接: 3
|
|
||||||
2025-03-07T08:59:05.383848+0800 - INFO - 新客户端连接: 4
|
|
||||||
2025-03-07T08:59:51.560133+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T08:59:52.909275+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T08:59:55.915998+0800 - INFO - 客户端断开连接: 1
|
|
||||||
2025-03-07T09:00:43.283319+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:00:44.635715+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:01:29.896734+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:01:31.332525+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:02:17.026957+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:02:18.296634+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:02:48.143588+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:02:49.427049+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:03:10.834981+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:03:12.119704+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:03:12.119986+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T09:03:12.120071+0800 - INFO - 客户端断开连接: 1
|
|
||||||
2025-03-07T09:03:42.168476+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:03:43.472305+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:03:43.472547+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T09:11:05.352860+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:11:06.882419+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:11:06.882773+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T09:12:14.693477+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:12:16.851004+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:12:16.851656+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T09:19:09.007145+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:19:10.754140+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:19:10.754434+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T09:19:18.493238+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:19:20.016578+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:19:20.016862+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T09:19:37.700993+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:19:39.081530+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:19:39.081796+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T09:19:43.080448+0800 - INFO - 处理信息: {"id": "comet", "receiver": "1", "content": "hi"}
|
|
||||||
2025-03-07T09:19:43.080720+0800 - ERROR - 无法解码 JSON 消息
|
|
||||||
2025-03-07T09:20:12.487057+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:20:13.857585+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:20:13.857880+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T09:20:14.799728+0800 - INFO - 客户端断开连接: 1
|
|
||||||
2025-03-07T09:20:17.208026+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:20:22.577076+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:20:22.577489+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T09:20:23.575751+0800 - INFO - 处理信息: {"id": "comet", "receiver": "1", "content": "hi"}
|
|
||||||
2025-03-07T09:20:23.576044+0800 - ERROR - 无法解码 JSON 消息
|
|
||||||
2025-03-07T09:26:56.246176+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:26:57.807128+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:26:57.807419+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T09:26:58.805874+0800 - INFO - 处理信息: {"id": "comet", "receiver": "1", "content": "hi"}
|
|
||||||
2025-03-07T09:26:58.806066+0800 - ERROR - 无法解码 JSON 消息
|
|
||||||
2025-03-07T09:28:25.117434+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:28:26.673876+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:28:26.674262+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T09:28:27.671936+0800 - INFO - 处理信息: {"id": "comet", "receiver": "1", "content": "hi"}
|
|
||||||
2025-03-07T09:28:27.672177+0800 - ERROR - 无法解码 JSON 消息
|
|
||||||
2025-03-07T09:28:29.778081+0800 - INFO - 客户端断开连接: 1
|
|
||||||
2025-03-07T09:28:33.464911+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:28:35.158721+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:28:35.159064+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T09:28:36.157310+0800 - INFO - 处理信息: {"id": "comet", "receiver": "1", "content": "hi"}
|
|
||||||
2025-03-07T09:28:36.157514+0800 - ERROR - 无法解码 JSON 消息
|
|
||||||
2025-03-07T09:29:18.205614+0800 - INFO - 客户端断开连接: 1
|
|
||||||
2025-03-07T09:29:22.053522+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:29:25.102228+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:29:25.102476+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T09:29:26.101162+0800 - INFO - 处理信息: {"id": "comet", "receiver": "1", "content": "hi"}
|
|
||||||
2025-03-07T09:29:26.101351+0800 - ERROR - 无法解码 JSON 消息
|
|
||||||
2025-03-07T09:30:12.603632+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:30:13.967524+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:30:13.967874+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T09:30:14.966548+0800 - INFO - 处理信息: {"id": "comet", "receiver": "1", "content": {"hi": "hi"}}
|
|
||||||
2025-03-07T09:30:14.966787+0800 - INFO - 客户端断开连接: 1
|
|
||||||
2025-03-07T09:31:19.129916+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:31:20.564382+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:31:20.564841+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T09:31:21.562523+0800 - INFO - 处理信息: {"id": "comet", "receiver": "1", "content": {"hi": "hi"}}
|
|
||||||
2025-03-07T09:31:21.562718+0800 - WARNING - 无法发送至端点
|
|
||||||
2025-03-07T09:31:31.503846+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T09:31:41.873175+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T09:31:41.873532+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T09:31:42.872072+0800 - INFO - 处理信息: {"id": "comet", "receiver": "*", "content": {"hi": "hi"}}
|
|
||||||
2025-03-07T09:31:42.872284+0800 - WARNING - 无法发送至端点
|
|
||||||
2025-03-07T09:31:42.872356+0800 - WARNING - 无法发送至端点
|
|
||||||
2025-03-07T10:02:07.173456+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T10:02:22.249420+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T10:02:22.249683+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T10:02:22.348277+0800 - INFO - 处理信息: {"id": "comet", "receiver": "*", "content": {"hi": "hi"}}
|
|
||||||
2025-03-07T10:02:22.348428+0800 - WARNING - 无法发送至端点
|
|
||||||
2025-03-07T10:02:22.348504+0800 - WARNING - 无法发送至端点
|
|
||||||
2025-03-07T15:55:54.389580+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T15:55:55.865014+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T15:55:55.865334+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T15:55:55.964049+0800 - INFO - 处理信息: {"id": "comet", "receiver": "*", "content": {"hi": "hi"}}
|
|
||||||
2025-03-07T15:55:55.964208+0800 - WARNING - 无法发送至端点
|
|
||||||
2025-03-07T15:55:55.964276+0800 - WARNING - 无法发送至端点
|
|
||||||
2025-03-07T15:55:58.304575+0800 - INFO - 客户端断开连接: 1
|
|
||||||
2025-03-07T15:56:27.769208+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T15:56:29.266347+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T15:56:29.266586+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T15:56:29.365433+0800 - INFO - 处理信息: {"id": "comet", "receiver": "*", "content": {"hi": "hi"}}
|
|
||||||
2025-03-07T15:56:29.365581+0800 - WARNING - 无法发送至端点
|
|
||||||
2025-03-07T15:56:29.365643+0800 - WARNING - 无法发送至端点
|
|
||||||
2025-03-07T15:56:45.972404+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
|
|
||||||
2025-03-07T15:56:47.268864+0800 - INFO - 新客户端连接: 1
|
|
||||||
2025-03-07T15:56:47.269164+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
||||||
2025-03-07T15:56:47.367838+0800 - INFO - 处理信息: {"id": "comet", "receiver": "*", "content": {"hi": "hi"}}
|
|
||||||
2025-03-07T15:56:47.367979+0800 - WARNING - 无法发送至端点
|
|
||||||
2025-03-07T15:56:47.368041+0800 - WARNING - 无法发送至端点
|
|
||||||
2025-03-07T17:45:07.183410+0800 - INFO - 客户端断开连接: 1
|
|
||||||
2025-03-07T17:45:08.365484+0800 - INFO - 新客户端连接: 2
|
|
||||||
2025-03-07T17:45:08.365737+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
|
|
6
moon/main.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
def main():
|
||||||
|
print("Hello from moon!")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
7
moon/pyproject.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[project]
|
||||||
|
name = "Moon"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "A relay server implementation of AiraPulsar Flight Control System"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
dependencies = []
|
1
pulsar/.python-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3.13
|
0
pulsar/agent_logs.log
Normal file
24
pulsar/components/__horizontal_indicator.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import vgl
|
||||||
|
|
||||||
|
|
||||||
|
name = "Marking Lines"
|
||||||
|
|
||||||
|
def main(window):
|
||||||
|
frame = vgl.Frame().attach(window_object=window, poscale=(0,0), clone_name="Marking Line")
|
||||||
|
lines = list()
|
||||||
|
for i in range(0, 24):
|
||||||
|
lines.append(vgl.elements.Line(ends=[(0, i/24), (1, i/24)], color="green").attach(frame_object=frame))
|
||||||
|
for i in range(0, 32):
|
||||||
|
lines.append(vgl.elements.Line(ends=[(i/32, 0), (i/32, 1)], color="green").attach(frame_object=frame))
|
||||||
|
# We monitor events like this.
|
||||||
|
@window.observerize
|
||||||
|
def observer(info):
|
||||||
|
if info["delta"] == "key":
|
||||||
|
print(info)
|
||||||
|
if info["delta"] == "click":
|
||||||
|
if info["click"]:
|
||||||
|
print(info["cursor"]["poscale"])
|
||||||
|
# We make changes like this.
|
||||||
|
for i in lines:
|
||||||
|
i.set_color('#' + str((info['cursor']['position'][0] + info['cursor']['position'][1]*100 + info['cursor']['position'][0]*10000) % 999999).zfill(6))
|
||||||
|
observer()
|
BIN
pulsar/components/__pycache__/marker.cpython-312.pyc
Normal file
BIN
pulsar/components/__pycache__/marker.cpython-313.pyc
Normal file
BIN
pulsar/components/__pycache__/marking_lines.cpython-312.pyc
Normal file
25
pulsar/components/marker.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
from lib import vgl
|
||||||
|
name = "Marking Lines"
|
||||||
|
|
||||||
|
def main(window):
|
||||||
|
frame = vgl.Frame().attach(window_object=window, poscale=(0,0), clone_name="Marking Line")
|
||||||
|
lines = list()
|
||||||
|
for i in range(0, 24):
|
||||||
|
lines.append(vgl.elements.Line(ends=[(0, i/24), (1, i/24)], color="green").attach(frame_object=frame))
|
||||||
|
for i in range(0, 32):
|
||||||
|
lines.append(vgl.elements.Line(ends=[(i/32, 0), (i/32, 1)], color="green").attach(frame_object=frame))
|
||||||
|
# We monitor events like this.
|
||||||
|
@window.observerize
|
||||||
|
def observer(info):
|
||||||
|
if info["delta"] == "key":
|
||||||
|
print(info)
|
||||||
|
if info["delta"] == "click":
|
||||||
|
if info["click"]:
|
||||||
|
print(info["cursor"]["poscale"])
|
||||||
|
# We make changes like this.
|
||||||
|
for i in lines:
|
||||||
|
i.set_color('#' + str((info['cursor']['position'][0] + info['cursor']['position'][1]*100 + info['cursor']['position'][0]*10000) % 999999).zfill(6))
|
||||||
|
observer()
|
||||||
|
#@vgl.loop # TODO
|
||||||
|
#def toloop():
|
||||||
|
# pass
|
15
pulsar/layouts.yaml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
horizontal_indicator:
|
||||||
|
- type: line
|
||||||
|
args:
|
||||||
|
ends: [(0.4, 0.5), (0.6, 0.5)]
|
||||||
|
color: green
|
||||||
|
- type: line
|
||||||
|
name: remote
|
||||||
|
args:
|
||||||
|
ends: [(0.4, 0.5), (0.6, 0.5)]
|
||||||
|
color: green
|
||||||
|
- type: line
|
||||||
|
args:
|
||||||
|
ends: [(0.4, 0.5), (0.6, 0.5)]
|
||||||
|
color: green
|
||||||
|
|
49
pulsar/main.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
from lib import vgl
|
||||||
|
from lib import agency
|
||||||
|
import colorama
|
||||||
|
import os
|
||||||
|
import importlib.util
|
||||||
|
|
||||||
|
window = vgl.Window(title="Pulsar", size=(1600, 1000))
|
||||||
|
|
||||||
|
def loader():
|
||||||
|
components_dir = "components"
|
||||||
|
for i in os.listdir(components_dir):
|
||||||
|
if i.endswith(".py") and (not i.startswith("__")):
|
||||||
|
module_name = i[:-3]
|
||||||
|
module_path = os.path.join(components_dir, i)
|
||||||
|
|
||||||
|
spec = importlib.util.spec_from_file_location(module_name, module_path)
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
try:
|
||||||
|
if hasattr(module, "main") and callable(module.main):
|
||||||
|
print(f"[ .... ] Starting {module.name} module")
|
||||||
|
try:
|
||||||
|
module.main(window=window)
|
||||||
|
print(f"[{colorama.Fore.GREEN} OK {colorama.Style.RESET_ALL}] Started {module.name} module")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[{colorama.Fore.RED}FAILED{colorama.Style.RESET_ALL}] Failed to start {module_name} module: {e}")
|
||||||
|
except:
|
||||||
|
print(f"Unsupported module: {module_name}.py")
|
||||||
|
|
||||||
|
def console():
|
||||||
|
print("You've entered Pulsar's command console, an embbedded Python interpreter for debugging & testing")
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
i = input(">>> ")
|
||||||
|
if i == "q":
|
||||||
|
return
|
||||||
|
exec(i)
|
||||||
|
except:
|
||||||
|
print("An error occured & captured")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
print("Welcome to AiraPulsar Client")
|
||||||
|
window.start()
|
||||||
|
loader()
|
||||||
|
console()
|
||||||
|
print("Quitting")
|
||||||
|
window.kill()
|
||||||
|
exit()
|
0
pulsar/numerator.py
Normal file
7
pulsar/pyproject.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[project]
|
||||||
|
name = "Pulsar"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "A client implementation of AiraPulsar Flight Control System"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
dependencies = []
|
BIN
pulsar/src/dock/angle_limit.png
Normal file
After Width: | Height: | Size: 435 B |
BIN
pulsar/src/dock/drop_indicator.png
Normal file
After Width: | Height: | Size: 400 B |
BIN
pulsar/src/dock/extremis.png
Normal file
After Width: | Height: | Size: 426 B |
BIN
pulsar/src/dock/landing_mode.png
Normal file
After Width: | Height: | Size: 422 B |
BIN
pulsar/src/dock/recording_indicator.png
Normal file
After Width: | Height: | Size: 332 B |
BIN
pulsar/src/dock/sync_indicator.png
Normal file
After Width: | Height: | Size: 399 B |
48
pulsar/tester.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import vgl
|
||||||
|
import colorama
|
||||||
|
import os
|
||||||
|
import importlib.util
|
||||||
|
|
||||||
|
window = vgl.Window(title="Pulsar", size=(1024, 768))
|
||||||
|
|
||||||
|
def loader():
|
||||||
|
components_dir = "components"
|
||||||
|
for i in os.listdir(components_dir):
|
||||||
|
if i.endswith(".py") and (not i.startswith("__")):
|
||||||
|
module_name = i[:-3]
|
||||||
|
module_path = os.path.join(components_dir, i)
|
||||||
|
|
||||||
|
spec = importlib.util.spec_from_file_location(module_name, module_path)
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
try:
|
||||||
|
if hasattr(module, "main") and callable(module.main):
|
||||||
|
print(f"[ .... ] Starting {module.name} module")
|
||||||
|
try:
|
||||||
|
module.main(window=window)
|
||||||
|
print(f"[{colorama.Fore.GREEN} OK {colorama.Style.RESET_ALL}] Started {module.name} module")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[{colorama.Fore.RED}FAILED{colorama.Style.RESET_ALL}] Failed to start {module_name} module: {e}")
|
||||||
|
except:
|
||||||
|
print(f"Unsupported module: {module_name}.py")
|
||||||
|
|
||||||
|
def console():
|
||||||
|
print("You've entered Pulsar's command console, an embbedded Python interpreter for debugging & testing")
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
i = input(">>> ")
|
||||||
|
if i == "q":
|
||||||
|
return
|
||||||
|
exec(i)
|
||||||
|
except:
|
||||||
|
print("An error occured & captured")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
print("Tester")
|
||||||
|
window.start()
|
||||||
|
loader()
|
||||||
|
console()
|
||||||
|
print("Quitting")
|
||||||
|
window.kill()
|
||||||
|
exit()
|
@@ -1,113 +0,0 @@
|
|||||||
def generate_shape_dict(shape, **kwargs):
|
|
||||||
if shape == 'rect':
|
|
||||||
return {
|
|
||||||
'method': 'rect',
|
|
||||||
'pos': kwargs.get('pos', (0, 0)),
|
|
||||||
'size': kwargs.get('size', (1, 1)),
|
|
||||||
'color': kwargs.get('color', (255, 255, 255)),
|
|
||||||
'width': kwargs.get('width', 0) # 添加边框宽度
|
|
||||||
}
|
|
||||||
elif shape == 'line':
|
|
||||||
return {
|
|
||||||
'method': 'line',
|
|
||||||
'start_pos': kwargs.get('start_pos', (0, 0)),
|
|
||||||
'end_pos': kwargs.get('end_pos', (1, 1)),
|
|
||||||
'color': kwargs.get('color', (255, 255, 255))
|
|
||||||
}
|
|
||||||
elif shape == 'circle':
|
|
||||||
return {
|
|
||||||
'method': 'circle',
|
|
||||||
'center': kwargs.get('center', (0, 0)),
|
|
||||||
'radius': kwargs.get('radius', 1),
|
|
||||||
'color': kwargs.get('color', (255, 255, 255))
|
|
||||||
}
|
|
||||||
elif shape == 'ellipse':
|
|
||||||
return {
|
|
||||||
'method': 'ellipse',
|
|
||||||
'pos': kwargs.get('pos', (0, 0)),
|
|
||||||
'size': kwargs.get('size', (1, 1)),
|
|
||||||
'color': kwargs.get('color', (255, 255, 255))
|
|
||||||
}
|
|
||||||
elif shape == 'polygon':
|
|
||||||
return {
|
|
||||||
'method': 'polygon',
|
|
||||||
'pointlist': kwargs.get('pointlist', []),
|
|
||||||
'color': kwargs.get('color', (255, 255, 255))
|
|
||||||
}
|
|
||||||
elif shape == 'arc':
|
|
||||||
return {
|
|
||||||
'method': 'arc',
|
|
||||||
'pos': kwargs.get('pos', (0, 0)),
|
|
||||||
'size': kwargs.get('size', (1, 1)),
|
|
||||||
'color': kwargs.get('color', (255, 255, 255)),
|
|
||||||
'start_angle': kwargs.get('start_angle', 0),
|
|
||||||
'stop_angle': kwargs.get('stop_angle', 3.14)
|
|
||||||
}
|
|
||||||
elif shape == 'point':
|
|
||||||
return {
|
|
||||||
'method': 'point',
|
|
||||||
'pos': kwargs.get('pos', (0, 0)),
|
|
||||||
'color': kwargs.get('color', (255, 255, 255))
|
|
||||||
}
|
|
||||||
elif shape == 'lines':
|
|
||||||
return {
|
|
||||||
'method': 'lines',
|
|
||||||
'pointlist': kwargs.get('pointlist', []),
|
|
||||||
'color': kwargs.get('color', (255, 255, 255))
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
raise ValueError("Unsupported shape type")
|
|
||||||
|
|
||||||
def main():
|
|
||||||
print("欢迎使用图形参数生成器!")
|
|
||||||
shape = input("请输入图形类型 (rect, line, circle, ellipse, polygon, arc, point, lines): ").strip().lower()
|
|
||||||
|
|
||||||
params = {}
|
|
||||||
|
|
||||||
if shape == 'rect':
|
|
||||||
params['pos'] = tuple(map(int, input("请输入矩形位置 (x y): ").split()))
|
|
||||||
params['size'] = tuple(map(int, input("请输入矩形大小 (width height): ").split()))
|
|
||||||
params['color'] = tuple(map(int, input("请输入矩形颜色 (R G B): ").split()))
|
|
||||||
params['width'] = int(input("请输入矩形边框宽度: ")) # 添加边框宽度输入
|
|
||||||
elif shape == 'line':
|
|
||||||
params['start_pos'] = tuple(map(int, input("请输入起始位置 (x y): ").split()))
|
|
||||||
params['end_pos'] = tuple(map(int, input("请输入结束位置 (x y): ").split()))
|
|
||||||
params['color'] = tuple(map(int, input("请输入线条颜色 (R G B): ").split()))
|
|
||||||
elif shape == 'circle':
|
|
||||||
params['center'] = tuple(map(int, input("请输入圆心位置 (x y): ").split()))
|
|
||||||
params['radius'] = int(input("请输入圆的半径: "))
|
|
||||||
params['color'] = tuple(map(int, input("请输入圆的颜色 (R G B): ").split()))
|
|
||||||
elif shape == 'ellipse':
|
|
||||||
params['pos'] = tuple(map(int, input("请输入椭圆位置 (x y): ").split()))
|
|
||||||
params['size'] = tuple(map(int, input("请输入椭圆大小 (width height): ").split()))
|
|
||||||
params['color'] = tuple(map(int, input("请输入椭圆颜色 (R G B): ").split()))
|
|
||||||
elif shape == 'polygon':
|
|
||||||
points = input("请输入多边形的点 (x1 y1 x2 y2 ...): ").split()
|
|
||||||
params['pointlist'] = [(int(points[i]), int(points[i + 1])) for i in range(0, len(points), 2)]
|
|
||||||
params['color'] = tuple(map(int, input("请输入多边形颜色 (R G B): ").split()))
|
|
||||||
elif shape == 'arc':
|
|
||||||
params['pos'] = tuple(map(int, input("请输入弧的位置 (x y): ").split()))
|
|
||||||
params['size'] = tuple(map(int, input("请输入弧的大小 (width height): ").split()))
|
|
||||||
params['color'] = tuple(map(int, input("请输入弧的颜色 (R G B): ").split()))
|
|
||||||
params['start_angle'] = float(input("请输入弧的起始角度 (弧度制): "))
|
|
||||||
params['stop_angle'] = float(input("请输入弧的结束角度 (弧度制): "))
|
|
||||||
elif shape == 'point':
|
|
||||||
params['pos'] = tuple(map(int, input("请输入点的位置 (x y): ").split()))
|
|
||||||
params['color'] = tuple(map(int, input("请输入点的颜色 (R G B): ").split()))
|
|
||||||
elif shape == 'lines':
|
|
||||||
points = input("请输入线段的点 (x1 y1 x2 y2 ...): ").split()
|
|
||||||
params['pointlist'] = [(int(points[i]), int(points[i + 1])) for i in range(0, len(points), 2)]
|
|
||||||
params['color'] = tuple(map(int, input("请输入线段颜色 (R G B): ").split()))
|
|
||||||
else:
|
|
||||||
print("不支持的图形类型")
|
|
||||||
return
|
|
||||||
|
|
||||||
# 生成图形字典
|
|
||||||
shape_dict = generate_shape_dict(shape, **params)
|
|
||||||
|
|
||||||
# 输出结果
|
|
||||||
print("生成的图形参数字典:")
|
|
||||||
print(shape_dict)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
@@ -1,31 +0,0 @@
|
|||||||
{'method': 'rect', 'pos': (12, 45), 'size': (25, 15), 'color': (34, 200, 150)}
|
|
||||||
{'method': 'rect', 'pos': (67, 22), 'size': (18, 28), 'color': (255, 100, 50)}
|
|
||||||
{'method': 'rect', 'pos': (5, 78), 'size': (30, 20), 'color': (0, 0, 255)}
|
|
||||||
{'method': 'rect', 'pos': (90, 10), 'size': (15, 25), 'color': (200, 50, 100)}
|
|
||||||
{'method': 'rect', 'pos': (33, 60), 'size': (20, 30), 'color': (150, 200, 255)}
|
|
||||||
{'method': 'rect', 'pos': (50, 50), 'size': (10, 10), 'color': (255, 0, 0)}
|
|
||||||
{'method': 'rect', 'pos': (80, 40), 'size': (22, 18), 'color': (100, 255, 100)}
|
|
||||||
{'method': 'rect', 'pos': (15, 15), 'size': (28, 12), 'color': (255, 255, 0)}
|
|
||||||
{'method': 'rect', 'pos': (70, 70), 'size': (20, 20), 'color': (0, 255, 255)}
|
|
||||||
{'method': 'rect', 'pos': (40, 30), 'size': (25, 25), 'color': (255, 0, 255)}
|
|
||||||
{'method': 'rect', 'pos': (24, 88), 'size': (15, 25), 'color': (123, 45, 67)}
|
|
||||||
{'method': 'rect', 'pos': (56, 12), 'size': (20, 30), 'color': (200, 150, 100)}
|
|
||||||
{'method': 'rect', 'pos': (3, 55), 'size': (10, 20), 'color': (0, 128, 255)}
|
|
||||||
{'method': 'rect', 'pos': (77, 33), 'size': (18, 18), 'color': (255, 165, 0)}
|
|
||||||
{'method': 'rect', 'pos': (39, 72), 'size': (22, 14), 'color': (75, 0, 130)}
|
|
||||||
{'method': 'rect', 'pos': (90, 5), 'size': (30, 10), 'color': (255, 20, 147)}
|
|
||||||
{'method': 'rect', 'pos': (11, 44), 'size': (25, 25), 'color': (135, 206, 235)}
|
|
||||||
{'method': 'rect', 'pos': (62, 29), 'size': (15, 15), 'color': (255, 99, 71)}
|
|
||||||
{'method': 'rect', 'pos': (48, 60), 'size': (20, 20), 'color': (0, 255, 127)}
|
|
||||||
{'method': 'rect', 'pos': (34, 10), 'size': (28, 12), 'color': (255, 228, 196)}
|
|
||||||
{'method': 'rect', 'pos': (70, 80), 'size': (12, 22), 'color': (255, 0, 0)}
|
|
||||||
{'method': 'rect', 'pos': (15, 15), 'size': (20, 30), 'color': (0, 0, 0)}
|
|
||||||
{'method': 'rect', 'pos': (88, 44), 'size': (18, 18), 'color': (255, 215, 0)}
|
|
||||||
{'method': 'rect', 'pos': (5, 90), 'size': (30, 20), 'color': (0, 0, 139)}
|
|
||||||
{'method': 'rect', 'pos': (40, 40), 'size': (25, 25), 'color': (255, 105, 180)}
|
|
||||||
{'method': 'rect', 'pos': (60, 20), 'size': (15, 25), 'color': (255, 140, 0)}
|
|
||||||
{'method': 'rect', 'pos': (20, 70), 'size': (22, 18), 'color': (0, 191, 255)}
|
|
||||||
{'method': 'rect', 'pos': (30, 30), 'size': (10, 10), 'color': (255, 20, 147)}
|
|
||||||
{'method': 'rect', 'pos': (75, 50), 'size': (20, 20), 'color': (255, 0, 255)}
|
|
||||||
{'method': 'rect', 'pos': (10, 10), 'size': (28, 12), 'color': (128, 0, 128)}
|
|
||||||
{'method': 'rect', 'pos': (45, 55), 'size': (15, 15), 'color': (255, 69, 0)}
|
|
@@ -1,26 +0,0 @@
|
|||||||
import pygame
|
|
||||||
import sys
|
|
||||||
from testfield.vgl.vgllib import *
|
|
||||||
pygame.init()
|
|
||||||
window_size = (800, 600)
|
|
||||||
window = pygame.display.set_mode(window_size)
|
|
||||||
|
|
||||||
frame = Frame("Main Frame", window_size)
|
|
||||||
|
|
||||||
# 注册一个矩形组件
|
|
||||||
frame.register("rectangle", {"method": "rect", "pos": (100, 100), "size": (200, 150), "color": (255, 0, 0)})
|
|
||||||
|
|
||||||
# 注册一个圆形组件
|
|
||||||
frame.register("circle", {"method": "circle", "center": (400, 300), "radius": 50, "color": (0, 255, 0)})
|
|
||||||
|
|
||||||
# 主循环
|
|
||||||
while True:
|
|
||||||
for event in pygame.event.get():
|
|
||||||
if event.type == pygame.QUIT:
|
|
||||||
pygame.quit()
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
window.fill((0, 0, 0)) # 清空窗口
|
|
||||||
frame.drawall(frame.components) # 绘制所有组件
|
|
||||||
frame.show(window, (0, 0)) # 显示框架
|
|
||||||
pygame.display.flip() # 更新显示
|
|
@@ -1,149 +0,0 @@
|
|||||||
# Vector Graphics Library
|
|
||||||
# vgllib.py
|
|
||||||
import pygame
|
|
||||||
import uuid
|
|
||||||
import time
|
|
||||||
import threading
|
|
||||||
|
|
||||||
class Graph:
|
|
||||||
@staticmethod
|
|
||||||
def rect(frame, pos: tuple = (0, 0), size: tuple = (1, 1), color: tuple = (255, 255, 255), width: int = 0):
|
|
||||||
pygame.draw.rect(frame.surface, color=color, rect=(pos[0], pos[1], size[0], size[1]), width=width)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def line(frame, start_pos: tuple, end_pos: tuple, color: tuple = (255, 255, 255)):
|
|
||||||
pygame.draw.aaline(frame.surface, color, start_pos, end_pos)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def circle(frame, center: tuple, radius: int, color: tuple = (255, 255, 255), width: int = 0):
|
|
||||||
pygame.draw.circle(frame.surface, color, center, radius, width)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def ellipse(frame, pos: tuple = (0, 0), size: tuple = (1, 1), color: tuple = (255, 255, 255), width: int = 0):
|
|
||||||
rect=(pos[0], pos[1], size[0], size[1])
|
|
||||||
pygame.draw.ellipse(frame.surface, color, rect, width)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def polygon(frame, pointlist: list, color: tuple = (255, 255, 255), width: int = 0):
|
|
||||||
pygame.draw.polygon(frame.surface, color, pointlist, width)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def arc(frame, pos: tuple = (0, 0), size: tuple = (1, 1), color: tuple = (255, 255, 255), start_angle: float = 0, stop_angle: float = 3.14, width: int = 1):
|
|
||||||
rect=(pos[0], pos[1], size[0], size[1])
|
|
||||||
pygame.draw.arc(frame.surface, color, rect, start_angle, stop_angle, width)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def point(frame, pos: tuple, color: tuple = (255, 255, 255)):
|
|
||||||
pygame.draw.point(frame.surface, color, pos)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def lines(frame, pointlist: list, color: tuple = (255, 255, 255)):
|
|
||||||
pygame.draw.aalines(frame.surface, color, closed=False, points=pointlist)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def call(frame, method, **kwargs):
|
|
||||||
if hasattr(Graph, method):
|
|
||||||
getattr(Graph, method)(frame, **kwargs)
|
|
||||||
else:
|
|
||||||
print(f"方法 {method} 不存在")
|
|
||||||
|
|
||||||
class Frame(object):
|
|
||||||
components = dict()
|
|
||||||
components_stat = dict()
|
|
||||||
thread = None
|
|
||||||
motion_queue = None
|
|
||||||
def __init__(self, name: str, size: tuple):
|
|
||||||
self.name = name
|
|
||||||
self.size = size
|
|
||||||
self.surface = pygame.Surface(size, flags=pygame.HWSURFACE)
|
|
||||||
self.is_hide = False
|
|
||||||
print("初始化子模块")
|
|
||||||
self.thread = threading.Thread(target=self.render)
|
|
||||||
|
|
||||||
def move(self, subname, direction, duration, effect="linear"): # our powerful move!
|
|
||||||
# direction: 使用角度制, 以直角笛卡尔坐标系的x正半轴方向为0度, 逆时针为加, 接受负数
|
|
||||||
# duration: "动画"时间, 为0则即时
|
|
||||||
# effect: "动画"效果, linear为线性移动, 或许会在未来增加贝塞尔曲线
|
|
||||||
# TODO: 增加 bezier 曲线
|
|
||||||
pass
|
|
||||||
|
|
||||||
def render(self):
|
|
||||||
while 1:
|
|
||||||
self.draw_all()
|
|
||||||
|
|
||||||
def show(self, window, position: tuple):
|
|
||||||
if not self.is_hide:
|
|
||||||
window.blit(self.surface, position)
|
|
||||||
|
|
||||||
def set_visible(self, newstat=True):
|
|
||||||
self.is_hide = newstat
|
|
||||||
|
|
||||||
def set_position(self, newposition):
|
|
||||||
self.position = newposition
|
|
||||||
|
|
||||||
def register(self, subname="", attr=None):
|
|
||||||
if subname == "":
|
|
||||||
subname = uuid.uuid4()
|
|
||||||
# use percent of frame size instead of pixels :)
|
|
||||||
attr['pos'] = list(attr['pos'])
|
|
||||||
attr['pos'][0] = round(attr['pos'][0] / 100 * self.size[0])
|
|
||||||
attr['pos'][1] = round(attr['pos'][1] / 100 * self.size[1])
|
|
||||||
attr['size'] = list(attr['size'])
|
|
||||||
attr['size'][0] = round(attr['size'][0] / 100 * self.size[0])
|
|
||||||
attr['size'][1] = round(attr['size'][1] / 100 * self.size[1])
|
|
||||||
self.components[subname] = attr
|
|
||||||
self.components_stat[subname] = 1 # by default, not hiding
|
|
||||||
|
|
||||||
def draw(self, attr):
|
|
||||||
Graph.call(self, **attr)
|
|
||||||
|
|
||||||
def set_component_visible(self, subname, newstat):
|
|
||||||
self.components_stat[subname] = newstat
|
|
||||||
|
|
||||||
def draw_all(self):
|
|
||||||
for i in self.components.keys():
|
|
||||||
if self.components_stat[i]:
|
|
||||||
self.draw(self.components[i])
|
|
||||||
|
|
||||||
def refresh(self, color=(0,0,0)):
|
|
||||||
self.surface.fill(color)
|
|
||||||
|
|
||||||
def clear(self):
|
|
||||||
components = dict()
|
|
||||||
components_stat = dict()
|
|
||||||
|
|
||||||
def loads(self, grap_str):
|
|
||||||
# TODO: 将会重写 以替代不安全的 eval
|
|
||||||
self.register(subname="", attr=(eval(grap_str)))
|
|
||||||
|
|
||||||
def load(self, file="default.vgld", mode="a"):
|
|
||||||
# a: 增量加载 (默认)
|
|
||||||
# w: 覆盖式加载
|
|
||||||
# 文件扩展名: vgld (矢量图形层描述文件)
|
|
||||||
if mode == 'w':
|
|
||||||
self.clear()
|
|
||||||
with open(file=file, mode="r+", encoding="UTF-8") as f:
|
|
||||||
for i in f.readlines():
|
|
||||||
#print(i)
|
|
||||||
self.loads(i)
|
|
||||||
|
|
||||||
|
|
||||||
# 示例
|
|
||||||
if __name__ == "__main__":
|
|
||||||
pygame.init()
|
|
||||||
window = pygame.display.set_mode((800, 600))
|
|
||||||
frame = Frame("Test", (800, 600))
|
|
||||||
input()
|
|
||||||
frame.load()
|
|
||||||
running = True
|
|
||||||
while running:
|
|
||||||
for event in pygame.event.get():
|
|
||||||
if event.type == pygame.QUIT:
|
|
||||||
running = False
|
|
||||||
|
|
||||||
window.fill((0, 0, 0))
|
|
||||||
frame.draw_all()
|
|
||||||
frame.show(window, (0, 0))
|
|
||||||
pygame.display.flip()
|
|
||||||
pygame.time.delay(10)
|
|
||||||
pygame.quit()
|
|
Before Width: | Height: | Size: 1.7 MiB |
Before Width: | Height: | Size: 992 KiB After Width: | Height: | Size: 1.7 MiB |
7
vgl/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import os
|
||||||
|
os.environ["PYGAME_HIDE_SUPPORT_PROMPT"] = "True"
|
||||||
|
from .main import *
|
||||||
|
from . import basic_elements as elements
|
||||||
|
|
||||||
|
version = '0.3.0'
|
||||||
|
print(f"Powered by Vector Graphic Layer, version {version}")
|
BIN
vgl/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
vgl/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
vgl/__pycache__/basic_elements.cpython-312.pyc
Normal file
BIN
vgl/__pycache__/basic_elements.cpython-313.pyc
Normal file
BIN
vgl/__pycache__/main.cpython-312.pyc
Normal file
BIN
vgl/__pycache__/main.cpython-313.pyc
Normal file
96
vgl/basic_elements.py
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
from .main import *
|
||||||
|
import pygame
|
||||||
|
import time
|
||||||
|
|
||||||
|
class Line(Element):
|
||||||
|
def __init__(self, ends = [(0.1, 0.1), (0.2, 0.2)], color = "#ffffff"):
|
||||||
|
self.color = color
|
||||||
|
self.ends = ends
|
||||||
|
def set_color(self, new_color):
|
||||||
|
self.color = new_color
|
||||||
|
def move(self, delta, duration):
|
||||||
|
self.add_task(group = "move", start_time = Aux.gettime(), duration = duration, delta = delta)
|
||||||
|
def update(self):
|
||||||
|
for i in self.tasks.get("move", []):
|
||||||
|
self.teleport(Aux.tuple_scale(tup = i['delta'], mul = Aux.getprogress(i)))
|
||||||
|
def render(self):
|
||||||
|
pygame.draw.aaline(self.attached_frame.surface, self.color, Aux.pixelizer(self.ends[0], self.attached_window), Aux.pixelizer(self.ends[1], self.attached_window))
|
||||||
|
class Rect(Element):
|
||||||
|
def __init__(self, nw_posc=(0.1, 0.1), se_posc=(0.2,0.2), color = "#114514", width = 0): # non-0 for "frame"
|
||||||
|
self.color = color
|
||||||
|
self.width = width
|
||||||
|
self.rect = (nw_posc[0], nw_posc[1], se_posc[0], se_posc[1])
|
||||||
|
def move(self, delta, duration):
|
||||||
|
self.add_task(group = "move", start_time = Aux.gettime(), duration = duration, delta = delta)
|
||||||
|
def update(self):
|
||||||
|
for i in self.tasks.get("move", []):
|
||||||
|
self.teleport(Aux.tuple_scale(tup = i['delta'], mul = Aux.getprogress(i)))
|
||||||
|
def render(self):
|
||||||
|
pygame.draw.rect(surface=self.attached_frame.surface, color=self.color, rect=Aux.pixelizer(self.rect, self.attached_window), width=self.width)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Graph:
|
||||||
|
@staticmethod
|
||||||
|
def rect(frame, pos: tuple = (0, 0), size: tuple = (1, 1), color: tuple = (255, 255, 255), width: int = 0):
|
||||||
|
pygame.draw.rect(frame.surface, color=color, rect=(pos[0], pos[1], size[0], size[1]), width=width)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def line(frame, start_pos: tuple, end_pos: tuple, color: tuple = (255, 255, 255)):
|
||||||
|
pygame.draw.aaline(frame.surface, color, start_pos, end_pos)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def circle(frame, center: tuple, radius: int, color: tuple = (255, 255, 255), width: int = 0):
|
||||||
|
pygame.draw.circle(frame.surface, color, center, radius, width)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ellipse(frame, pos: tuple = (0, 0), size: tuple = (1, 1), color: tuple = (255, 255, 255), width: int = 0):
|
||||||
|
rect=(pos[0], pos[1], size[0], size[1])
|
||||||
|
pygame.draw.ellipse(frame.surface, color, rect, width)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def polygon(frame, pointlist: list, color: tuple = (255, 255, 255), width: int = 0):
|
||||||
|
pygame.draw.polygon(frame.surface, color, pointlist, width)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def arc(frame, pos: tuple = (0, 0), size: tuple = (1, 1), color: tuple = (255, 255, 255), start_angle: float = 0, stop_angle: float = 3.14, width: int = 1):
|
||||||
|
rect=(pos[0], pos[1], size[0], size[1])
|
||||||
|
pygame.draw.arc(frame.surface, color, rect, start_angle, stop_angle, width)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def point(frame, pos: tuple, color: tuple = (255, 255, 255)):
|
||||||
|
pygame.draw.point(frame.surface, color, pos)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def lines(frame, pointlist: list, color: tuple = (255, 255, 255)):
|
||||||
|
pygame.draw.aalines(frame.surface, color, closed=False, points=pointlist)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def call(frame, method, **kwargs):
|
||||||
|
if hasattr(Graph, method):
|
||||||
|
getattr(Graph, method)(frame, **kwargs)
|
||||||
|
else:
|
||||||
|
print(f"方法 {method} 不存在")
|
283
vgl/main.py
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
# Vector Graphics Library
|
||||||
|
# vgllib.py
|
||||||
|
import pygame
|
||||||
|
import uuid
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
import copy
|
||||||
|
import queue
|
||||||
|
|
||||||
|
class Aux():
|
||||||
|
def gettime():
|
||||||
|
return round(time.time(), 1)
|
||||||
|
def getprogress(task):
|
||||||
|
return (Aux.gettime() - task["start"] / task["duration"])
|
||||||
|
def tuple_scale(tup, mul):
|
||||||
|
return (tup[0]*mul, tup[1]*mul)
|
||||||
|
def pixelizer(arg, base):
|
||||||
|
if not isinstance(base, tuple):
|
||||||
|
base = base.size
|
||||||
|
if isinstance(arg, int):
|
||||||
|
return arg * base
|
||||||
|
else:
|
||||||
|
if len(arg) == 2:
|
||||||
|
return (round(arg[0] * base[0]), round(arg[1] * base[1]))
|
||||||
|
if len(arg) == 4:
|
||||||
|
return (round(arg[0] * base[0]), round(arg[1] * base[1]), round(arg[2] * base[0]), round(arg[3] * base[1]))
|
||||||
|
def eventproc(events_dict, event, window_size):
|
||||||
|
"""events = {
|
||||||
|
"cursor": {
|
||||||
|
"position": (15,12),
|
||||||
|
"relative": (13,11),
|
||||||
|
"poscale": (0.1,0.5),
|
||||||
|
"relscale": (0.05,0.1),
|
||||||
|
},
|
||||||
|
"wheel": 1, # 远离用户为 1, 靠近为 -1, 无动作为 0
|
||||||
|
"click": [1, 2], # 被按下的按钮
|
||||||
|
#"focus": 1, # 或 0
|
||||||
|
"key": ["ctrl", "k"],
|
||||||
|
"delta": ""
|
||||||
|
}"""
|
||||||
|
if event.type == pygame.MOUSEMOTION:
|
||||||
|
events_dict["cursor"]["position"] = event.pos
|
||||||
|
events_dict["cursor"]["relative"] = event.rel
|
||||||
|
events_dict["cursor"]["poscale"] = (round(event.pos[0] / window_size[0], 3), round(event.pos[1] / window_size[1],3))
|
||||||
|
events_dict["cursor"]["relscale"] = (round(event.rel[0] / window_size[0], 3), round(event.rel[1] / window_size[1], 3))
|
||||||
|
events_dict["delta"] = "cursor"
|
||||||
|
elif event.type == pygame.MOUSEWHEEL:
|
||||||
|
events_dict["wheel"] = event.y # 远离用户为正,靠近为负
|
||||||
|
events_dict["delta"] = "wheel"
|
||||||
|
elif event.type == pygame.MOUSEBUTTONDOWN:
|
||||||
|
if event.button not in events_dict["click"]:
|
||||||
|
events_dict.setdefault("click", []).append(event.button)
|
||||||
|
events_dict["delta"] = "click"
|
||||||
|
elif event.type == pygame.MOUSEBUTTONUP:
|
||||||
|
if event.button in events_dict["click"]:
|
||||||
|
events_dict["click"].remove(event.button)
|
||||||
|
events_dict["delta"] = "click"
|
||||||
|
elif event.type == pygame.KEYDOWN:
|
||||||
|
keys = events_dict.setdefault("key", [])
|
||||||
|
key_name = pygame.key.name(event.key).lower()
|
||||||
|
# 简单的修饰键处理, 为兼容部分精简键盘 故合并左右功能键
|
||||||
|
if key_name in ["left ctrl", "right ctrl"]:
|
||||||
|
if "ctrl" not in keys:
|
||||||
|
keys.append("ctrl")
|
||||||
|
elif key_name in ["left shift", "right shift"]:
|
||||||
|
if "shift" not in keys:
|
||||||
|
keys.append("shift")
|
||||||
|
elif key_name in ["left alt", "right alt"]:
|
||||||
|
if "alt" not in keys:
|
||||||
|
keys.append("alt")
|
||||||
|
elif len(key_name) == 1: # 处理字母和数字
|
||||||
|
keys.append(key_name)
|
||||||
|
events_dict["delta"] = "key"
|
||||||
|
elif event.type == pygame.KEYUP:
|
||||||
|
events_dict["delta"] = "key"
|
||||||
|
if "key" in events_dict:
|
||||||
|
key_name = pygame.key.name(event.key).lower()
|
||||||
|
if key_name in ["left ctrl", "right ctrl"] and "ctrl" in events_dict["key"]:
|
||||||
|
events_dict["key"].remove("ctrl")
|
||||||
|
elif key_name in ["left shift", "right shift"] and "shift" in events_dict["key"]:
|
||||||
|
events_dict["key"].remove("shift")
|
||||||
|
elif key_name in ["left alt", "right alt"] and "alt" in events_dict["key"]:
|
||||||
|
events_dict["key"].remove("alt")
|
||||||
|
elif len(key_name) == 1 and key_name in events_dict["key"]:
|
||||||
|
events_dict["key"].remove(key_name)
|
||||||
|
else:
|
||||||
|
#print("不重要事件")
|
||||||
|
return 0
|
||||||
|
return 1
|
||||||
|
class Anchor(object):
|
||||||
|
"""
|
||||||
|
"锚定点" 对象
|
||||||
|
镜像
|
||||||
|
@: 矩阵乘法
|
||||||
|
*: 中心/任意参照点缩放
|
||||||
|
中点/比例点
|
||||||
|
|
||||||
|
"""
|
||||||
|
poscale = (0, 0) # 位置
|
||||||
|
fixed = 3 # 小数点后精度
|
||||||
|
def __init__(self, *args):
|
||||||
|
if isinstance(args[0], tuple) or isinstance(args[0], Anchor):
|
||||||
|
if isinstance(args[0], Anchor):
|
||||||
|
self.poscale = args[0].poscale
|
||||||
|
else:
|
||||||
|
self.poscale = args[0]
|
||||||
|
else:
|
||||||
|
self.poscale = (args[0], args[1])
|
||||||
|
def x(self):
|
||||||
|
return self.poscale[0]
|
||||||
|
def y(self):
|
||||||
|
return self.poscale[1]
|
||||||
|
def __add__(self, other):
|
||||||
|
"矩阵加法"
|
||||||
|
if isinstance(other, tuple):
|
||||||
|
return Anchor((self.poscale[0] + other[0], self.poscale[1] + other[1]))
|
||||||
|
elif isinstance(other, Anchor):
|
||||||
|
return Anchor((self.poscale[0] + other.poscale[0], self.poscale[1] + other.poscale[1]))
|
||||||
|
else:
|
||||||
|
raise TypeError("不支持的加法操作数类型")
|
||||||
|
def __mul__(self, other):
|
||||||
|
"中心倍增/倍缩"
|
||||||
|
if isinstance(other, tuple):
|
||||||
|
return Anchor((0.5 + other[0] * (self.poscale[0] - 0.5), 0.5 + other[1] * (self.poscale[1] - 0.5)))
|
||||||
|
elif isinstance(other, (int, float)):
|
||||||
|
return Anchor((0.5 + other * (self.poscale[0] - 0.5), 0.5 + other * (self.poscale[1] - 0.5)))
|
||||||
|
else:
|
||||||
|
raise TypeError("不支持的乘法操作数类型")
|
||||||
|
def __imul__(self, other):
|
||||||
|
result = self.__mul__(other)
|
||||||
|
self.poscale = result.poscale
|
||||||
|
return self
|
||||||
|
def __truediv__(self, other):
|
||||||
|
"乘以倒数"
|
||||||
|
if isinstance(other, (int, float)):
|
||||||
|
if other == 0:
|
||||||
|
return Anchor((0.5, 0.5))
|
||||||
|
else:
|
||||||
|
return self.__mul__(1 / other)
|
||||||
|
elif isinstance(other, Anchor) or isinstance(other, tuple):
|
||||||
|
divisor_x = other[0] if isinstance(other, tuple) else other.x()
|
||||||
|
divisor_y = other[1] if isinstance(other, tuple) else other.y()
|
||||||
|
# TODO: 错误代码, 应当予以修正
|
||||||
|
if divisor_x == 0 and divisor_y == 0:
|
||||||
|
return Anchor((0.5, 0.5))
|
||||||
|
if divisor_x == 0 and divisor_y == 0:
|
||||||
|
return Anchor((0.5, 0.5))
|
||||||
|
else:
|
||||||
|
return self.__mul__((1 / divisor_x, 1 / divisor_y))
|
||||||
|
else:
|
||||||
|
raise TypeError("不支持的除法操作数类型")
|
||||||
|
def __itruediv__(self, other):
|
||||||
|
"就地除法"
|
||||||
|
result = self.__truediv__(other)
|
||||||
|
self.poscale = result.poscale
|
||||||
|
return self
|
||||||
|
def __str__(self):
|
||||||
|
if self.fixed is not None:
|
||||||
|
return f"({round(self.poscale[0], self.fixed)}, {round(self.poscale[1], self.fixed)})"
|
||||||
|
else:
|
||||||
|
return f"({self.poscale[0]}, {self.poscale[1]})"
|
||||||
|
class Element(object):
|
||||||
|
is_hide = False
|
||||||
|
is_template = True
|
||||||
|
attached = None
|
||||||
|
attached_frame = None
|
||||||
|
attached_window = None
|
||||||
|
tasks = dict()
|
||||||
|
def __init__(self, name="Element"):
|
||||||
|
self.name = name
|
||||||
|
def attach(self, frame_object, clone_name=""):
|
||||||
|
if clone_name == "":
|
||||||
|
clone_name = str(uuid.uuid4())
|
||||||
|
clone = copy.deepcopy(self)
|
||||||
|
clone.is_template = False
|
||||||
|
frame_object.elements[clone_name] = clone
|
||||||
|
clone.attached_frame = frame_object
|
||||||
|
clone.attached_window = frame_object.attached_window
|
||||||
|
return clone
|
||||||
|
def teleport(self, delta: tuple):
|
||||||
|
if not self.is_template:
|
||||||
|
pass
|
||||||
|
def add_task(self, group, start_time, duration, **kwargs):
|
||||||
|
if group not in self.tasks():
|
||||||
|
self.tasks[group] = list()
|
||||||
|
self.tasks[group].append({"start": start_time, "duration":duration, "para":kwargs})
|
||||||
|
def render():
|
||||||
|
print("未配置渲染器")
|
||||||
|
pass
|
||||||
|
def update():
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Frame(object):
|
||||||
|
surface = None
|
||||||
|
elements = dict()
|
||||||
|
is_hide = False
|
||||||
|
is_template = True
|
||||||
|
attached_window = None
|
||||||
|
poscale: tuple = (0, 0) # 左上角, 对于每个实例的位置矢量
|
||||||
|
def __init__(self, name="Frame"):
|
||||||
|
self.name = name
|
||||||
|
def attach(self, window_object, poscale, clone_name=""):
|
||||||
|
if clone_name == "":
|
||||||
|
clone_name = str(uuid.uuid4())
|
||||||
|
clone = copy.deepcopy(self)
|
||||||
|
clone.surface = pygame.Surface(window_object.size)
|
||||||
|
clone.poscale = poscale
|
||||||
|
clone.is_template = False
|
||||||
|
clone.attached_window = window_object
|
||||||
|
window_object.frames[clone_name] = clone
|
||||||
|
return window_object.frames[clone_name]
|
||||||
|
def render(self):
|
||||||
|
for i in self.elements.values():
|
||||||
|
i.render()
|
||||||
|
self.attached_window.screen.blit(self.surface, self.poscale)
|
||||||
|
|
||||||
|
class Window(object):
|
||||||
|
frames = dict()
|
||||||
|
events = {
|
||||||
|
"cursor": {
|
||||||
|
"position": (0,0),
|
||||||
|
"relative": (0,0),
|
||||||
|
"poscale": (0,0),
|
||||||
|
"relscale": (0,0),
|
||||||
|
},
|
||||||
|
"wheel": 0, # 远离用户为 1, 靠近为 -1, 无动作为 0
|
||||||
|
"click": [], # 被按下的按钮
|
||||||
|
"focus": 1, # 或 0
|
||||||
|
"key": [],
|
||||||
|
"delta": "" # 变化的键值, 避免多次调用
|
||||||
|
}
|
||||||
|
"""{
|
||||||
|
"cursor": {
|
||||||
|
"position": (15,12),
|
||||||
|
"relative": (13,11),
|
||||||
|
"poscale": (0.1,0.5),
|
||||||
|
"relscale": (0.05,0.1),
|
||||||
|
},
|
||||||
|
"wheel": 1, # 远离用户为 1, 靠近为 -1, 无动作为 0
|
||||||
|
"click": [1, 2], # 被按下的按钮
|
||||||
|
"focus": 1, # 或 0
|
||||||
|
"key": ["ctrl", "k"],
|
||||||
|
"delta": ["key"]
|
||||||
|
}"""
|
||||||
|
observers = list()
|
||||||
|
def __init__(self, title="Vector Graphic Layer Window", size: tuple=(1024,768)):
|
||||||
|
self.title = title
|
||||||
|
self.size = size
|
||||||
|
self.running = 1
|
||||||
|
pygame.init()
|
||||||
|
self.screen = pygame.display.set_mode(size)
|
||||||
|
pygame.display.set_caption(title)
|
||||||
|
def set_title(self, title):
|
||||||
|
self.title = title
|
||||||
|
pygame.display.set_caption(title)
|
||||||
|
def start(self):
|
||||||
|
self.thr = threading.Thread(target=self.main_loop, name="main_loop")
|
||||||
|
self.thr.start()
|
||||||
|
def kill(self):
|
||||||
|
self.running = 0
|
||||||
|
def main_loop(self):
|
||||||
|
while self.running:
|
||||||
|
for event in pygame.event.get():
|
||||||
|
if event.type == pygame.QUIT:
|
||||||
|
self.running = 0
|
||||||
|
if_matters = Aux.eventproc(self.events, event, self.size)
|
||||||
|
if if_matters:
|
||||||
|
#print(self.observers)
|
||||||
|
for i in self.observers:
|
||||||
|
i(self.events)
|
||||||
|
#print(self.events)
|
||||||
|
|
||||||
|
for i in self.frames.values():
|
||||||
|
i.render()
|
||||||
|
pygame.display.flip()
|
||||||
|
pygame.time.delay(10)
|
||||||
|
pygame.quit()
|
||||||
|
def observerize(self, func):
|
||||||
|
def wrapper():
|
||||||
|
self.observers.append(func)
|
||||||
|
return wrapper
|
||||||
|
def remove_observer(self, func):
|
||||||
|
self.observers.remove(func)
|
28
vgl/test.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import pygame
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# 初始化 Pygame
|
||||||
|
pygame.init()
|
||||||
|
|
||||||
|
# 设置窗口大小
|
||||||
|
screen = pygame.display.set_mode((800, 600))
|
||||||
|
|
||||||
|
# 设置颜色 (RGB)
|
||||||
|
background_color = (255, 255, 255) # 白色背景
|
||||||
|
line_color = (255, 0, 0) # 红色线条
|
||||||
|
|
||||||
|
# 主循环
|
||||||
|
while True:
|
||||||
|
for event in pygame.event.get():
|
||||||
|
if event.type == pygame.QUIT:
|
||||||
|
pygame.quit()
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
# 填充背景
|
||||||
|
screen.fill(background_color)
|
||||||
|
|
||||||
|
# 绘制抗锯齿线条
|
||||||
|
pygame.draw.aaline(screen, line_color, (100, 100), (700, 500))
|
||||||
|
|
||||||
|
# 更新显示
|
||||||
|
pygame.display.flip()
|