physicsLab 1.4.5__tar.gz → 1.4.7__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-1.4.5 → physicslab-1.4.7}/PKG-INFO +1 -1
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/__init__.py +24 -4
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/_colorUtils.py +2 -2
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/circuit/elementXYZ.py +22 -23
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/circuit/elements/_elementBase.py +31 -30
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/circuit/elements/basicCircuit.py +37 -1
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/circuit/elements/logicCircuit.py +17 -1
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/circuit/elements/otherCircuit.py +2 -5
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/circuit/wire.py +6 -1
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/element.py +34 -24
- physicslab-1.4.7/physicsLab/elementBase.py +2 -0
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/errors.py +1 -1
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/experiment.py +110 -44
- {physicsLab-1.4.5/physicsLab/unit → physicslab-1.4.7/physicsLab/lib}/__init__.py +1 -1
- physicsLab-1.4.5/physicsLab/unit/unionLogic.py → physicslab-1.4.7/physicsLab/lib/logic.py +1 -1
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/music/music.py +37 -29
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab.egg-info/PKG-INFO +1 -1
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab.egg-info/SOURCES.txt +6 -5
- {physicsLab-1.4.5 → physicslab-1.4.7}/setup.py +1 -1
- {physicsLab-1.4.5 → physicslab-1.4.7}/LICENSE +0 -0
- {physicsLab-1.4.5 → physicslab-1.4.7}/README.md +0 -0
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/_tools.py +0 -0
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/celestial/__init__.py +0 -0
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/celestial/elementsClass.py +0 -0
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/circuit/__init__.py +0 -0
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/circuit/elements/__init__.py +0 -0
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/circuit/elements/artificialCircuit.py +0 -0
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/electromagnetism/__init__.py +0 -0
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/electromagnetism/elements.py +0 -0
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/experimentType.py +0 -0
- {physicsLab-1.4.5/physicsLab/unit → physicslab-1.4.7/physicsLab/lib}/_unionClassHead.py +0 -0
- {physicsLab-1.4.5/physicsLab/unit → physicslab-1.4.7/physicsLab/lib}/wires.py +0 -0
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/music/__init__.py +0 -0
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/savTemplate.py +0 -0
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/typehint.py +0 -0
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab.egg-info/dependency_links.txt +0 -0
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab.egg-info/requires.txt +0 -0
- {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab.egg-info/top_level.txt +0 -0
- {physicsLab-1.4.5 → physicslab-1.4.7}/setup.cfg +0 -0
|
@@ -17,8 +17,8 @@ from .element import *
|
|
|
17
17
|
# `physicsLab`自定义异常类
|
|
18
18
|
from .errors import *
|
|
19
19
|
# 模块化电路
|
|
20
|
-
from .
|
|
21
|
-
from physicsLab import
|
|
20
|
+
from .lib.wires import *
|
|
21
|
+
from physicsLab import lib
|
|
22
22
|
from physicsLab import music
|
|
23
23
|
|
|
24
24
|
# 检测操作系统
|
|
@@ -32,6 +32,26 @@ else:
|
|
|
32
32
|
if not os.path.exists("physicsLabSav"):
|
|
33
33
|
os.mkdir("physicsLabSav")
|
|
34
34
|
|
|
35
|
+
# 获取 Physics-Lab-AR 版本
|
|
36
|
+
def get_Physics_Lab_AR_version() -> Optional[str]:
|
|
37
|
+
if platform.system() == "Windows":
|
|
38
|
+
from getpass import getuser
|
|
39
|
+
version_file = f"C:/Users/{getuser()}/AppData/LocalLow/CIVITAS/Quantum Physics/Unity/" \
|
|
40
|
+
f"30fdf88e-c67c-4ae8-a0f5-83bb57b9a5c3/Analytics/values"
|
|
41
|
+
if os.path.exists(version_file):
|
|
42
|
+
import json
|
|
43
|
+
with open(version_file) as f:
|
|
44
|
+
version = json.load(f)["app_ver"]
|
|
45
|
+
return version
|
|
46
|
+
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
plAR_version = get_Physics_Lab_AR_version()
|
|
50
|
+
if plAR_version is not None:
|
|
51
|
+
_, mid, small = eval(f"({plAR_version.replace('.', ',')})")
|
|
52
|
+
if mid < 4 or mid == 4 and small < 7:
|
|
53
|
+
warning("the version of Physics-Lab-AR is less than v2.4.7")
|
|
54
|
+
|
|
35
55
|
__all__ = [
|
|
36
56
|
# _colorUtils.py
|
|
37
57
|
"close_color_print",
|
|
@@ -41,7 +61,7 @@ __all__ = [
|
|
|
41
61
|
|
|
42
62
|
# errors.py
|
|
43
63
|
"OpenExperimentError", "WireColorError", "WireNotFoundError", "bitLengthError",
|
|
44
|
-
"experimentExistError", "ExperimentTypeError", "
|
|
64
|
+
"experimentExistError", "ExperimentTypeError", "ElementNotFound", "crtExperimentFailError",
|
|
45
65
|
"ExperimentError", "set_warning_status", "WarningError", "ElementNotExistError",
|
|
46
66
|
|
|
47
67
|
# experiment.py
|
|
@@ -69,7 +89,7 @@ __all__ = [
|
|
|
69
89
|
"Resistance_Law", "Solenoid",
|
|
70
90
|
|
|
71
91
|
# unionElements
|
|
72
|
-
"
|
|
92
|
+
"lib", "music",
|
|
73
93
|
|
|
74
94
|
# wires.py
|
|
75
95
|
"crt_Wires", "del_Wires",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
import
|
|
2
|
+
import platform
|
|
3
3
|
from enum import Enum, unique
|
|
4
4
|
|
|
5
5
|
@unique
|
|
@@ -17,7 +17,7 @@ class COLOR(Enum):
|
|
|
17
17
|
# 打印write_Experiment的信息时是否使用彩色字
|
|
18
18
|
colorSupport = True
|
|
19
19
|
|
|
20
|
-
if colorSupport and
|
|
20
|
+
if colorSupport and platform.system == "Windows":
|
|
21
21
|
try:
|
|
22
22
|
# https://stackoverflow.com/questions/36760127/...
|
|
23
23
|
# how-to-use-the-new-support-for-ansi-escape-sequences-in-the-windows-10-console
|
|
@@ -24,25 +24,22 @@ def is_elementXYZ() -> bool:
|
|
|
24
24
|
return get_Experiment().is_elementXYZ
|
|
25
25
|
|
|
26
26
|
# 物实坐标系x, y, z单位1
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
_X_UNIT: float = 0.16
|
|
28
|
+
_Y_UNIT: float = 0.08
|
|
29
|
+
_Z_UNIT: float = 0.1
|
|
30
30
|
# big_element坐标修正
|
|
31
|
-
|
|
31
|
+
_Y_AMEND = 0.045
|
|
32
32
|
|
|
33
|
-
# 元件坐标系原点
|
|
34
|
-
_xOrigin, _yOrigin, _zOrigin = 0, 0, 0
|
|
35
|
-
|
|
36
|
-
### end define ###
|
|
37
|
-
|
|
38
|
-
# 将元件坐标系转换为物实支持的坐标系
|
|
39
33
|
def xyzTranslate(x: numType, y: numType, z: numType, is_bigElement: bool = False):
|
|
34
|
+
''' 将元件坐标系转换为物实支持的坐标系 '''
|
|
40
35
|
if get_Experiment().ExperimentType != experimentType.Circuit:
|
|
41
36
|
raise errors.ExperimentTypeError
|
|
42
37
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
_xOrigin, _yOrigin, _zOrigin = get_OriginPosition()
|
|
39
|
+
|
|
40
|
+
x *= _X_UNIT
|
|
41
|
+
y *= _Y_UNIT
|
|
42
|
+
z *= _Z_UNIT
|
|
46
43
|
# 修改元件坐标系原点
|
|
47
44
|
x += _xOrigin
|
|
48
45
|
y += _yOrigin
|
|
@@ -51,21 +48,23 @@ def xyzTranslate(x: numType, y: numType, z: numType, is_bigElement: bool = False
|
|
|
51
48
|
x, y, z = amend_big_Element(x, y, z)
|
|
52
49
|
return x, y, z
|
|
53
50
|
|
|
54
|
-
# 将物实支持的坐标系转换为元件坐标系
|
|
55
51
|
def translateXYZ(x: numType, y: numType, z: numType, is_bigElement: bool = False):
|
|
52
|
+
''' 将物实支持的坐标系转换为元件坐标系 '''
|
|
56
53
|
if get_Experiment().ExperimentType != experimentType.Circuit:
|
|
57
54
|
raise errors.ExperimentTypeError
|
|
58
55
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
_xOrigin, _yOrigin, _zOrigin = get_OriginPosition()
|
|
57
|
+
|
|
58
|
+
x /= _X_UNIT
|
|
59
|
+
y /= _Y_UNIT
|
|
60
|
+
z /= _Z_UNIT
|
|
62
61
|
# 修改元件坐标系原点
|
|
63
62
|
x -= _xOrigin
|
|
64
63
|
y -= _yOrigin
|
|
65
64
|
z -= _zOrigin
|
|
66
65
|
# 修改大体积逻辑电路原件的坐标
|
|
67
66
|
if is_bigElement:
|
|
68
|
-
y -=
|
|
67
|
+
y -= _Y_AMEND
|
|
69
68
|
return x, y, z
|
|
70
69
|
|
|
71
70
|
# 设置元件坐标系原点O,输入值为物实坐标系
|
|
@@ -81,11 +80,11 @@ def set_O(x: numType, y: numType, z: numType) -> None:
|
|
|
81
80
|
|
|
82
81
|
# 修正bigElement的坐标
|
|
83
82
|
def amend_big_Element(x: numType, y: numType, z: numType):
|
|
84
|
-
return x, y +
|
|
83
|
+
return x, y + _Y_AMEND, z
|
|
85
84
|
|
|
86
85
|
# 获取坐标原点
|
|
87
86
|
def get_OriginPosition() -> position:
|
|
88
|
-
return
|
|
87
|
+
return get_Experiment().elementXYZ_origin_position
|
|
89
88
|
|
|
90
89
|
# 输入"x" 返回_xUnit
|
|
91
90
|
# 输入"y", "z" 返回_yUnit, _zUnit
|
|
@@ -94,9 +93,9 @@ def get_xyzUnit(*args):
|
|
|
94
93
|
raise TypeError
|
|
95
94
|
|
|
96
95
|
index = {
|
|
97
|
-
"x":
|
|
98
|
-
"y":
|
|
99
|
-
"z":
|
|
96
|
+
"x": _X_UNIT,
|
|
97
|
+
"y": _Y_UNIT,
|
|
98
|
+
"z": _Z_UNIT
|
|
100
99
|
}
|
|
101
100
|
if len(args) == 1:
|
|
102
101
|
return index[args[0]]
|
|
@@ -2,19 +2,18 @@
|
|
|
2
2
|
import inspect
|
|
3
3
|
|
|
4
4
|
from physicsLab import errors
|
|
5
|
+
from physicsLab import _tools
|
|
5
6
|
from physicsLab.circuit import wire
|
|
7
|
+
from physicsLab.elementBase import ElementBase
|
|
6
8
|
import physicsLab.circuit.elementXYZ as _elementXYZ
|
|
7
9
|
|
|
8
10
|
from physicsLab.experimentType import experimentType
|
|
9
|
-
from physicsLab.
|
|
11
|
+
from physicsLab.typehint import Optional, Self, numType
|
|
12
|
+
from physicsLab._tools import roundData, randString
|
|
10
13
|
from physicsLab.experiment import Experiment, stack_Experiment
|
|
11
|
-
from physicsLab.typehint import Optional, NoReturn, Self, numType
|
|
12
14
|
|
|
13
15
|
# electricity class's metaClass
|
|
14
16
|
class CircuitMeta(type):
|
|
15
|
-
# element index
|
|
16
|
-
__index = 1
|
|
17
|
-
|
|
18
17
|
def __call__(cls,
|
|
19
18
|
x: numType = 0,
|
|
20
19
|
y: numType = 0,
|
|
@@ -30,6 +29,7 @@ class CircuitMeta(type):
|
|
|
30
29
|
):
|
|
31
30
|
raise TypeError('illegal argument')
|
|
32
31
|
_Expe: Experiment = stack_Experiment.top()
|
|
32
|
+
self.experiment = _Expe
|
|
33
33
|
|
|
34
34
|
if _Expe.ExperimentType != experimentType.Circuit:
|
|
35
35
|
raise errors.ExperimentTypeError
|
|
@@ -39,9 +39,9 @@ class CircuitMeta(type):
|
|
|
39
39
|
cls.is_bigElement = property(lambda self: False) # 2体积元件
|
|
40
40
|
|
|
41
41
|
x, y, z = roundData(x, y, z) # type: ignore -> result type: tuple
|
|
42
|
-
self._position = position(x, y, z) # type: ignore -> define _arguments in metaclass
|
|
42
|
+
self._position = _tools.position(x, y, z) # type: ignore -> define _arguments in metaclass
|
|
43
43
|
# 元件坐标系
|
|
44
|
-
if elementXYZ is True or
|
|
44
|
+
if elementXYZ is True or _elementXYZ.is_elementXYZ() is True and elementXYZ is None:
|
|
45
45
|
x, y, z = _elementXYZ.xyzTranslate(x, y, z)
|
|
46
46
|
self.is_elementXYZ = True
|
|
47
47
|
|
|
@@ -50,7 +50,7 @@ class CircuitMeta(type):
|
|
|
50
50
|
if self.is_elementXYZ and self.is_bigElement:
|
|
51
51
|
x, y, z = _elementXYZ.amend_big_Element(x, y, z)
|
|
52
52
|
|
|
53
|
-
self._arguments["Identifier"] =
|
|
53
|
+
self._arguments["Identifier"] = randString(32)
|
|
54
54
|
# x, z, y 物实采用欧拉坐标系
|
|
55
55
|
self._arguments["Position"] = f"{x},{z},{y}"
|
|
56
56
|
|
|
@@ -60,18 +60,14 @@ class CircuitMeta(type):
|
|
|
60
60
|
else:
|
|
61
61
|
_Expe.elements_Position[self._position] = [self]
|
|
62
62
|
self.set_Rotation()
|
|
63
|
-
|
|
64
|
-
self._index = CircuitMeta.__index
|
|
63
|
+
|
|
65
64
|
_Expe.Elements.append(self)
|
|
66
|
-
# 元件index索引加1
|
|
67
|
-
CircuitMeta.__index += 1
|
|
68
|
-
return self
|
|
69
65
|
|
|
66
|
+
return self
|
|
70
67
|
|
|
71
68
|
# 所有电学元件的父类
|
|
72
|
-
class CircuitBase(metaclass=CircuitMeta):
|
|
69
|
+
class CircuitBase(ElementBase, metaclass=CircuitMeta):
|
|
73
70
|
def __repr__(self) -> str:
|
|
74
|
-
#TODO Simple_Instrument 的__repr__方法参数更多
|
|
75
71
|
return f"{self.__class__.__name__}" \
|
|
76
72
|
f"({self._position.x}, {self._position.y}, {self._position.z}, {self.is_elementXYZ})"
|
|
77
73
|
|
|
@@ -82,7 +78,7 @@ class CircuitBase(metaclass=CircuitMeta):
|
|
|
82
78
|
isinstance(yRotation, (int, float)) and
|
|
83
79
|
isinstance(zRotation, (int, float))
|
|
84
80
|
):
|
|
85
|
-
raise
|
|
81
|
+
raise TypeError
|
|
86
82
|
|
|
87
83
|
self._arguments["Rotation"] = f"{roundData(xRotation)},{roundData(zRotation)},{roundData(yRotation)}"
|
|
88
84
|
return self
|
|
@@ -90,26 +86,31 @@ class CircuitBase(metaclass=CircuitMeta):
|
|
|
90
86
|
# 重新设置元件的坐标
|
|
91
87
|
def set_Position(self, x: numType, y: numType, z: numType, elementXYZ: Optional[bool] = None) -> Self:
|
|
92
88
|
if not (isinstance(x, (int, float)) and isinstance(y, (int, float)) and isinstance(z, (int, float))):
|
|
93
|
-
raise
|
|
89
|
+
raise TypeError
|
|
94
90
|
x, y, z = roundData(x, y, z) # type: ignore -> result type: tuple
|
|
95
91
|
|
|
92
|
+
self._position = _tools.position(x, y, z)
|
|
93
|
+
|
|
96
94
|
#元件坐标系
|
|
97
|
-
if elementXYZ is True or
|
|
98
|
-
x, y, z = _elementXYZ.xyzTranslate(x, y, z)
|
|
95
|
+
if elementXYZ is True or _elementXYZ.is_elementXYZ() is True and elementXYZ is None:
|
|
96
|
+
x, y, z = _elementXYZ.xyzTranslate(x, y, z, self.is_bigElement)
|
|
99
97
|
self.is_elementXYZ = True
|
|
98
|
+
# if self.is_bigElement:
|
|
99
|
+
# x, y, z = _elementXYZ.amend_big_Element(x, y, z)
|
|
100
|
+
|
|
101
|
+
for _, self_list in stack_Experiment.top().elements_Position.items():
|
|
102
|
+
if self in self_list:
|
|
103
|
+
self_list.remove(self)
|
|
100
104
|
|
|
101
|
-
del stack_Experiment.top().elements_Position[self._position]
|
|
102
|
-
self._position = position(x, y, z) # type: ignore -> define _arguments in metaclass
|
|
103
105
|
self._arguments['Position'] = f"{x},{z},{y}" # type: ignore -> define _arguments in metaclass
|
|
104
|
-
stack_Experiment.top().elements_Position[self._position] = self
|
|
105
|
-
return self
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
107
|
+
_Expe = stack_Experiment.top()
|
|
108
|
+
if self._position in _Expe.elements_Position.keys():
|
|
109
|
+
_Expe.elements_Position[self._position].append(self)
|
|
110
|
+
else:
|
|
111
|
+
_Expe.elements_Position[self._position] = [self]
|
|
112
|
+
|
|
113
|
+
return self
|
|
113
114
|
|
|
114
115
|
# 获取原件的坐标
|
|
115
116
|
def get_Position(self) -> tuple:
|
|
@@ -117,7 +118,7 @@ class CircuitBase(metaclass=CircuitMeta):
|
|
|
117
118
|
|
|
118
119
|
# 获取元件的index(每创建一个元件,index就加1)
|
|
119
120
|
def get_Index(self) -> int:
|
|
120
|
-
return self.
|
|
121
|
+
return self.experiment.Elements.index(self) + 1
|
|
121
122
|
|
|
122
123
|
# 获取子类的类型(也就是ModelID)
|
|
123
124
|
@property
|
|
@@ -9,7 +9,7 @@ from ._elementBase import CircuitBase, TwoPinMixIn
|
|
|
9
9
|
# 开关基类
|
|
10
10
|
class _switch_Base(CircuitBase):
|
|
11
11
|
def __init__(self, x: numType = 0, y: numType = 0, z: numType = 0, elementXYZ: Optional[bool] = None):
|
|
12
|
-
self._arguments = {"ModelID":
|
|
12
|
+
self._arguments = {"ModelID": Generate, "Identifier": Generate, "IsBroken": False,
|
|
13
13
|
"IsLocked": False, "Properties": {"开关": 0, "锁定": 1.0},
|
|
14
14
|
"Statistics": {}, "Position": Generate,
|
|
15
15
|
"Rotation": Generate, "DiagramCached": False,
|
|
@@ -26,6 +26,14 @@ class Simple_Switch(_switch_Base, TwoPinMixIn):
|
|
|
26
26
|
super(Simple_Switch, self).__init__(x, y, z, elementXYZ)
|
|
27
27
|
self._arguments["ModelID"] = "Simple Switch"
|
|
28
28
|
|
|
29
|
+
def __repr__(self) -> str:
|
|
30
|
+
res = f"Simple_Switch({self._position.x}, {self._position.y}, {self._position.z}, " \
|
|
31
|
+
f"elementXYZ={self.is_elementXYZ})"
|
|
32
|
+
|
|
33
|
+
if self._arguments["Properties"]["开关"] == 1:
|
|
34
|
+
res += ".turn_on_switch()"
|
|
35
|
+
return res
|
|
36
|
+
|
|
29
37
|
# 闭合开关
|
|
30
38
|
def turn_on_switch(self) -> Self:
|
|
31
39
|
self._arguments["Properties"]["开关"] = 1
|
|
@@ -37,6 +45,16 @@ class SPDT_Switch(_switch_Base):
|
|
|
37
45
|
super(SPDT_Switch, self).__init__(x, y, z, elementXYZ)
|
|
38
46
|
self._arguments["ModelID"] = "SPDT Switch"
|
|
39
47
|
|
|
48
|
+
def __repr__(self) -> str:
|
|
49
|
+
res = f"SPDT_Switch({self._position.x}, {self._position.y}, {self._position.z}, " \
|
|
50
|
+
f"elementXYZ={self.is_elementXYZ})"
|
|
51
|
+
|
|
52
|
+
if self._arguments["Properties"]["开关"] == 1:
|
|
53
|
+
res += ".left_turn_on_switch()"
|
|
54
|
+
elif self._arguments["Properties"]["开关"] == 2:
|
|
55
|
+
res += ".right_turn_on_switch()"
|
|
56
|
+
return res
|
|
57
|
+
|
|
40
58
|
# 向左闭合开关
|
|
41
59
|
def left_turn_on_switch(self) -> Self:
|
|
42
60
|
self._arguments["Properties"]["开关"] = 1
|
|
@@ -65,6 +83,16 @@ class DPDT_Switch(_switch_Base):
|
|
|
65
83
|
super(DPDT_Switch, self).__init__(x, y, z, elementXYZ)
|
|
66
84
|
self._arguments["ModelID"] = "DPDT Switch"
|
|
67
85
|
|
|
86
|
+
def __repr__(self) -> str:
|
|
87
|
+
res = f"DPDT_Switch({self._position.x}, {self._position.y}, {self._position.z}, " \
|
|
88
|
+
f"elementXYZ={self.is_elementXYZ})"
|
|
89
|
+
|
|
90
|
+
if self._arguments["Properties"]["开关"] == 1:
|
|
91
|
+
res += ".left_turn_on_switch()"
|
|
92
|
+
elif self._arguments["Properties"]["开关"] == 2:
|
|
93
|
+
res += ".right_turn_on_switch()"
|
|
94
|
+
return res
|
|
95
|
+
|
|
68
96
|
# 向左闭合开关
|
|
69
97
|
def left_turn_on_switch(self) -> Self:
|
|
70
98
|
self._arguments["Properties"]["开关"] = 1
|
|
@@ -119,6 +147,14 @@ class Air_Switch(TwoPinMixIn):
|
|
|
119
147
|
"Position": Generate, "Rotation": Generate, "DiagramCached": False,
|
|
120
148
|
"DiagramPosition": {"X": 0, "Y": 0, "Magnitude": 0.0}, "DiagramRotation": 0}
|
|
121
149
|
|
|
150
|
+
def __repr__(self) -> str:
|
|
151
|
+
res = f"Air_Switch({self._position.x}, {self._position.y}, {self._position.z}, " \
|
|
152
|
+
f"elementXYZ={self.is_elementXYZ})"
|
|
153
|
+
|
|
154
|
+
if self._arguments["Properties"]["开关"] == 1:
|
|
155
|
+
res += ".turn_on_switch()"
|
|
156
|
+
return res
|
|
157
|
+
|
|
122
158
|
# 断开开关
|
|
123
159
|
def turn_off_switch(self) -> Self:
|
|
124
160
|
self._arguments["Properties"]["开关"] = 0
|
|
@@ -45,9 +45,17 @@ class Logic_Input(_logicBase):
|
|
|
45
45
|
"DiagramPosition": {"X": 0, "Y": 0, "Magnitude": 0.0},
|
|
46
46
|
"DiagramRotation": 0}
|
|
47
47
|
|
|
48
|
+
def __repr__(self) -> str:
|
|
49
|
+
res = f"Logic_Input({self._position.x}, {self._position.y}, {self._position.z}, " \
|
|
50
|
+
f"elementXYZ={self.is_elementXYZ})"
|
|
51
|
+
|
|
52
|
+
if self._arguments["Properties"]["开关"] == 1.0:
|
|
53
|
+
res += ".set_highLevel()"
|
|
54
|
+
return res
|
|
55
|
+
|
|
48
56
|
# 将逻辑输入的状态设置为1
|
|
49
57
|
def set_highLevel(self) -> "Logic_Input":
|
|
50
|
-
self._arguments["Properties"][
|
|
58
|
+
self._arguments["Properties"]["开关"] = 1.0
|
|
51
59
|
return self
|
|
52
60
|
|
|
53
61
|
@property
|
|
@@ -405,6 +413,14 @@ class eight_bit_Input(_logicBase):
|
|
|
405
413
|
"Position": Generate, "Rotation": Generate, "DiagramCached": False,
|
|
406
414
|
"DiagramPosition": {"X": 0, "Y": 0, "Magnitude": 0.0}, "DiagramRotation": 0}
|
|
407
415
|
|
|
416
|
+
def __repr__(self) -> str:
|
|
417
|
+
res = f"eight_bit_Input({self._position.x}, {self._position.y}, {self._position.z}, " \
|
|
418
|
+
f"elementXYZ={self.is_elementXYZ})"
|
|
419
|
+
|
|
420
|
+
if self._arguments["Properties"]["十进制"] != 0:
|
|
421
|
+
res += f".set_num({self._arguments['Properties']['十进制']})"
|
|
422
|
+
return res
|
|
423
|
+
|
|
408
424
|
def set_num(self, num : int):
|
|
409
425
|
if 0 <= num <= 255:
|
|
410
426
|
self._arguments["Properties"]["十进制"] = num
|
|
@@ -205,7 +205,6 @@ class Simple_Instrument(TwoPinMixIn):
|
|
|
205
205
|
rated_oltage: numType = 3.0, # 额定电压
|
|
206
206
|
is_ideal_model: bool = False, # 是否为理想模式
|
|
207
207
|
is_single: bool = True, # 简单乐器是否只响一次
|
|
208
|
-
other_chord_notes: list = [], # 其他和弦音
|
|
209
208
|
) -> None:
|
|
210
209
|
if not (
|
|
211
210
|
(isinstance(instrument, int) and 0 <= instrument <= 128) and
|
|
@@ -228,8 +227,6 @@ class Simple_Instrument(TwoPinMixIn):
|
|
|
228
227
|
|
|
229
228
|
self.set_Tonality(pitch)
|
|
230
229
|
self.notes: List[int] = [self._arguments["Properties"]["音高"]] # 仅用于记录self已有的音符
|
|
231
|
-
for a_note in other_chord_notes:
|
|
232
|
-
self.add_note(a_note)
|
|
233
230
|
|
|
234
231
|
@property
|
|
235
232
|
def i(self) -> Pin:
|
|
@@ -246,8 +243,8 @@ class Simple_Instrument(TwoPinMixIn):
|
|
|
246
243
|
f"velocity={self._arguments['Properties']['音量']}, " \
|
|
247
244
|
f"rated_oltage={self._arguments['Properties']['额定电压']}, " \
|
|
248
245
|
f"is_ideal_model={self._arguments['Properties']['理想模式']}, " \
|
|
249
|
-
f"is_single={bool(self._arguments['Properties']['脉冲'])}
|
|
250
|
-
f"
|
|
246
|
+
f"is_single={bool(self._arguments['Properties']['脉冲'])}" \
|
|
247
|
+
f").add_note({str(self.notes)[1:-2]})"
|
|
251
248
|
|
|
252
249
|
# 物实v2.4.7新功能: 简单乐器同时播放多个音符
|
|
253
250
|
def add_note(self, *pitchs: int) -> Self:
|
|
@@ -28,7 +28,7 @@ class Pin:
|
|
|
28
28
|
def export_str(self) -> str:
|
|
29
29
|
pin_name = self._get_pin_name_of_class()
|
|
30
30
|
if pin_name is None:
|
|
31
|
-
raise
|
|
31
|
+
raise errors.ExperimentError("Pin is not belong to any element")
|
|
32
32
|
return f"e{self.element_self.get_Index()}.{pin_name}"
|
|
33
33
|
|
|
34
34
|
def _get_pin_name_of_class(self) -> Optional[str]:
|
|
@@ -56,6 +56,9 @@ class Wire:
|
|
|
56
56
|
if not isinstance(Source, Pin) or not isinstance(Target, Pin):
|
|
57
57
|
raise TypeError
|
|
58
58
|
|
|
59
|
+
if Source.element_self.experiment != Target.element_self.experiment:
|
|
60
|
+
raise errors.ExperimentError("can't link wire in two experiment")
|
|
61
|
+
|
|
59
62
|
if color in ("black", "blue", "red", "green", "yellow"):
|
|
60
63
|
color = {"black": "黑", "blue": "蓝", "red": "红", "green": "绿", "yellow": "黄"}[color]
|
|
61
64
|
if color not in ('蓝', '绿', '黄', '红', '紫'):
|
|
@@ -68,6 +71,8 @@ class Wire:
|
|
|
68
71
|
def __hash__(self) -> int:
|
|
69
72
|
return hash(
|
|
70
73
|
(self.Source.element_self, self.Source.pinLabel, self.Target.element_self, self.Target.pinLabel)
|
|
74
|
+
) + hash(
|
|
75
|
+
(self.Target.element_self, self.Target.pinLabel, self.Source.element_self, self.Source.pinLabel)
|
|
71
76
|
)
|
|
72
77
|
|
|
73
78
|
def __eq__(self, other: "Wire") -> bool:
|
|
@@ -11,74 +11,84 @@ from physicsLab.circuit.elements._elementBase import CircuitBase
|
|
|
11
11
|
|
|
12
12
|
NnumType = Optional[numType]
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
def crt_Element(name: str,
|
|
15
|
+
x: numType = 0,
|
|
16
|
+
y: numType = 0,
|
|
17
|
+
z: numType = 0,
|
|
18
|
+
elementXYZ: Optional[bool] = None,
|
|
19
|
+
*args,
|
|
20
|
+
**kwargs
|
|
17
21
|
) -> CircuitBase:
|
|
22
|
+
''' 创建原件,本质上仍然是实例化 '''
|
|
18
23
|
if not (isinstance(name, str)
|
|
19
24
|
and isinstance(x, (int, float))
|
|
20
25
|
and isinstance(y, (int, float))
|
|
21
26
|
and isinstance(z, (int, float))
|
|
22
27
|
):
|
|
23
|
-
raise
|
|
28
|
+
raise TypeError
|
|
24
29
|
|
|
25
30
|
name = name.strip()
|
|
26
|
-
# 元件坐标系
|
|
27
|
-
if elementXYZ is True or (_elementXYZ.is_elementXYZ() is True and elementXYZ is None):
|
|
28
|
-
x, y, z = _elementXYZ.xyzTranslate(x, y, z)
|
|
29
31
|
x, y, z = _tools.roundData(x, y, z) # type: ignore
|
|
30
32
|
if (name == '555 Timer'):
|
|
31
|
-
return elements.NE555(x, y, z)
|
|
33
|
+
return elements.NE555(x, y, z, elementXYZ)
|
|
32
34
|
elif (name == '8bit Input'):
|
|
33
|
-
return elements.eight_bit_Input(x, y, z)
|
|
35
|
+
return elements.eight_bit_Input(x, y, z, elementXYZ)
|
|
34
36
|
elif (name == '8bit Display'):
|
|
35
|
-
return elements.eight_bit_Display(x, y, z)
|
|
37
|
+
return elements.eight_bit_Display(x, y, z, elementXYZ)
|
|
36
38
|
else:
|
|
37
|
-
return eval(f"elements.{name.replace(' ', '_').replace('-', '_')}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def get_Element(
|
|
41
|
-
|
|
39
|
+
return eval(f"elements.{name.replace(' ', '_').replace('-', '_')}"
|
|
40
|
+
f"({x}, {y}, {z}, {elementXYZ}, *{args}, **{kwargs})")
|
|
41
|
+
|
|
42
|
+
def get_Element(x: NnumType=None,
|
|
43
|
+
y: NnumType=None,
|
|
44
|
+
z: NnumType=None,
|
|
45
|
+
*,
|
|
46
|
+
index: NnumType=None,
|
|
47
|
+
**kwargs
|
|
42
48
|
) -> Union[CircuitBase, List[CircuitBase]]:
|
|
49
|
+
''' 获取对应坐标的id '''
|
|
43
50
|
# 通过坐标索引元件
|
|
44
|
-
def
|
|
51
|
+
def position_get(x: numType, y: numType, z: numType):
|
|
45
52
|
if not (
|
|
46
53
|
isinstance(x, (int, float))
|
|
47
54
|
and isinstance(y, (int, float))
|
|
48
55
|
and isinstance(z, (int, float))
|
|
49
56
|
):
|
|
50
|
-
raise TypeError
|
|
57
|
+
raise TypeError
|
|
51
58
|
|
|
52
59
|
position = _tools.roundData(x, y, z)
|
|
53
60
|
if position not in _Expe.elements_Position.keys():
|
|
54
|
-
|
|
61
|
+
if "defualt" in kwargs:
|
|
62
|
+
return kwargs["defualt"]
|
|
63
|
+
raise errors.ElementNotFound(f"{position} do not exist")
|
|
55
64
|
|
|
56
65
|
result: list = _Expe.elements_Position[position]
|
|
57
66
|
return result[0] if len(result) == 1 else result
|
|
58
67
|
|
|
59
68
|
# 通过index(元件生成顺序)索引元件
|
|
60
|
-
def
|
|
69
|
+
def index_get(index: int):
|
|
61
70
|
if not isinstance(index, int):
|
|
62
71
|
raise TypeError
|
|
63
72
|
|
|
64
73
|
if 0 < index <= len(_Expe.Elements):
|
|
65
74
|
return _Expe.Elements[index - 1]
|
|
66
75
|
else:
|
|
67
|
-
|
|
76
|
+
if "defualt" in kwargs:
|
|
77
|
+
return kwargs["defualt"]
|
|
78
|
+
raise errors.ElementNotFound
|
|
68
79
|
|
|
69
80
|
_Expe = stack_Experiment.top()
|
|
70
81
|
if None not in [x, y, z]:
|
|
71
|
-
return
|
|
82
|
+
return position_get(x, y, z)
|
|
72
83
|
elif index is not None:
|
|
73
|
-
return
|
|
84
|
+
return index_get(index)
|
|
74
85
|
else:
|
|
75
86
|
raise TypeError
|
|
76
87
|
|
|
77
|
-
# 删除原件
|
|
78
88
|
def del_Element(
|
|
79
89
|
self: CircuitBase # self是物实三大实验支持的所有元件
|
|
80
90
|
) -> None:
|
|
81
|
-
|
|
91
|
+
''' 删除原件 '''
|
|
82
92
|
if not isinstance(self, CircuitBase):
|
|
83
93
|
raise TypeError
|
|
84
94
|
|
|
@@ -60,7 +60,7 @@ class ExperimentTypeError(Exception):
|
|
|
60
60
|
return "The type of experiment does not match the element"
|
|
61
61
|
|
|
62
62
|
# 用于get_Element 获取元件引用失败
|
|
63
|
-
class
|
|
63
|
+
class ElementNotFound(Exception):
|
|
64
64
|
def __str__(self):
|
|
65
65
|
return "Index out of range"
|
|
66
66
|
|
|
@@ -13,7 +13,6 @@ from .savTemplate import Generate
|
|
|
13
13
|
from .experimentType import experimentType
|
|
14
14
|
from .typehint import Union, Optional, List, Dict, numType, Self
|
|
15
15
|
|
|
16
|
-
# 最新被操作的存档
|
|
17
16
|
class stack_Experiment:
|
|
18
17
|
data: List["Experiment"] = []
|
|
19
18
|
|
|
@@ -40,12 +39,12 @@ class stack_Experiment:
|
|
|
40
39
|
cls.data.pop()
|
|
41
40
|
return res
|
|
42
41
|
|
|
43
|
-
# 获取当前正在操作的存档
|
|
44
42
|
def get_Experiment() -> "Experiment":
|
|
43
|
+
''' 获取当前正在操作的存档 '''
|
|
45
44
|
return stack_Experiment.top()
|
|
46
45
|
|
|
47
|
-
# 实验(存档)类
|
|
48
46
|
class Experiment:
|
|
47
|
+
''' 实验(存档)类 '''
|
|
49
48
|
FILE_HEAD = "physicsLabSav"
|
|
50
49
|
if platform.system() == "Windows":
|
|
51
50
|
from getpass import getuser
|
|
@@ -56,20 +55,20 @@ class Experiment:
|
|
|
56
55
|
self.is_open: bool = False
|
|
57
56
|
self.is_crt: bool = False
|
|
58
57
|
self.is_read: bool = False
|
|
59
|
-
self.is_elementXYZ: bool = False
|
|
60
58
|
|
|
61
59
|
self.FileName: Optional[str] = None # 存档的文件名
|
|
62
60
|
self.SavPath: Optional[str] = None # 存档的完整路径, 为 f"{experiment.FILE_HEAD}/{self.FileName}"
|
|
63
61
|
# 通过坐标索引元件
|
|
64
62
|
self.elements_Position: Dict[tuple, list] = {} # key: self._position, value: List[self...]
|
|
65
63
|
# 通过index(元件生成顺序)索引元件
|
|
66
|
-
|
|
64
|
+
from .circuit.elements._elementBase import CircuitBase
|
|
65
|
+
self.Elements: List[CircuitBase] = []
|
|
67
66
|
|
|
68
67
|
if sav_name is not None:
|
|
69
68
|
self.open_or_crt(sav_name)
|
|
70
69
|
|
|
71
|
-
# 通过_arguments["Identifier"]获取元件
|
|
72
70
|
def get_element_from_identifier(self, identifier: str):
|
|
71
|
+
''' 通过_arguments["Identifier"]获取元件 '''
|
|
73
72
|
for element in self.Elements:
|
|
74
73
|
if element._arguments["Identifier"] == identifier:
|
|
75
74
|
return element
|
|
@@ -93,6 +92,9 @@ class Experiment:
|
|
|
93
92
|
self.PlSav["Summary"] = savTemplate.Circuit["Summary"]
|
|
94
93
|
|
|
95
94
|
if self.ExperimentType == experimentType.Circuit:
|
|
95
|
+
self.is_elementXYZ: bool = False
|
|
96
|
+
# 元件坐标系的坐标原点
|
|
97
|
+
self.elementXYZ_origin_position: _tools.position = _tools.position(0, 0, 0)
|
|
96
98
|
self.Wires: set = set() # Set[Wire] # 存档对应的导线
|
|
97
99
|
# 存档对应的StatusSave, 存放实验元件,导线(如果是电学实验的话)
|
|
98
100
|
self.StatusSave: dict = {"SimulationSpeed": 1.0, "Elements": Generate, "Wires": Generate}
|
|
@@ -105,8 +107,8 @@ class Experiment:
|
|
|
105
107
|
elif self.ExperimentType == experimentType.Electromagnetism:
|
|
106
108
|
self.StatusSave: dict = {"SimulationSpeed": 1.0, "Elements": []}
|
|
107
109
|
|
|
108
|
-
# 打开一个指定的sav文件 (支持输入本地实验的名字或sav文件名)
|
|
109
110
|
def open(self, sav_name : str) -> Self:
|
|
111
|
+
''' 打开一个指定的sav文件 (支持输入本地实验的名字或sav文件名) '''
|
|
110
112
|
if self.is_open_or_crt:
|
|
111
113
|
raise errors.experimentExistError
|
|
112
114
|
self.is_open_or_crt = True
|
|
@@ -147,6 +149,9 @@ class Experiment:
|
|
|
147
149
|
self.SavPath = f"{Experiment.FILE_HEAD}/{self.FileName}"
|
|
148
150
|
|
|
149
151
|
if self.ExperimentType == experimentType.Circuit:
|
|
152
|
+
self.is_elementXYZ: bool = False
|
|
153
|
+
# 元件坐标系的坐标原点
|
|
154
|
+
self.elementXYZ_origin_position: _tools.position = _tools.position(0, 0, 0)
|
|
150
155
|
self.PlSav: dict = copy.deepcopy(savTemplate.Circuit)
|
|
151
156
|
self.Wires: set = set() # Set[Wire] # 存档对应的导线
|
|
152
157
|
# 存档对应的StatusSave, 存放实验元件,导线(如果是电学实验的话)
|
|
@@ -181,12 +186,15 @@ class Experiment:
|
|
|
181
186
|
|
|
182
187
|
self.entitle(sav_name)
|
|
183
188
|
|
|
184
|
-
# 创建存档,输入为存档名 sav_name: 存档名; experiment_type: 实验类型; force_crt: 不论实验是否已经存在,强制创建
|
|
185
189
|
def crt(self,
|
|
186
190
|
sav_name: str,
|
|
187
191
|
experiment_type: experimentType = experimentType.Circuit,
|
|
188
192
|
force_crt: bool=False
|
|
189
193
|
) -> Self:
|
|
194
|
+
''' 创建存档,输入为存档名 sav_name: 存档名;
|
|
195
|
+
experiment_type: 实验类型;
|
|
196
|
+
force_crt: 不论实验是否已经存在,强制创建
|
|
197
|
+
'''
|
|
190
198
|
if self.is_open_or_crt:
|
|
191
199
|
raise errors.experimentExistError
|
|
192
200
|
self.is_open_or_crt = True
|
|
@@ -208,11 +216,11 @@ class Experiment:
|
|
|
208
216
|
self.__crt(sav_name, experiment_type)
|
|
209
217
|
return self
|
|
210
218
|
|
|
211
|
-
# 先尝试打开实验, 若失败则创建实验
|
|
212
219
|
def open_or_crt(self,
|
|
213
220
|
savName: str,
|
|
214
221
|
experimentType: experimentType = experimentType.Circuit
|
|
215
222
|
) -> Self:
|
|
223
|
+
''' 先尝试打开实验, 若失败则创建实验 '''
|
|
216
224
|
if self.is_open_or_crt:
|
|
217
225
|
raise errors.experimentExistError
|
|
218
226
|
self.is_open_or_crt = True
|
|
@@ -230,8 +238,8 @@ class Experiment:
|
|
|
230
238
|
self.__crt(savName, experimentType)
|
|
231
239
|
return self
|
|
232
240
|
|
|
233
|
-
# 读取实验已有状态
|
|
234
241
|
def read(self) -> Self:
|
|
242
|
+
''' 读取实验已有状态 '''
|
|
235
243
|
if self.SavPath is None: # 是否已.open()或.crt()
|
|
236
244
|
raise TypeError
|
|
237
245
|
if self.is_read:
|
|
@@ -279,34 +287,47 @@ class Experiment:
|
|
|
279
287
|
obj.set_Rotation(r_x, r_y, r_z)
|
|
280
288
|
obj._arguments['Identifier'] = element['Identifier']
|
|
281
289
|
from .circuit.elements.logicCircuit import Logic_Input, eight_bit_Input
|
|
282
|
-
|
|
290
|
+
from .circuit.elements.basicCircuit import Simple_Switch, SPDT_Switch, DPDT_Switch, Air_Switch
|
|
291
|
+
|
|
283
292
|
if isinstance(obj, Logic_Input) and element['Properties'].get('开关') == 1:
|
|
284
293
|
obj.set_highLevel()
|
|
285
|
-
|
|
294
|
+
|
|
286
295
|
elif isinstance(obj, eight_bit_Input):
|
|
287
296
|
obj._arguments['Statistics'] = element['Statistics']
|
|
288
297
|
obj._arguments['Properties']['十进制'] = element['Properties']['十进制']
|
|
289
298
|
|
|
299
|
+
elif isinstance(obj, Simple_Switch) and element["Properties"]["开关"] == 1:
|
|
300
|
+
obj.turn_on_switch()
|
|
301
|
+
|
|
302
|
+
elif isinstance(obj, Air_Switch) and element["Properties"]["开关"] == 1:
|
|
303
|
+
obj.turn_on_switch()
|
|
304
|
+
|
|
305
|
+
elif isinstance(obj, SPDT_Switch) or isinstance(obj, DPDT_Switch):
|
|
306
|
+
if element["Properties"]["开关"] == 1:
|
|
307
|
+
obj.left_turn_on_switch()
|
|
308
|
+
elif element["Properties"]["开关"] == 2:
|
|
309
|
+
obj.right_turn_on_switch()
|
|
310
|
+
|
|
290
311
|
# 导线
|
|
291
312
|
if self.ExperimentType == experimentType.Circuit:
|
|
292
313
|
from .circuit.wire import Wire, Pin
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
314
|
+
for wire_dict in status_sav['Wires']:
|
|
315
|
+
self.Wires.add(
|
|
316
|
+
Wire(
|
|
317
|
+
Pin(self.get_element_from_identifier(wire_dict["Source"]), wire_dict["SourcePin"]),
|
|
318
|
+
Pin(self.get_element_from_identifier(wire_dict["Target"]), wire_dict["TargetPin"]),
|
|
319
|
+
wire_dict["ColorName"][0] # e.g. "蓝"
|
|
320
|
+
)
|
|
298
321
|
)
|
|
299
|
-
for wire_dict in status_sav['Wires']
|
|
300
|
-
]
|
|
301
322
|
|
|
302
323
|
return self
|
|
303
324
|
|
|
304
|
-
# 以物实存档的格式导出实验
|
|
305
325
|
def write(self,
|
|
306
326
|
extra_filepath: Optional[str] = None,
|
|
307
327
|
ln: bool = False,
|
|
308
328
|
no_pop: bool = False
|
|
309
329
|
) -> Self:
|
|
330
|
+
''' 以物实存档的格式导出实验 '''
|
|
310
331
|
def _format_StatusSave(stringJson: str) -> str:
|
|
311
332
|
stringJson = stringJson.replace( # format element json
|
|
312
333
|
"{\\\"ModelID', '\n {\\\"ModelID"
|
|
@@ -331,17 +352,14 @@ class Experiment:
|
|
|
331
352
|
self.PlSav["Experiment"]["CreationDate"] = int(time.time() * 1000)
|
|
332
353
|
self.PlSav["Summary"]["CreationDate"] = int(time.time() * 1000)
|
|
333
354
|
|
|
334
|
-
self.CameraSave["VisionCenter"] =
|
|
335
|
-
|
|
336
|
-
self.CameraSave["TargetRotation"] = \
|
|
337
|
-
f"{self.TargetRotation.x},{self.TargetRotation.z},{self.TargetRotation.y}"
|
|
355
|
+
self.CameraSave["VisionCenter"] = f"{self.VisionCenter.x},{self.VisionCenter.z},{self.VisionCenter.y}"
|
|
356
|
+
self.CameraSave["TargetRotation"] = f"{self.TargetRotation.x},{self.TargetRotation.z},{self.TargetRotation.y}"
|
|
338
357
|
self.PlSav["Experiment"]["CameraSave"] = json.dumps(self.CameraSave)
|
|
339
358
|
|
|
340
359
|
self.StatusSave["Elements"] = [a_element._arguments for a_element in self.Elements]
|
|
341
360
|
if self.ExperimentType == experimentType.Circuit:
|
|
342
361
|
self.StatusSave["Wires"] = [a_wire.release() for a_wire in self.Wires]
|
|
343
|
-
self.PlSav["Experiment"]["StatusSave"] =
|
|
344
|
-
json.dumps(self.StatusSave, ensure_ascii=False, separators=(',', ': '))
|
|
362
|
+
self.PlSav["Experiment"]["StatusSave"] = json.dumps(self.StatusSave, ensure_ascii=False, separators=(',', ': '))
|
|
345
363
|
|
|
346
364
|
context: str = json.dumps(self.PlSav, indent=2, ensure_ascii=False, separators=(',', ': '))
|
|
347
365
|
if ln:
|
|
@@ -375,8 +393,8 @@ class Experiment:
|
|
|
375
393
|
|
|
376
394
|
return self
|
|
377
395
|
|
|
378
|
-
# 删除存档
|
|
379
396
|
def delete(self, warning_status: Optional[bool]=None) -> None:
|
|
397
|
+
''' 删除存档 '''
|
|
380
398
|
if self.SavPath is None:
|
|
381
399
|
raise TypeError
|
|
382
400
|
|
|
@@ -399,12 +417,12 @@ class Experiment:
|
|
|
399
417
|
|
|
400
418
|
stack_Experiment.pop()
|
|
401
419
|
|
|
402
|
-
# 退出实验而不进行任何操作
|
|
403
420
|
def exit(self) -> None:
|
|
421
|
+
''' 退出实验而不进行任何操作 '''
|
|
404
422
|
stack_Experiment.pop()
|
|
405
423
|
|
|
406
|
-
# 对存档名进行重命名
|
|
407
424
|
def entitle(self, sav_name: str) -> Self:
|
|
425
|
+
''' 对存档名进行重命名 '''
|
|
408
426
|
if not isinstance(sav_name, str):
|
|
409
427
|
raise TypeError
|
|
410
428
|
|
|
@@ -413,8 +431,8 @@ class Experiment:
|
|
|
413
431
|
|
|
414
432
|
return self
|
|
415
433
|
|
|
416
|
-
# 使用notepad打开改存档
|
|
417
434
|
def show(self) -> Self:
|
|
435
|
+
''' 使用notepad打开改存档 '''
|
|
418
436
|
if self.SavPath is None:
|
|
419
437
|
raise TypeError
|
|
420
438
|
|
|
@@ -424,15 +442,15 @@ class Experiment:
|
|
|
424
442
|
os.popen(f'notepad {self.SavPath}')
|
|
425
443
|
return self
|
|
426
444
|
|
|
427
|
-
# 生成与发布实验有关的存档内容
|
|
428
445
|
def publish(self, title: Optional[str] = None, introduction: Optional[str] = None) -> Self:
|
|
429
|
-
|
|
446
|
+
''' 生成与发布实验有关的存档内容 '''
|
|
430
447
|
def introduce_Experiment(introduction: Union[str, None]) -> None:
|
|
448
|
+
''' 发布实验时输入实验介绍 '''
|
|
431
449
|
if introduction is not None:
|
|
432
450
|
self.PlSav['Summary']['Description'] = introduction.split('\n')
|
|
433
451
|
|
|
434
|
-
# 发布实验时输入实验标题
|
|
435
452
|
def name_Experiment(title: Union[str, None]) -> None:
|
|
453
|
+
''' 发布实验时输入实验标题 '''
|
|
436
454
|
if title is not None:
|
|
437
455
|
self.PlSav['Summary']['Subject'] = title
|
|
438
456
|
|
|
@@ -441,10 +459,6 @@ class Experiment:
|
|
|
441
459
|
|
|
442
460
|
return self
|
|
443
461
|
|
|
444
|
-
# 设置实验者的视角
|
|
445
|
-
# x, y, z : 实验者观察的坐标
|
|
446
|
-
# distance: 实验者到(x, y, z)的距离
|
|
447
|
-
# rotation: 实验者观察的角度
|
|
448
462
|
def observe(self,
|
|
449
463
|
x: Optional[numType] = None,
|
|
450
464
|
y: Optional[numType] = None,
|
|
@@ -454,6 +468,11 @@ class Experiment:
|
|
|
454
468
|
rotation_y: Optional[numType] = None,
|
|
455
469
|
rotation_z: Optional[numType] = None
|
|
456
470
|
) -> Self:
|
|
471
|
+
''' 设置实验者的视角
|
|
472
|
+
x, y, z : 实验者观察的坐标
|
|
473
|
+
distance: 实验者到(x, y, z)的距离
|
|
474
|
+
rotation: 实验者观察的角度
|
|
475
|
+
'''
|
|
457
476
|
if self.SavPath is None:
|
|
458
477
|
raise TypeError
|
|
459
478
|
|
|
@@ -493,16 +512,16 @@ class Experiment:
|
|
|
493
512
|
|
|
494
513
|
return self
|
|
495
514
|
|
|
496
|
-
# 与物实示波器图表有关的支持
|
|
497
515
|
def graph(self) -> Self:
|
|
516
|
+
''' 与物实示波器图表有关的支持 '''
|
|
498
517
|
if self.SavPath is None:
|
|
499
518
|
raise TypeError
|
|
500
519
|
|
|
501
520
|
pass
|
|
502
521
|
return self
|
|
503
522
|
|
|
504
|
-
# 以physicsLab代码的形式导出实验
|
|
505
523
|
def export(self, output_path: str = "temp.pl.py", sav_name: str = "temp") -> Self:
|
|
524
|
+
''' 以physicsLab代码的形式导出实验 '''
|
|
506
525
|
if self.SavPath is None:
|
|
507
526
|
raise TypeError
|
|
508
527
|
|
|
@@ -519,8 +538,56 @@ class Experiment:
|
|
|
519
538
|
|
|
520
539
|
return self
|
|
521
540
|
|
|
522
|
-
|
|
541
|
+
def merge(self,
|
|
542
|
+
other: "Experiment",
|
|
543
|
+
x: numType = 0,
|
|
544
|
+
y: numType = 0,
|
|
545
|
+
z: numType = 0,
|
|
546
|
+
elementXYZ: Optional[bool] = None
|
|
547
|
+
) -> Self:
|
|
548
|
+
''' 合并另一实验
|
|
549
|
+
x, y, z, elementXYZ为重新设置要合并的实验的坐标系原点在self的坐标系的位置
|
|
550
|
+
'''
|
|
551
|
+
if self.SavPath is None:
|
|
552
|
+
raise TypeError
|
|
553
|
+
if other.SavPath is None:
|
|
554
|
+
raise TypeError
|
|
555
|
+
|
|
556
|
+
if self is other:
|
|
557
|
+
return self
|
|
558
|
+
|
|
559
|
+
identifier_to_element: dict = {}
|
|
560
|
+
|
|
561
|
+
for a_element in other.Elements:
|
|
562
|
+
a_element = copy.deepcopy(a_element, memo={id(a_element.experiment): self})
|
|
563
|
+
e_x, e_y, e_z = a_element.get_Position()
|
|
564
|
+
if self.ExperimentType == experimentType.Circuit:
|
|
565
|
+
from .circuit.elementXYZ import xyzTranslate, translateXYZ
|
|
566
|
+
if elementXYZ and not a_element.is_elementXYZ:
|
|
567
|
+
e_x, e_y, e_z = translateXYZ(e_x, e_y, e_z, a_element.is_bigElement)
|
|
568
|
+
elif not elementXYZ and a_element.is_elementXYZ:
|
|
569
|
+
e_x, e_y, e_z = xyzTranslate(e_x, e_y, e_z, a_element.is_bigElement)
|
|
570
|
+
a_element.set_Position(e_x + x, e_y + y, e_z + z, elementXYZ)
|
|
571
|
+
# set_Position已处理与elements_Position有关的操作
|
|
572
|
+
self.Elements.append(a_element)
|
|
573
|
+
|
|
574
|
+
identifier_to_element[a_element._arguments["Identifier"]] = a_element
|
|
575
|
+
|
|
576
|
+
if self.ExperimentType == experimentType.Circuit and other.ExperimentType == experimentType.Circuit:
|
|
577
|
+
for a_wire in other.Wires:
|
|
578
|
+
a_wire = copy.deepcopy(
|
|
579
|
+
a_wire, memo={
|
|
580
|
+
id(a_wire.Source.element_self):
|
|
581
|
+
identifier_to_element[a_wire.Source.element_self._arguments["Identifier"]],
|
|
582
|
+
id(a_wire.Target.element_self):
|
|
583
|
+
identifier_to_element[a_wire.Target.element_self._arguments["Identifier"]],
|
|
584
|
+
})
|
|
585
|
+
self.Wires.add(a_wire)
|
|
586
|
+
|
|
587
|
+
return self
|
|
588
|
+
|
|
523
589
|
class experiment:
|
|
590
|
+
''' 仅提供通过with操作存档 '''
|
|
524
591
|
def __init__(self,
|
|
525
592
|
sav_name: str, # 实验名(非存档文件名)
|
|
526
593
|
read: bool = False, # 是否读取存档原有状态
|
|
@@ -555,7 +622,6 @@ class experiment:
|
|
|
555
622
|
self.force_crt = force_crt
|
|
556
623
|
self.is_exit = is_exit
|
|
557
624
|
|
|
558
|
-
# 上下文管理器,搭配with使用
|
|
559
625
|
def __enter__(self) -> Experiment:
|
|
560
626
|
if not self.force_crt:
|
|
561
627
|
self._Experiment: Experiment = Experiment().open_or_crt(self.savName, self.experimentType)
|
|
@@ -587,15 +653,15 @@ class experiment:
|
|
|
587
653
|
self._Experiment.delete()
|
|
588
654
|
return
|
|
589
655
|
|
|
590
|
-
# 获取所有物实存档的文件名
|
|
591
656
|
def getAllSav() -> List[str]:
|
|
657
|
+
''' 获取所有物实存档的文件名 '''
|
|
592
658
|
from os import walk
|
|
593
659
|
savs = [i for i in walk(Experiment.FILE_HEAD)][0]
|
|
594
660
|
savs = savs[savs.__len__() - 1]
|
|
595
661
|
return [aSav for aSav in savs if aSav.endswith('sav')]
|
|
596
662
|
|
|
597
|
-
# 打开一个存档, 返回存档对应的dict
|
|
598
663
|
def _open_sav(sav_name) -> Optional[dict]:
|
|
664
|
+
''' 打开一个存档, 返回存档对应的dict '''
|
|
599
665
|
def encode_sav(path: str, encoding: str) -> Optional[dict]:
|
|
600
666
|
try:
|
|
601
667
|
with open(path, encoding=encoding) as f:
|
|
@@ -621,8 +687,8 @@ def _open_sav(sav_name) -> Optional[dict]:
|
|
|
621
687
|
encoding = chardet.detect(f.read())["encoding"]
|
|
622
688
|
return encode_sav(f"{Experiment.FILE_HEAD}/{sav_name}", encoding)
|
|
623
689
|
|
|
624
|
-
# 检测实验是否存在,输入为存档名,若存在则返回存档对应的文件名,若不存在则返回None
|
|
625
690
|
def search_Experiment(sav_name: str) -> Optional[str]:
|
|
691
|
+
''' 检测实验是否存在, 输入为存档名, 若存在则返回存档对应的文件名, 若不存在则返回None'''
|
|
626
692
|
for aSav in getAllSav():
|
|
627
693
|
sav = _open_sav(aSav)
|
|
628
694
|
if sav["InternalName"] == sav_name:
|
|
@@ -41,7 +41,7 @@ class Union_LogicBase(UnionBase):
|
|
|
41
41
|
element.set_LowLeaveValue(num)
|
|
42
42
|
return self
|
|
43
43
|
|
|
44
|
-
#
|
|
44
|
+
# 只读非门,若没有则创建一个只读非门,若已存在则不会创建新的元件
|
|
45
45
|
class Const_NoGate:
|
|
46
46
|
__singleton: "Const_NoGate" = None # type: ignore
|
|
47
47
|
__singleton_NoGate: No_Gate = None # type: ignore
|
|
@@ -9,7 +9,7 @@ from enum import Enum, unique
|
|
|
9
9
|
from physicsLab import errors
|
|
10
10
|
from physicsLab.circuit import elements
|
|
11
11
|
from physicsLab._tools import roundData
|
|
12
|
-
from physicsLab.
|
|
12
|
+
from physicsLab.lib import crt_Wires, D_WaterLamp
|
|
13
13
|
from physicsLab.typehint import Optional, Union, List, Iterator, Dict, Self, numType
|
|
14
14
|
|
|
15
15
|
def _format_velocity(velocity: float) -> float:
|
|
@@ -48,10 +48,35 @@ class Midi:
|
|
|
48
48
|
program: midi音色
|
|
49
49
|
tempo: 播放速度
|
|
50
50
|
'''
|
|
51
|
+
def read_midopy(plpath: str = "temp.mido.py") -> Self:
|
|
52
|
+
self.midifile = "temp.mid"
|
|
53
|
+
|
|
54
|
+
context = None
|
|
55
|
+
with open(plpath, encoding="utf-8") as f:
|
|
56
|
+
context = f.read()
|
|
57
|
+
|
|
58
|
+
import re
|
|
59
|
+
from mido import MidiFile, MidiTrack, Message, MetaMessage
|
|
60
|
+
# 正则匹配内容: MidiTrack([Message(...), ...])
|
|
61
|
+
re_context = re.search(r"MidiTrack\(\[[^\]]+\]\)", context, re.M)
|
|
62
|
+
if re_context is None:
|
|
63
|
+
raise SyntaxError(f"error context in {plpath}")
|
|
64
|
+
self.messages = eval(re_context.group()) # 用到mido import出的内容
|
|
65
|
+
|
|
66
|
+
return self
|
|
67
|
+
|
|
51
68
|
if not isinstance(midifile, str):
|
|
52
69
|
raise TypeError
|
|
53
70
|
|
|
54
|
-
|
|
71
|
+
from os import path
|
|
72
|
+
if not path.exists(midifile):
|
|
73
|
+
raise FileNotFoundError
|
|
74
|
+
|
|
75
|
+
if midifile.endswith(".mido.py"):
|
|
76
|
+
read_midopy(midifile)
|
|
77
|
+
else:
|
|
78
|
+
self.midifile: str = midifile
|
|
79
|
+
|
|
55
80
|
self.channels: List[int] = [0] * 16
|
|
56
81
|
self.tempo: int = 500_000
|
|
57
82
|
self.messages: mido.MidiTrack = self.__get_midi_messages()
|
|
@@ -95,6 +120,8 @@ class Midi:
|
|
|
95
120
|
|
|
96
121
|
# 使用pygame播放midi
|
|
97
122
|
def sound_by_pygame() -> bool:
|
|
123
|
+
import os
|
|
124
|
+
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1'
|
|
98
125
|
try:
|
|
99
126
|
from pygame import mixer, time
|
|
100
127
|
except ImportError:
|
|
@@ -196,7 +223,7 @@ class Midi:
|
|
|
196
223
|
return res
|
|
197
224
|
|
|
198
225
|
# 转换为physicsLab的piece类
|
|
199
|
-
def
|
|
226
|
+
def to_piece(self, div_time: numType = 100, max_notes: Optional[int] = 800) -> "Piece":
|
|
200
227
|
return Piece(self._get_notes_list(div_time, max_notes))
|
|
201
228
|
|
|
202
229
|
''' *.pl.py文件:
|
|
@@ -210,27 +237,6 @@ class Midi:
|
|
|
210
237
|
为了修改方便, 默认使用 str(mido.MidiTrack) 的方式导出
|
|
211
238
|
而且是个Py文件, 大家想要自己修改也是很方便的
|
|
212
239
|
'''
|
|
213
|
-
def read_midopy(self, plpath: str = "temp.mido.py") -> Self:
|
|
214
|
-
def _read_midopy(plpath):
|
|
215
|
-
context = None
|
|
216
|
-
with open(plpath, encoding="utf-8") as f:
|
|
217
|
-
context = f.read()
|
|
218
|
-
|
|
219
|
-
import re
|
|
220
|
-
from mido import MidiFile, MidiTrack, Message, MetaMessage
|
|
221
|
-
# 正则匹配内容: MidiTrack([Message(...), ...])
|
|
222
|
-
re_context = re.search(r"MidiTrack\(\[[^\]]+\]\)", context, re.M)
|
|
223
|
-
if re_context is None:
|
|
224
|
-
raise SyntaxError(f"error context in {plpath}")
|
|
225
|
-
self.messages = eval(re_context.group()) # 用到mido import出的内容
|
|
226
|
-
|
|
227
|
-
from os import path
|
|
228
|
-
if not path.exists(plpath):
|
|
229
|
-
raise FileNotFoundError
|
|
230
|
-
|
|
231
|
-
self.midifile = "temp.mid"
|
|
232
|
-
_read_midopy(plpath)
|
|
233
|
-
return self
|
|
234
240
|
|
|
235
241
|
# 导出一个 .mido.py 文件
|
|
236
242
|
def write_midopy(self, path: str="temp.mido.py") -> Self:
|
|
@@ -238,7 +244,9 @@ class Midi:
|
|
|
238
244
|
path += ".mido.py"
|
|
239
245
|
|
|
240
246
|
with open(path, "w", encoding="utf-8") as f:
|
|
241
|
-
f.write(f"
|
|
247
|
+
f.write(f"import os\n"
|
|
248
|
+
f"os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1'\n"
|
|
249
|
+
f"from mido import MidiFile, MidiTrack, MetaMessage, Message\n"
|
|
242
250
|
f"mid = MidiFile()\n"
|
|
243
251
|
f"track = {str(self.messages)}\n"
|
|
244
252
|
f"mid.tracks.append(track)\n"
|
|
@@ -533,7 +541,7 @@ class Piece:
|
|
|
533
541
|
return self
|
|
534
542
|
|
|
535
543
|
# 将Piece类转换为Midi
|
|
536
|
-
def
|
|
544
|
+
def to_midi(self, filepath="temp.mid") -> Midi:
|
|
537
545
|
self.write_midi(filepath)
|
|
538
546
|
return Midi(filepath)
|
|
539
547
|
|
|
@@ -585,9 +593,9 @@ class Player:
|
|
|
585
593
|
count_elements_start: int = count_Elements()
|
|
586
594
|
|
|
587
595
|
if not (
|
|
588
|
-
isinstance(x, (int, float))
|
|
589
|
-
isinstance(y, (int, float))
|
|
590
|
-
isinstance(z, (int, float))
|
|
596
|
+
isinstance(x, (int, float)) and
|
|
597
|
+
isinstance(y, (int, float)) and
|
|
598
|
+
isinstance(z, (int, float)) and
|
|
591
599
|
isinstance(musicArray, Piece)
|
|
592
600
|
):
|
|
593
601
|
raise TypeError
|
|
@@ -5,6 +5,7 @@ physicsLab/__init__.py
|
|
|
5
5
|
physicsLab/_colorUtils.py
|
|
6
6
|
physicsLab/_tools.py
|
|
7
7
|
physicsLab/element.py
|
|
8
|
+
physicsLab/elementBase.py
|
|
8
9
|
physicsLab/errors.py
|
|
9
10
|
physicsLab/experiment.py
|
|
10
11
|
physicsLab/experimentType.py
|
|
@@ -28,9 +29,9 @@ physicsLab/circuit/elements/logicCircuit.py
|
|
|
28
29
|
physicsLab/circuit/elements/otherCircuit.py
|
|
29
30
|
physicsLab/electromagnetism/__init__.py
|
|
30
31
|
physicsLab/electromagnetism/elements.py
|
|
32
|
+
physicsLab/lib/__init__.py
|
|
33
|
+
physicsLab/lib/_unionClassHead.py
|
|
34
|
+
physicsLab/lib/logic.py
|
|
35
|
+
physicsLab/lib/wires.py
|
|
31
36
|
physicsLab/music/__init__.py
|
|
32
|
-
physicsLab/music/music.py
|
|
33
|
-
physicsLab/unit/__init__.py
|
|
34
|
-
physicsLab/unit/_unionClassHead.py
|
|
35
|
-
physicsLab/unit/unionLogic.py
|
|
36
|
-
physicsLab/unit/wires.py
|
|
37
|
+
physicsLab/music/music.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|