From bce75a1727902f4482bb00eaaee6c4de974f9508 Mon Sep 17 00:00:00 2001 From: david-ajax Date: Fri, 4 Apr 2025 22:19:27 +0800 Subject: [PATCH] major improvements --- pulsar/__pycache__/main.cpython-312.pyc | Bin 0 -> 867 bytes pulsar/layouts.yaml | 16 ++ pulsar/main.py | 25 +++ pulsar/vgl | 1 + testfield/vgl/test.py | 0 testfield/vgl/vgllib old.py | 189 ------------------ {testfield/vgl => vgl}/README.md | 0 vgl/__init__.py | 7 + vgl/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 448 bytes vgl/__pycache__/basic.cpython-312.pyc | Bin 0 -> 7925 bytes .../basic_elements.cpython-312.pyc | Bin 0 -> 7144 bytes vgl/__pycache__/main.cpython-312.pyc | Bin 0 -> 7396 bytes vgl/__pycache__/vgllib.cpython-312.pyc | Bin 0 -> 8221 bytes .../ basic.py => vgl/basic_elements.py | 46 ++++- testfield/vgl/vgllib.py => vgl/main.py | 53 +++-- vgl/test.py | 28 +++ vgl/vgl | 1 + 17 files changed, 158 insertions(+), 208 deletions(-) create mode 100644 pulsar/__pycache__/main.cpython-312.pyc create mode 100644 pulsar/layouts.yaml create mode 100644 pulsar/main.py create mode 120000 pulsar/vgl delete mode 100644 testfield/vgl/test.py delete mode 100644 testfield/vgl/vgllib old.py rename {testfield/vgl => vgl}/README.md (100%) create mode 100644 vgl/__init__.py create mode 100644 vgl/__pycache__/__init__.cpython-312.pyc create mode 100644 vgl/__pycache__/basic.cpython-312.pyc create mode 100644 vgl/__pycache__/basic_elements.cpython-312.pyc create mode 100644 vgl/__pycache__/main.cpython-312.pyc create mode 100644 vgl/__pycache__/vgllib.cpython-312.pyc rename testfield/vgl/elements/ basic.py => vgl/basic_elements.py (66%) rename testfield/vgl/vgllib.py => vgl/main.py (63%) create mode 100644 vgl/test.py create mode 120000 vgl/vgl diff --git a/pulsar/__pycache__/main.cpython-312.pyc b/pulsar/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..34ca5146cc6b3a0611b2e4279320a2ffdbb38e85 GIT binary patch literal 867 zcmZuvO=uHA6rTNU8k5yZLHuj>B6Kg!Sx{@xMvGbm4}~Z!OJ>4Zu*62OBT@;R)5vq)9;dpAjF{+5Fe=O7|2N+cM1Si-Q z3S$7il9<9NsGOR@DZmO=_qB=k|Mhx~CKK3_n#j`EESU+pk!#K0^%-sXUg)^ys)uRO z34LbCKjH@8mYJA0*Gh~K&yu-HJRESX=(~`mbERH#xt3>6m2lk&L#J5gdV_k{Z}0?h zNtJkEz}5TIBT%euM2R!yxMd(znC73QY}v1ptW$BGWpDe%T2z$HG3q?5xq-v7oAFk4 zsJFD)2d<62r%rcQJF7469i^lH($FPb4QPv4Iuj4d#0y4J1-A!dvu&NK5H`ql8#qSne2RBqCN&144$Fu3S{##b0HEEbcYHFwTNjd*no$V(V f4^`_peQxJbx6moPD!+Mrn7+E5A8JUw5YhhyGZn}D literal 0 HcmV?d00001 diff --git a/pulsar/layouts.yaml b/pulsar/layouts.yaml new file mode 100644 index 0000000..f481770 --- /dev/null +++ b/pulsar/layouts.yaml @@ -0,0 +1,16 @@ +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 + status: static + args: + ends: [(0.4, 0.5), (0.6, 0.5)] + color: green + \ No newline at end of file diff --git a/pulsar/main.py b/pulsar/main.py new file mode 100644 index 0000000..354e91d --- /dev/null +++ b/pulsar/main.py @@ -0,0 +1,25 @@ +import vgl +import time +window = None +def horizontal_indicator(): + global window + frame = vgl.Frame().attach(window, (0, 0), "Horizontal Indicator") + vgl.elements.Line(ends=[(0.4, 0.5), (0.6, 0.5)], color="green").attach(frame) + pass +def console(): + print("You've entered Pulsar's command console, an embbedded Python interpreter for debugging & testing") + while True: + try: + exec(input(">>> ")) + except: + print("An error caused & captured") + +if __name__ == '__main__': + print("Welcome to AiraPulsar Client") + window = vgl.Window(title="Pulsar", size=(1600, 800)) + window.start() + horizontal_indicator() + #console() + time.sleep(3) + window.kill() + exit() \ No newline at end of file diff --git a/pulsar/vgl b/pulsar/vgl new file mode 120000 index 0000000..c640883 --- /dev/null +++ b/pulsar/vgl @@ -0,0 +1 @@ +/home/ajax/Documents/Index/302A/vgl \ No newline at end of file diff --git a/testfield/vgl/test.py b/testfield/vgl/test.py deleted file mode 100644 index e69de29..0000000 diff --git a/testfield/vgl/vgllib old.py b/testfield/vgl/vgllib old.py deleted file mode 100644 index 46b1317..0000000 --- a/testfield/vgl/vgllib old.py +++ /dev/null @@ -1,189 +0,0 @@ -# Vector Graphics Library -# vgllib.py -import pygame -import uuid -import time -import threading -import math - -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() - render_thread = None - motion_queue = list() - 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.render_thread = threading.Thread(target=self.render) - print("启动图形渲染子线程") - self.render_thread.start() - - def move(self, subname, direction, length, duration = 0, effect="linear"): # our powerful move! - # direction: 使用角度制, 以直角笛卡尔坐标系的x正半轴方向为0度, 逆时针为加, 接受负数 - # length: 百分数 - # duration: "动画"时间, 为0则即时 - # effect: "动画"效果, linear为线性移动, 或许会在未来增加贝塞尔曲线 - # TODO: 增加 bezier 曲线 - if duration == 0: - self.components[subname]["pos"][0] += math.cos(math.radians(direction)) * length / 100 * self.size[0] - self.components[subname]["pos"][1] += math.sin(math.radians(direction)) * length / 100 * self.size[1] - return - self.motion_queue.append({"subname":subname, "direction":direction, "length":length, "start":round(time.time(), 1), "duration":duration, "effect":"linear"}) - - def render(self): - while 1: - rest = list() - while self.motion_queue: - i = self.motion_queue.pop(0) # 从队列的开头移除元素 - self.move(subname=i['subname'], direction=i['direction'], - length=((time.time() - i['start']) / i['duration']) * i['length'], - duration=0) - if ((time.time() - i['start']) / i['duration']) > 0: - i['start'] = time.time() - i['duration'] -= (time.time() - i['start']) - rest.append(i) - self.draw_all() - self.motion_queue = rest - time.sleep(0.1) - - - 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__": - frame = None - def grap(): - global frame - pygame.init() - window = pygame.display.set_mode((1200, 900)) - frame = Frame("Test", (1200, 900)) - #frame.load() - frame.register(subname="test", attr={'method': 'rect', 'pos': (33, 33), 'size': (50, 50), 'color': (255, 255, 255)}) - #frame.move(subname='test', direction=0, length=90, duration=1, effect="linear") - running = True - while running: - for event in pygame.event.get(): - if event.type == pygame.QUIT: - running = False - - window.fill((0, 0, 0)) - frame.show(window, (0, 0)) - pygame.display.flip() - pygame.time.delay(10) - pygame.quit() - def debug(): - global frame - while 1: - try: - e = input(">>>") - if e == "f": - e = "frame.move(subname='test', direction=0, length=3, duration=1, effect='linear')" - if e == "g": - e = "frame.move(subname='test', direction=180, length=3, duration=1, effect='linear')" - exec(e) - except: - print("ER") - grap_thd = threading.Thread(target=grap) - debug_thd = threading.Thread(target=debug) - grap_thd.start() - debug_thd.start() \ No newline at end of file diff --git a/testfield/vgl/README.md b/vgl/README.md similarity index 100% rename from testfield/vgl/README.md rename to vgl/README.md diff --git a/vgl/__init__.py b/vgl/__init__.py new file mode 100644 index 0000000..d1e7c50 --- /dev/null +++ b/vgl/__init__.py @@ -0,0 +1,7 @@ +import os +os.environ["PYGAME_HIDE_SUPPORT_PROMPT"] = "True" +from .main import * +from . import basic_elements as elements + +version = '0.1.0' +print(f"Powered by Vector Graphic Layer, version {version}") \ No newline at end of file diff --git a/vgl/__pycache__/__init__.cpython-312.pyc b/vgl/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6c62fbe3ae0a6f43942e3ef259e98d23f0fe2fc0 GIT binary patch literal 448 zcmYLEJxjw-6umD^npjc%m~_f!qtdhvE*3#hRInOq73>mX-^0F=kMfdM>!g!E!9n~1 zB0_(L;8F)cp9(^!P6}>LzNp1J+;a}xbMC!&LqjPLwbp$v$-957(y zLK(0`peFT){t+kgLSPvyu5=@zm?hnWCzln$)pAk-qGF9CFW^$TM!!A;Eo2PzVzJ5P z5^E4XY3;2p6;|xE_2m_Nd&jc2iY41BZWXLjFLoJ-qiNBG_nnYdY~m7+_)&PQWHY%; z_Smq3L&6Bw_gnfdsYC(OSD90<(u%&}vL_)W?JpUa*@eGW_& zOSsn0J{Q~2!kbX;zo~Tct TbhKQ1sy)$3E<8Y~86Y!zmjp>Rb|1(igEcSV9F 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