physicsLab 2.0.0__tar.gz → 2.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {physicslab-2.0.0 → physicslab-2.0.2}/PKG-INFO +3 -3
- {physicslab-2.0.0 → physicslab-2.0.2}/README.md +14 -12
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/__init__.py +7 -4
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/_colorUtils.py +3 -3
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/_core.py +129 -50
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/_tools.py +7 -6
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/celestial/_planetbase.py +17 -20
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/celestial/planets.py +15 -2
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/chart.py +1 -1
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/circuit/__init__.py +0 -2
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/circuit/_circuit_core.py +51 -39
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/circuit/elements/artificialCircuit.py +329 -47
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/circuit/elements/basicCircuit.py +192 -27
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/circuit/elements/logicCircuit.py +322 -61
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/circuit/elements/otherCircuit.py +93 -13
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/circuit/elements/sensor.py +1 -1
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/electromagnetism/_electromagnetismBase.py +14 -17
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/electromagnetism/elements.py +1 -1
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/element.py +32 -27
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/enums.py +1 -1
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/errors.py +6 -6
- physicslab-2.0.2/physicsLab/lib/__init__.py +3 -0
- physicslab-2.0.2/physicsLab/lib/analog_circuit/__init__.py +2 -0
- physicslab-2.0.2/physicsLab/lib/analog_circuit/analog.py +869 -0
- {physicslab-2.0.0/physicsLab/lib → physicslab-2.0.2/physicsLab/lib/logic_circuit}/edge_trigger.py +14 -8
- {physicslab-2.0.0/physicsLab/lib → physicslab-2.0.2/physicsLab/lib/logic_circuit}/logic.py +89 -142
- {physicslab-2.0.0/physicsLab/lib → physicslab-2.0.2/physicsLab/lib/logic_circuit}/super_logic_gate.py +92 -27
- {physicslab-2.0.0/physicsLab/lib → physicslab-2.0.2/physicsLab/lib/logic_circuit}/wires.py +7 -5
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/music.py +5 -6
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/web/__init__.py +0 -1
- physicslab-2.0.0/physicsLab/web/api.py → physicslab-2.0.2/physicsLab/web/_api.py +206 -377
- physicslab-2.0.2/physicsLab/web/_threadpool.py +115 -0
- physicslab-2.0.2/physicsLab/web/api.py +187 -0
- physicslab-2.0.2/physicsLab/web/webutils.py +522 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab.egg-info/PKG-INFO +3 -3
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab.egg-info/SOURCES.txt +10 -7
- physicslab-2.0.2/physicsLab.egg-info/requires.txt +3 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/setup.py +2 -2
- physicslab-2.0.0/physicsLab/circuit/elementXYZ.py +0 -57
- physicslab-2.0.0/physicsLab/web/_async_tool.py +0 -44
- physicslab-2.0.0/physicsLab/web/webutils.py +0 -421
- physicslab-2.0.0/physicsLab.egg-info/requires.txt +0 -3
- {physicslab-2.0.0 → physicslab-2.0.2}/LICENSE +0 -0
- /physicslab-2.0.0/physicsLab/typehint.py → /physicslab-2.0.2/physicsLab/_typing.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/celestial/__init__.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/circuit/elements/__init__.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/electromagnetism/__init__.py +0 -0
- {physicslab-2.0.0/physicsLab/lib → physicslab-2.0.2/physicsLab/lib/logic_circuit}/__init__.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/__init__.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/__init__.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/frozen.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/messages/__init__.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/messages/checks.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/messages/decode.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/messages/encode.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/messages/messages.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/messages/specs.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/messages/strings.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/midifiles/__init__.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/midifiles/meta.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/midifiles/midifiles.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/midifiles/tracks.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/midifiles/units.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/parser.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/ports.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/scripts/__init__.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/scripts/mido_connect.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/scripts/mido_play.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/scripts/mido_ports.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/scripts/mido_serve.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/sockets.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/syx.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/music/mido/tokenizer.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/plAR.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/savTemplate.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab/utils.py +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab.egg-info/dependency_links.txt +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/physicsLab.egg-info/top_level.txt +0 -0
- {physicslab-2.0.0 → physicslab-2.0.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: physicsLab
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.2
|
|
4
4
|
Summary: Python API for Quantum-Physics App
|
|
5
5
|
Home-page: https://github.com/GoodenoughPhysicsLab/physicsLab
|
|
6
6
|
Author: Arendelle
|
|
@@ -14,8 +14,8 @@ Requires-Python: >=3.8
|
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
15
15
|
License-File: LICENSE
|
|
16
16
|
Requires-Dist: typing-extensions
|
|
17
|
-
Requires-Dist: requests
|
|
18
|
-
Requires-Dist: colorama
|
|
17
|
+
Requires-Dist: requests==2.32.3
|
|
18
|
+
Requires-Dist: colorama==0.4.6
|
|
19
19
|
Dynamic: author
|
|
20
20
|
Dynamic: author-email
|
|
21
21
|
Dynamic: classifier
|
|
@@ -17,10 +17,11 @@
|
|
|
17
17
|
* 将midi转换为物实对应的电路
|
|
18
18
|
* 修改物实的实验封面
|
|
19
19
|
* 获取用户的所有头像或实验用过的所有封面
|
|
20
|
+
|
|
20
21
|
更多好用的功能等你来发现
|
|
21
22
|
|
|
22
23
|
## 功能支持
|
|
23
|
-
*
|
|
24
|
+
* 跨平台支持,只要`Python3.8+`能在该平台上运行并且能够读写文件,比如`Windows7+`, `Linux`, `MacOS`, `Android`
|
|
24
25
|
* 支持物实**所有**实验类型:电学,天体物理,电与磁
|
|
25
26
|
* 支持物实**全部**元件
|
|
26
27
|
* 大多数物实网络api封装的支持 (直接与物实服务器进行交互)
|
|
@@ -29,13 +30,14 @@
|
|
|
29
30
|
自版本`2.0.0`及之后, `physicsLab`会逐步增加兼容的考虑与支持
|
|
30
31
|
|
|
31
32
|
但这并不意味着无意义的兼容。通常来说,会采用标注弃用, 并推荐转移到新api的模式。
|
|
32
|
-
* v2.0.0: `
|
|
33
|
+
* v2.0.0: `class Experiment, ExperimentOpenedError, ExperimentClosedError, ExperimentExistError, ExperimentNotExistError`实验性地加入`stable`支持
|
|
34
|
+
* v2.0.1: 三大实验所有元件的类名实验性地加入`stable`支持
|
|
33
35
|
|
|
34
36
|
## 版本发布
|
|
35
37
|
`physicsLab`的版本发布采取快照的方式, `physicsLab`仅会维护`trunk`
|
|
36
38
|
|
|
37
39
|
## 安装教程
|
|
38
|
-
1. 请确保你的电脑有[Python](https://www.python.org)(>=3.8)与[物理实验室AR](https://www.turtlesim.com/)(简称`物实`)(也可以联系物理实验室的开发者[
|
|
40
|
+
1. 请确保你的电脑有[Python](https://www.python.org)(>=3.8)与[物理实验室AR](https://www.turtlesim.com/)(简称`物实`)(也可以联系物理实验室的开发者[John-Chen](https://gitee.com/civitasjohn))
|
|
39
41
|
|
|
40
42
|
2. 在cmd或shell输入以下载physicsLab:
|
|
41
43
|
```shell
|
|
@@ -45,7 +47,7 @@ pip install physicsLab
|
|
|
45
47
|
```shell
|
|
46
48
|
python -m pip install physicsLab
|
|
47
49
|
```
|
|
48
|
-
> Note: 在`Windows`下你可以输入`py`来使用`
|
|
50
|
+
> Note: 在`Windows`下你可以输入`py`来使用`Python`,`Linux, MacOS`下可能需要输入`python3`或者`python3.x`(`python`加上你的`Python`版本)来使用`python`
|
|
49
51
|
|
|
50
52
|
3. 有一个并非必需的功能:播放midi(仅在Windows下可用)。你可以输入下面命令的任意一条:
|
|
51
53
|
```shell
|
|
@@ -64,10 +66,10 @@ pip install chardet
|
|
|
64
66
|
> Note: 每次通过`physicsLab`生成了一个新的存档之后,都需要重新加载物实的本地存档,即点击`从本地读取`,再次点击进入对应存档
|
|
65
67
|
|
|
66
68
|
### 新手解惑: 为什么我明明安装了physicsLab, python却告诉我无法找到?
|
|
67
|
-
pip
|
|
68
|
-
这大概率是因为pip安装的包所对应的`site-
|
|
69
|
-
解决方案:找到ide调用的`python`对应的`site-
|
|
70
|
-
同时我推荐去学一下`
|
|
69
|
+
`pip`安装的包会被放在`site-packages`文件夹下
|
|
70
|
+
这大概率是因为pip安装的包所对应的`site-packages`与你使用的`Python`对应的`site-packages`不一样导致的
|
|
71
|
+
解决方案:找到ide调用的`python`对应的`site-packages`,然后把`physicsLab`与`physicsLab.egg-info`复制过去
|
|
72
|
+
同时我推荐去学一下`Python`的虚拟环境`venv`,有效解决此问题
|
|
71
73
|
|
|
72
74
|
如果此方法失效了,虽然这一定不是这个方法的问题,但你还可以在python的开头写上这两行代码来解决这个问题:
|
|
73
75
|
```python
|
|
@@ -75,8 +77,8 @@ import sys
|
|
|
75
77
|
sys.path.append("/your/path/of/physicsLab") # 将字符串替换为你想添加的路径
|
|
76
78
|
```
|
|
77
79
|
这个方法很丑陋但很简单好用,可以帮你快速解决问题,毕竟能跑起来就很不错了
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
其原理是`Python`会在`sys.path`这个列表里面的路径去寻找`Python Package`,若未找到则会报错。因此该方法的原理就是把`Python`找不到的路径加进去,`Python`就找到了
|
|
81
|
+
注:每次运行的时候的`sys.path`都是临时的,因此该方法必须让`Python`在每次运行的时候都执行一遍
|
|
80
82
|
|
|
81
83
|
## 特殊说明事项
|
|
82
84
|
* 如果`physicsLab`抛出`AssertionError`,请**报告 bug** (请在issue中附上最小复现)
|
|
@@ -96,8 +98,8 @@ sys.path.append("/your/path/of/physicsLab") # 将字符串替换为你想添加
|
|
|
96
98
|
|
|
97
99
|
## 优点
|
|
98
100
|
* `physicsLab`拥有优秀的与物实存档交互的能力,你甚至可以使用程序完成部分工作之后你再继续完成或者让程序在你已完成的实验的基础上继续完成。
|
|
99
|
-
|
|
100
|
-
* `physicsLab
|
|
101
|
+
如此灵活的功能使得`physicsLab`即使是在`Python`的`shell`上也能出色的完成工作!
|
|
102
|
+
* `physicsLab`为纯`Python`库,其c拓展部分(播放midi的部分)被放到了`plmidi`中,但`plmidi`不是必须需要的。纯`Python`库通常意味着更容易使用,更少的问题。
|
|
101
103
|
* 封装了物实里的大量元件,即使是***未解锁的元件***也可以轻易用脚本生成,甚至一些常用的电路也被封装好了!
|
|
102
104
|
* 物理实验室存档的位置有点隐蔽,但用该脚本生成实验时,你无须亲自寻找这个文件在哪里。
|
|
103
105
|
* 外部依赖少
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
''' Python API for Physics-Lab-AR '''
|
|
3
3
|
|
|
4
|
-
# 颜色打印
|
|
5
4
|
from ._colorUtils import close_color_print
|
|
6
5
|
# 操作实验
|
|
7
6
|
from .element import search_experiment, Experiment
|
|
8
|
-
from ._core import
|
|
7
|
+
from ._core import (
|
|
8
|
+
ElementBase,
|
|
9
|
+
get_current_experiment,
|
|
10
|
+
elementXYZ_to_native,
|
|
11
|
+
native_to_elementXYZ,
|
|
12
|
+
ElementXYZ,
|
|
13
|
+
)
|
|
9
14
|
# 实验, 标签类型
|
|
10
15
|
from .enums import ExperimentType, Category, Tag, OpenMode, WireColor
|
|
11
16
|
# 电学实验
|
|
@@ -17,8 +22,6 @@ from .electromagnetism import *
|
|
|
17
22
|
# `physicsLab`自定义异常类
|
|
18
23
|
from .errors import *
|
|
19
24
|
|
|
20
|
-
from .lib.wires import crt_wires, del_wires
|
|
21
|
-
|
|
22
25
|
from physicsLab.plAR import *
|
|
23
26
|
from physicsLab.utils import *
|
|
24
27
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
+
''' 在命令行打印出有颜色的字 '''
|
|
2
3
|
from enum import Enum, unique
|
|
3
4
|
|
|
4
5
|
import colorama
|
|
@@ -15,10 +16,9 @@ class COLOR(Enum):
|
|
|
15
16
|
CYAN = colorama.Fore.CYAN
|
|
16
17
|
WHITE = colorama.Fore.WHITE
|
|
17
18
|
|
|
18
|
-
# 打印write_Experiment的信息时是否使用彩色字
|
|
19
19
|
_ColorSupport = True
|
|
20
20
|
|
|
21
|
-
def color_print(msg: str, color: COLOR, end=
|
|
21
|
+
def color_print(msg: str, color: COLOR, end='\n') -> None:
|
|
22
22
|
if not isinstance(color, COLOR) or not isinstance(msg, str):
|
|
23
23
|
raise TypeError
|
|
24
24
|
|
|
@@ -30,6 +30,6 @@ def color_print(msg: str, color: COLOR, end="\n") -> None:
|
|
|
30
30
|
print(msg, end=end)
|
|
31
31
|
|
|
32
32
|
def close_color_print():
|
|
33
|
-
'''
|
|
33
|
+
''' 关闭打印的文字是有颜色的功能 '''
|
|
34
34
|
global _ColorSupport
|
|
35
35
|
_ColorSupport = False
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
''' `physicsLab` 操作存档的核心文件
|
|
3
3
|
该文件提供操作存档的核心: `class _Experiment` 与所有元件的基类: class `_ElementBase`
|
|
4
4
|
为了避免在physicsLab内出现大量的cyclic import
|
|
5
|
-
该文件仅会对存档进行文件读写方面的操作,
|
|
6
|
-
`class Experiment
|
|
5
|
+
该文件仅会对存档进行文件读写方面的操作, 将元件的的信息进一步导入由`class Experiment`负责
|
|
6
|
+
`class Experiment`也提供了对用户更加友好的接口
|
|
7
7
|
'''
|
|
8
8
|
import os
|
|
9
9
|
import json
|
|
@@ -17,9 +17,9 @@ from physicsLab import plAR
|
|
|
17
17
|
from physicsLab import _tools
|
|
18
18
|
from physicsLab import errors
|
|
19
19
|
from physicsLab import _colorUtils
|
|
20
|
-
from .web import
|
|
20
|
+
from .web._api import _User, _check_response
|
|
21
21
|
from .enums import Category, Tag, ExperimentType, OpenMode
|
|
22
|
-
from .
|
|
22
|
+
from ._typing import Union, Optional, List, Dict, num_type, Self, Callable, Tuple, final, NoReturn
|
|
23
23
|
|
|
24
24
|
class _ExperimentStack:
|
|
25
25
|
data: List["_Experiment"] = []
|
|
@@ -56,7 +56,7 @@ class _ExperimentStack:
|
|
|
56
56
|
@classmethod
|
|
57
57
|
def top(cls) -> "_Experiment":
|
|
58
58
|
if len(cls.data) == 0:
|
|
59
|
-
raise errors.ExperimentError("no experiment can be operated
|
|
59
|
+
raise errors.ExperimentError("no experiment can be operated")
|
|
60
60
|
|
|
61
61
|
return cls.data[-1]
|
|
62
62
|
|
|
@@ -76,18 +76,18 @@ def _check_not_closed(method: Callable) -> Callable:
|
|
|
76
76
|
class _Experiment:
|
|
77
77
|
''' 物实实验 (支持物实的三种实验类型) '''
|
|
78
78
|
|
|
79
|
-
if "PHYSICSLAB_HOME_PATH" in os.environ
|
|
79
|
+
if "PHYSICSLAB_HOME_PATH" in os.environ:
|
|
80
80
|
SAV_PATH_DIR = os.environ["PHYSICSLAB_HOME_PATH"]
|
|
81
81
|
else:
|
|
82
82
|
if platform.system() == "Windows":
|
|
83
83
|
SAV_PATH_DIR = os.path.join(plAR.WIN_PLAR_HOME_DIR, "Circuit")
|
|
84
84
|
else:
|
|
85
|
-
SAV_PATH_DIR = "physicsLabSav"
|
|
85
|
+
SAV_PATH_DIR = os.path.abspath("physicsLabSav")
|
|
86
86
|
|
|
87
87
|
open_mode: OpenMode
|
|
88
|
-
_position2elements: Dict[Tuple[num_type, num_type, num_type], List["
|
|
89
|
-
_id2element: Dict[str, "
|
|
90
|
-
Elements: List["
|
|
88
|
+
_position2elements: Dict[Tuple[num_type, num_type, num_type], List["ElementBase"]]
|
|
89
|
+
_id2element: Dict[str, "ElementBase"]
|
|
90
|
+
Elements: List["ElementBase"]
|
|
91
91
|
SAV_PATH: str
|
|
92
92
|
PlSav: dict
|
|
93
93
|
CameraSave: dict
|
|
@@ -98,6 +98,21 @@ class _Experiment:
|
|
|
98
98
|
def __init__(*args, **kwargs) -> NoReturn:
|
|
99
99
|
raise NotImplementedError
|
|
100
100
|
|
|
101
|
+
@property
|
|
102
|
+
@_check_not_closed
|
|
103
|
+
def is_elementXYZ(self) -> bool:
|
|
104
|
+
return self._is_elementXYZ
|
|
105
|
+
|
|
106
|
+
@is_elementXYZ.setter
|
|
107
|
+
@_check_not_closed
|
|
108
|
+
def is_elementXYZ(self, status) -> None:
|
|
109
|
+
if not isinstance(status, bool):
|
|
110
|
+
raise TypeError
|
|
111
|
+
if self.experiment_type != ExperimentType.Circuit:
|
|
112
|
+
raise errors.ExperimentTypeError
|
|
113
|
+
|
|
114
|
+
self._is_elementXYZ = status
|
|
115
|
+
|
|
101
116
|
@_check_not_closed
|
|
102
117
|
def get_elements_count(self) -> int:
|
|
103
118
|
''' 该实验的元件的数量 '''
|
|
@@ -114,11 +129,11 @@ class _Experiment:
|
|
|
114
129
|
return self
|
|
115
130
|
|
|
116
131
|
@_check_not_closed
|
|
117
|
-
def del_element(self, element: "
|
|
132
|
+
def del_element(self, element: "ElementBase") -> Self:
|
|
118
133
|
''' 删除元件
|
|
119
134
|
@param element: 三大实验的元件
|
|
120
135
|
'''
|
|
121
|
-
if not isinstance(element,
|
|
136
|
+
if not isinstance(element, ElementBase):
|
|
122
137
|
raise TypeError
|
|
123
138
|
if element.experiment is not self:
|
|
124
139
|
raise errors.ExperimentError("element is not in this experiment") # TODO 换一个更好的异常类型?
|
|
@@ -128,14 +143,14 @@ class _Experiment:
|
|
|
128
143
|
identifier = element.data["Identifier"]
|
|
129
144
|
|
|
130
145
|
if self.experiment_type == ExperimentType.Circuit:
|
|
131
|
-
|
|
146
|
+
rest_wires = set()
|
|
132
147
|
for a_wire in self.Wires:
|
|
133
148
|
if a_wire.Source.element_self.data["Identifier"] == identifier \
|
|
134
149
|
or a_wire.Target.element_self.data["Identifier"] == identifier:
|
|
135
150
|
continue
|
|
136
151
|
|
|
137
|
-
|
|
138
|
-
self.Wires =
|
|
152
|
+
rest_wires.add(a_wire)
|
|
153
|
+
self.Wires = rest_wires
|
|
139
154
|
|
|
140
155
|
for position, elements in self._position2elements.items():
|
|
141
156
|
can_break: bool = False
|
|
@@ -158,29 +173,27 @@ class _Experiment:
|
|
|
158
173
|
|
|
159
174
|
return self
|
|
160
175
|
|
|
161
|
-
# TODO 统一返回的行为,即始终返回列表之类的
|
|
162
176
|
@_check_not_closed
|
|
163
177
|
def get_element_from_position(
|
|
164
178
|
self,
|
|
165
179
|
x: num_type,
|
|
166
180
|
y: num_type,
|
|
167
181
|
z: num_type,
|
|
168
|
-
) ->
|
|
182
|
+
) -> List["ElementBase"]:
|
|
169
183
|
''' 通过坐标索引元件 '''
|
|
170
|
-
if not isinstance(x, (int, float))
|
|
171
|
-
not isinstance(y, (int, float))
|
|
172
|
-
not isinstance(z, (int, float)):
|
|
184
|
+
if not isinstance(x, (int, float)) \
|
|
185
|
+
or not isinstance(y, (int, float)) \
|
|
186
|
+
or not isinstance(z, (int, float)):
|
|
173
187
|
raise TypeError
|
|
174
188
|
|
|
175
189
|
position = (_tools.round_data(x), _tools.round_data(y), _tools.round_data(z))
|
|
176
190
|
if position not in self._position2elements.keys():
|
|
177
191
|
raise errors.ElementNotFound(f"{position} do not exist")
|
|
178
192
|
|
|
179
|
-
|
|
180
|
-
return result[0] if len(result) == 1 else result
|
|
193
|
+
return self._position2elements[position]
|
|
181
194
|
|
|
182
195
|
@_check_not_closed
|
|
183
|
-
def get_element_from_index(self, index: int) -> "
|
|
196
|
+
def get_element_from_index(self, index: int) -> "ElementBase":
|
|
184
197
|
''' 通过index (元件生成顺序) 索引元件, index从1开始 '''
|
|
185
198
|
if not isinstance(index, int):
|
|
186
199
|
raise TypeError
|
|
@@ -190,7 +203,7 @@ class _Experiment:
|
|
|
190
203
|
return self.Elements[index - 1]
|
|
191
204
|
|
|
192
205
|
@_check_not_closed
|
|
193
|
-
def get_element_from_identifier(self, identifier: str) -> "
|
|
206
|
+
def get_element_from_identifier(self, identifier: str) -> "ElementBase":
|
|
194
207
|
''' 通过元件的id获取元件的引用 '''
|
|
195
208
|
res = self._id2element.get(identifier)
|
|
196
209
|
if res is None:
|
|
@@ -258,14 +271,17 @@ class _Experiment:
|
|
|
258
271
|
no_print_info: bool = False,
|
|
259
272
|
) -> Self:
|
|
260
273
|
''' 以物实存档的格式导出实验
|
|
261
|
-
@param target_path: 将存档保存在此路径 (
|
|
274
|
+
@param target_path: 将存档保存在此路径 (要求必须是文件的路径), 默认为 SAV_PATH
|
|
262
275
|
@param no_print_info: 是否打印写入存档的元件数, 导线数(如果是电学实验的话)
|
|
263
276
|
'''
|
|
264
|
-
if not isinstance(target_path, (str, type(None)))
|
|
277
|
+
if not isinstance(target_path, (str, type(None))) \
|
|
278
|
+
or not isinstance(no_print_info, bool):
|
|
265
279
|
raise TypeError
|
|
266
280
|
|
|
267
281
|
if target_path is None:
|
|
268
282
|
target_path = self.SAV_PATH
|
|
283
|
+
else:
|
|
284
|
+
target_path = os.path.abspath(target_path)
|
|
269
285
|
|
|
270
286
|
if self.open_mode in (OpenMode.load_by_sav_name, OpenMode.load_by_filepath, OpenMode.load_by_plar_app):
|
|
271
287
|
status: str = "update"
|
|
@@ -284,14 +300,16 @@ class _Experiment:
|
|
|
284
300
|
if not no_print_info:
|
|
285
301
|
if self.experiment_type == ExperimentType.Circuit:
|
|
286
302
|
_colorUtils.color_print(
|
|
287
|
-
f"Successfully {status} experiment \"{self.PlSav['InternalName']}\"
|
|
303
|
+
f"Successfully {status} experiment \"{self.PlSav['InternalName']}\""
|
|
304
|
+
f"(\"{target_path}\")! "
|
|
288
305
|
f"{self.get_elements_count()} elements, {self.get_wires_count()} wires.",
|
|
289
306
|
color=_colorUtils.COLOR.GREEN
|
|
290
307
|
)
|
|
291
308
|
elif self.experiment_type == ExperimentType.Celestial \
|
|
292
309
|
or self.experiment_type == ExperimentType.Electromagnetism:
|
|
293
310
|
_colorUtils.color_print(
|
|
294
|
-
f"Successfully {status} experiment \"{self.PlSav['InternalName']}\"
|
|
311
|
+
f"Successfully {status} experiment \"{self.PlSav['InternalName']}\""
|
|
312
|
+
f"(\"{target_path}\")! "
|
|
295
313
|
f"{self.get_elements_count()} elements.",
|
|
296
314
|
color=_colorUtils.COLOR.GREEN
|
|
297
315
|
)
|
|
@@ -376,17 +394,17 @@ class _Experiment:
|
|
|
376
394
|
|
|
377
395
|
def __upload(
|
|
378
396
|
self,
|
|
379
|
-
user:
|
|
397
|
+
user: _User,
|
|
380
398
|
category: Optional[Category],
|
|
381
399
|
image_path: Optional[str],
|
|
382
400
|
):
|
|
383
|
-
if
|
|
384
|
-
|
|
385
|
-
not isinstance(user,
|
|
401
|
+
if not isinstance(image_path, (str, type(None))) \
|
|
402
|
+
or not isinstance(category, (Category, type(None))) \
|
|
403
|
+
or not isinstance(user, _User):
|
|
386
404
|
raise TypeError
|
|
387
405
|
if image_path is not None and (not os.path.exists(image_path) or not os.path.isfile(image_path)):
|
|
388
406
|
raise FileNotFoundError
|
|
389
|
-
if user.
|
|
407
|
+
if not user.is_binded:
|
|
390
408
|
raise PermissionError("you must register first")
|
|
391
409
|
|
|
392
410
|
self.__write()
|
|
@@ -455,7 +473,7 @@ class _Experiment:
|
|
|
455
473
|
@_check_not_closed
|
|
456
474
|
def upload(
|
|
457
475
|
self,
|
|
458
|
-
user:
|
|
476
|
+
user: _User,
|
|
459
477
|
category: Category,
|
|
460
478
|
image_path: Optional[str] = None,
|
|
461
479
|
) -> Self:
|
|
@@ -464,8 +482,8 @@ class _Experiment:
|
|
|
464
482
|
@param category: 实验区还是黑洞区
|
|
465
483
|
@param image_path: 图片路径
|
|
466
484
|
'''
|
|
467
|
-
if not isinstance(category, Category)
|
|
468
|
-
|
|
485
|
+
if not isinstance(category, Category) \
|
|
486
|
+
or not isinstance(image_path, (str, type(None))):
|
|
469
487
|
raise TypeError
|
|
470
488
|
if self.PlSav["Summary"]["ID"] is not None:
|
|
471
489
|
raise Exception(
|
|
@@ -494,7 +512,7 @@ class _Experiment:
|
|
|
494
512
|
@_check_not_closed
|
|
495
513
|
def update(
|
|
496
514
|
self,
|
|
497
|
-
user:
|
|
515
|
+
user: _User,
|
|
498
516
|
image_path: Optional[str] = None,
|
|
499
517
|
) -> Self:
|
|
500
518
|
''' 更新实验到物实
|
|
@@ -617,11 +635,11 @@ class _Experiment:
|
|
|
617
635
|
x, y, z, elementXYZ为重新设置要合并的实验的坐标系原点在self的坐标系的位置
|
|
618
636
|
不是电学实验时, elementXYZ参数无效
|
|
619
637
|
'''
|
|
620
|
-
if not isinstance(other, _Experiment)
|
|
621
|
-
not isinstance(x, (int, float))
|
|
622
|
-
not isinstance(y, (int, float))
|
|
623
|
-
not isinstance(z, (int, float))
|
|
624
|
-
not isinstance(elementXYZ, (bool, type(bool))):
|
|
638
|
+
if not isinstance(other, _Experiment) \
|
|
639
|
+
or not isinstance(x, (int, float)) \
|
|
640
|
+
or not isinstance(y, (int, float)) \
|
|
641
|
+
or not isinstance(z, (int, float)) \
|
|
642
|
+
or not isinstance(elementXYZ, (bool, type(bool))):
|
|
625
643
|
raise TypeError
|
|
626
644
|
if self.experiment_type != other.experiment_type:
|
|
627
645
|
raise errors.ExperimentTypeError
|
|
@@ -640,11 +658,10 @@ class _Experiment:
|
|
|
640
658
|
a_element = copy.deepcopy(a_element, memo={id(a_element.experiment): self})
|
|
641
659
|
e_x, e_y, e_z = a_element.get_position()
|
|
642
660
|
if self.experiment_type == ExperimentType.Circuit:
|
|
643
|
-
from .circuit.elementXYZ import xyzTranslate, translateXYZ
|
|
644
661
|
if elementXYZ and not a_element.is_elementXYZ:
|
|
645
|
-
e_x, e_y, e_z =
|
|
662
|
+
e_x, e_y, e_z = native_to_elementXYZ(e_x, e_y, e_z, a_element.is_bigElement)
|
|
646
663
|
elif not elementXYZ and a_element.is_elementXYZ:
|
|
647
|
-
e_x, e_y, e_z =
|
|
664
|
+
e_x, e_y, e_z = elementXYZ_to_native(e_x, e_y, e_z, a_element.is_bigElement)
|
|
648
665
|
a_element.set_position(e_x + x, e_y + y, e_z + z, elementXYZ)
|
|
649
666
|
# set_Position已处理与_elements_position有关的操作
|
|
650
667
|
self.Elements.append(a_element)
|
|
@@ -664,7 +681,8 @@ class _Experiment:
|
|
|
664
681
|
|
|
665
682
|
return self
|
|
666
683
|
|
|
667
|
-
class
|
|
684
|
+
class ElementBase:
|
|
685
|
+
''' 三大类型实验的元件的基类 '''
|
|
668
686
|
data: dict
|
|
669
687
|
experiment: _Experiment
|
|
670
688
|
_position: _tools.position
|
|
@@ -674,9 +692,9 @@ class _ElementBase:
|
|
|
674
692
|
|
|
675
693
|
def set_position(self, x: num_type, y: num_type, z: num_type) -> Self:
|
|
676
694
|
''' 设置元件的位置 '''
|
|
677
|
-
if not isinstance(x, (int, float))
|
|
678
|
-
not isinstance(y, (int, float))
|
|
679
|
-
not isinstance(z, (int, float)):
|
|
695
|
+
if not isinstance(x, (int, float)) \
|
|
696
|
+
or not isinstance(y, (int, float)) \
|
|
697
|
+
or not isinstance(z, (int, float)):
|
|
680
698
|
raise TypeError
|
|
681
699
|
|
|
682
700
|
x, y, z = _tools.round_data(x), _tools.round_data(y), _tools.round_data(z)
|
|
@@ -699,7 +717,14 @@ class _ElementBase:
|
|
|
699
717
|
return self
|
|
700
718
|
|
|
701
719
|
@final
|
|
702
|
-
def
|
|
720
|
+
def _set_identifier(self, identifier: Optional[str] = None) -> None:
|
|
721
|
+
if identifier is None:
|
|
722
|
+
self.data["Identifier"] = _tools.randString(33)
|
|
723
|
+
else:
|
|
724
|
+
self.data["Identifier"] = identifier
|
|
725
|
+
|
|
726
|
+
@final
|
|
727
|
+
def get_position(self) -> _tools.position:
|
|
703
728
|
''' 获取元件的坐标 '''
|
|
704
729
|
assert hasattr(self, '_position')
|
|
705
730
|
return copy.deepcopy(self._position)
|
|
@@ -708,3 +733,57 @@ class _ElementBase:
|
|
|
708
733
|
def get_index(self) -> int:
|
|
709
734
|
''' 获取元件的index (每创建一个元件, index就加1 (index从1开始)) '''
|
|
710
735
|
return self.experiment.Elements.index(self) + 1
|
|
736
|
+
|
|
737
|
+
class ElementXYZ:
|
|
738
|
+
# 元件坐标系对应物实坐标系中的x, y, z的单位一
|
|
739
|
+
_X_UNIT: float = 0.16
|
|
740
|
+
_Y_UNIT: float = 0.08
|
|
741
|
+
_Z_UNIT: float = 0.1
|
|
742
|
+
# big_element坐标修正
|
|
743
|
+
_Y_AMEND: float = 0.045
|
|
744
|
+
|
|
745
|
+
def __init__(self) -> None:
|
|
746
|
+
self._expe = get_current_experiment()
|
|
747
|
+
self.origin_status: bool = self._expe.is_elementXYZ
|
|
748
|
+
|
|
749
|
+
def __enter__(self) -> None:
|
|
750
|
+
if self._expe.experiment_type != ExperimentType.Circuit:
|
|
751
|
+
raise errors.ExperimentTypeError
|
|
752
|
+
|
|
753
|
+
self._expe.is_elementXYZ = True
|
|
754
|
+
|
|
755
|
+
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
756
|
+
if exc_type is None:
|
|
757
|
+
self._expe.is_elementXYZ = self.origin_status
|
|
758
|
+
|
|
759
|
+
def elementXYZ_to_native(
|
|
760
|
+
x: num_type,
|
|
761
|
+
y: num_type,
|
|
762
|
+
z: num_type,
|
|
763
|
+
/,
|
|
764
|
+
is_bigElement: bool = False,
|
|
765
|
+
) -> Tuple[num_type, num_type, num_type]:
|
|
766
|
+
''' 将元件坐标系转换为物实的坐标系
|
|
767
|
+
@param is_bigElement: 是否为2体积的元件 (比如全加器)
|
|
768
|
+
'''
|
|
769
|
+
x *= ElementXYZ._X_UNIT
|
|
770
|
+
y *= ElementXYZ._Y_UNIT
|
|
771
|
+
z *= ElementXYZ._Z_UNIT
|
|
772
|
+
if is_bigElement:
|
|
773
|
+
y += ElementXYZ._Y_AMEND
|
|
774
|
+
return x, y, z
|
|
775
|
+
|
|
776
|
+
def native_to_elementXYZ(
|
|
777
|
+
x: num_type,
|
|
778
|
+
y: num_type,
|
|
779
|
+
z: num_type,
|
|
780
|
+
is_bigElement: bool = False,
|
|
781
|
+
) -> Tuple[num_type, num_type, num_type]:
|
|
782
|
+
''' 将物实的坐标系转换为元件坐标系 '''
|
|
783
|
+
x /= ElementXYZ._X_UNIT
|
|
784
|
+
y /= ElementXYZ._Y_UNIT
|
|
785
|
+
z /= ElementXYZ._Z_UNIT
|
|
786
|
+
# 修改大体积逻辑电路元件的坐标
|
|
787
|
+
if is_bigElement:
|
|
788
|
+
y -= ElementXYZ._Y_AMEND
|
|
789
|
+
return x, y, z
|
|
@@ -3,8 +3,9 @@ from random import choice
|
|
|
3
3
|
from string import ascii_lowercase, ascii_letters, digits
|
|
4
4
|
|
|
5
5
|
from collections import namedtuple
|
|
6
|
-
from .
|
|
6
|
+
from ._typing import num_type
|
|
7
7
|
|
|
8
|
+
# TODO 元件坐标系也应该由这玩意负责
|
|
8
9
|
position = namedtuple("position", ["x", "y", "z"])
|
|
9
10
|
|
|
10
11
|
def round_data(num: num_type) -> num_type:
|
|
@@ -12,13 +13,13 @@ def round_data(num: num_type) -> num_type:
|
|
|
12
13
|
raise TypeError
|
|
13
14
|
return round(num, 6)
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
def randString(length: int, is_lower: bool = False) -> str:
|
|
17
|
+
if not isinstance(length, int) \
|
|
18
|
+
or not isinstance(is_lower, bool):
|
|
18
19
|
raise TypeError
|
|
19
20
|
|
|
20
|
-
if
|
|
21
|
+
if is_lower:
|
|
21
22
|
letters = ascii_lowercase
|
|
22
23
|
else:
|
|
23
24
|
letters = ascii_letters
|
|
24
|
-
return ''.join(choice(letters + digits) for _ in range(
|
|
25
|
+
return ''.join(choice(letters + digits) for _ in range(length))
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
from physicsLab import _tools
|
|
3
3
|
from physicsLab import errors
|
|
4
4
|
from physicsLab.enums import ExperimentType
|
|
5
|
-
from physicsLab._core import get_current_experiment, _Experiment,
|
|
6
|
-
from physicsLab.
|
|
5
|
+
from physicsLab._core import get_current_experiment, _Experiment, ElementBase
|
|
6
|
+
from physicsLab._typing import num_type, Self, override, final, NoReturn, Optional
|
|
7
7
|
|
|
8
8
|
class _PlanetMeta(type):
|
|
9
9
|
def __call__(
|
|
@@ -15,16 +15,16 @@ class _PlanetMeta(type):
|
|
|
15
15
|
identifier: Optional[str] = None,
|
|
16
16
|
**kwargs,
|
|
17
17
|
):
|
|
18
|
-
if not isinstance(x, (int, float))
|
|
19
|
-
not isinstance(y, (int, float))
|
|
20
|
-
not isinstance(z, (int, float)):
|
|
18
|
+
if not isinstance(x, (int, float)) \
|
|
19
|
+
or not isinstance(y, (int, float)) \
|
|
20
|
+
or not isinstance(z, (int, float)):
|
|
21
21
|
raise TypeError
|
|
22
22
|
|
|
23
23
|
_Expe = get_current_experiment()
|
|
24
24
|
if _Expe.experiment_type != ExperimentType.Celestial:
|
|
25
25
|
raise errors.ExperimentTypeError
|
|
26
26
|
|
|
27
|
-
self: "PlanetBase" = cls.__new__(cls)
|
|
27
|
+
self: "PlanetBase" = cls.__new__(cls)
|
|
28
28
|
self.experiment = _Expe
|
|
29
29
|
|
|
30
30
|
x, y, z = _tools.round_data(x), _tools.round_data(y), _tools.round_data(z)
|
|
@@ -32,10 +32,7 @@ class _PlanetMeta(type):
|
|
|
32
32
|
self.__init__(x, y, z, *args, **kwargs)
|
|
33
33
|
assert isinstance(self.data, dict)
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
self.data["Identifier"] = _tools.randString(33)
|
|
37
|
-
else:
|
|
38
|
-
self.data["Identifier"] = identifier
|
|
35
|
+
self._set_identifier(identifier)
|
|
39
36
|
self.set_position(x, y, z)
|
|
40
37
|
self.set_velocity(0, 0, 0)
|
|
41
38
|
self.set_acceleration(0, 0, 0)
|
|
@@ -45,7 +42,7 @@ class _PlanetMeta(type):
|
|
|
45
42
|
|
|
46
43
|
return self
|
|
47
44
|
|
|
48
|
-
class PlanetBase(
|
|
45
|
+
class PlanetBase(ElementBase, metaclass=_PlanetMeta):
|
|
49
46
|
''' 星球基类 '''
|
|
50
47
|
experiment: _Experiment
|
|
51
48
|
|
|
@@ -56,9 +53,9 @@ class PlanetBase(_ElementBase, metaclass=_PlanetMeta):
|
|
|
56
53
|
@override
|
|
57
54
|
def set_position(self, x: num_type, y: num_type, z: num_type) -> Self:
|
|
58
55
|
''' 设置位置坐标 '''
|
|
59
|
-
if not isinstance(x, (int, float))
|
|
60
|
-
not isinstance(y, (int, float))
|
|
61
|
-
not isinstance(z, (int, float)):
|
|
56
|
+
if not isinstance(x, (int, float)) \
|
|
57
|
+
or not isinstance(y, (int, float)) \
|
|
58
|
+
or not isinstance(z, (int, float)):
|
|
62
59
|
raise TypeError
|
|
63
60
|
|
|
64
61
|
x, y, z = _tools.round_data(x), _tools.round_data(y), _tools.round_data(z)
|
|
@@ -68,9 +65,9 @@ class PlanetBase(_ElementBase, metaclass=_PlanetMeta):
|
|
|
68
65
|
def set_velocity(self, x_v: num_type, y_v: num_type, z_v: num_type) -> Self:
|
|
69
66
|
''' 设置速度
|
|
70
67
|
'''
|
|
71
|
-
if not isinstance(x_v, (int, float))
|
|
72
|
-
not isinstance(y_v, (int, float))
|
|
73
|
-
not isinstance(z_v, (int, float)):
|
|
68
|
+
if not isinstance(x_v, (int, float)) \
|
|
69
|
+
or not isinstance(y_v, (int, float)) \
|
|
70
|
+
or not isinstance(z_v, (int, float)):
|
|
74
71
|
raise TypeError
|
|
75
72
|
self.data["Velocity"] = f"{x_v},{z_v},{y_v}"
|
|
76
73
|
return self
|
|
@@ -78,9 +75,9 @@ class PlanetBase(_ElementBase, metaclass=_PlanetMeta):
|
|
|
78
75
|
def set_acceleration(self, x_a: num_type, y_a: num_type, z_a: num_type) -> Self:
|
|
79
76
|
''' 设置加速度
|
|
80
77
|
'''
|
|
81
|
-
if not isinstance(x_a, (int, float))
|
|
82
|
-
not isinstance(y_a, (int, float))
|
|
83
|
-
not isinstance(z_a, (int, float)):
|
|
78
|
+
if not isinstance(x_a, (int, float)) \
|
|
79
|
+
or not isinstance(y_a, (int, float)) \
|
|
80
|
+
or not isinstance(z_a, (int, float)):
|
|
84
81
|
raise TypeError
|
|
85
82
|
self.acceleration = _tools.position(x_a, y_a, z_a)
|
|
86
83
|
self.data["Acceleration"] = f"{x_a},{z_a},{y_a}"
|