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.
Files changed (39) hide show
  1. {physicsLab-1.4.5 → physicslab-1.4.7}/PKG-INFO +1 -1
  2. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/__init__.py +24 -4
  3. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/_colorUtils.py +2 -2
  4. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/circuit/elementXYZ.py +22 -23
  5. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/circuit/elements/_elementBase.py +31 -30
  6. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/circuit/elements/basicCircuit.py +37 -1
  7. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/circuit/elements/logicCircuit.py +17 -1
  8. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/circuit/elements/otherCircuit.py +2 -5
  9. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/circuit/wire.py +6 -1
  10. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/element.py +34 -24
  11. physicslab-1.4.7/physicsLab/elementBase.py +2 -0
  12. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/errors.py +1 -1
  13. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/experiment.py +110 -44
  14. {physicsLab-1.4.5/physicsLab/unit → physicslab-1.4.7/physicsLab/lib}/__init__.py +1 -1
  15. physicsLab-1.4.5/physicsLab/unit/unionLogic.py → physicslab-1.4.7/physicsLab/lib/logic.py +1 -1
  16. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/music/music.py +37 -29
  17. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab.egg-info/PKG-INFO +1 -1
  18. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab.egg-info/SOURCES.txt +6 -5
  19. {physicsLab-1.4.5 → physicslab-1.4.7}/setup.py +1 -1
  20. {physicsLab-1.4.5 → physicslab-1.4.7}/LICENSE +0 -0
  21. {physicsLab-1.4.5 → physicslab-1.4.7}/README.md +0 -0
  22. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/_tools.py +0 -0
  23. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/celestial/__init__.py +0 -0
  24. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/celestial/elementsClass.py +0 -0
  25. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/circuit/__init__.py +0 -0
  26. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/circuit/elements/__init__.py +0 -0
  27. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/circuit/elements/artificialCircuit.py +0 -0
  28. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/electromagnetism/__init__.py +0 -0
  29. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/electromagnetism/elements.py +0 -0
  30. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/experimentType.py +0 -0
  31. {physicsLab-1.4.5/physicsLab/unit → physicslab-1.4.7/physicsLab/lib}/_unionClassHead.py +0 -0
  32. {physicsLab-1.4.5/physicsLab/unit → physicslab-1.4.7/physicsLab/lib}/wires.py +0 -0
  33. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/music/__init__.py +0 -0
  34. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/savTemplate.py +0 -0
  35. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab/typehint.py +0 -0
  36. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab.egg-info/dependency_links.txt +0 -0
  37. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab.egg-info/requires.txt +0 -0
  38. {physicsLab-1.4.5 → physicslab-1.4.7}/physicsLab.egg-info/top_level.txt +0 -0
  39. {physicsLab-1.4.5 → physicslab-1.4.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: physicsLab
3
- Version: 1.4.5
3
+ Version: 1.4.7
4
4
  Summary: Python API for Physics-Lab-AR
5
5
  Home-page: https://gitee.com/script2000/physicsLab
6
6
  Author: Goodenough
@@ -17,8 +17,8 @@ from .element import *
17
17
  # `physicsLab`自定义异常类
18
18
  from .errors import *
19
19
  # 模块化电路
20
- from .unit.wires import *
21
- from physicsLab import unit
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", "getElementError", "crtExperimentFailError",
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
- "unit", "music",
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 sys
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 sys.platform == "win32":
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
- _xUnit: numType = 0.16
28
- _yUnit: numType = 0.08
29
- _zUnit: numType = 0.1
27
+ _X_UNIT: float = 0.16
28
+ _Y_UNIT: float = 0.08
29
+ _Z_UNIT: float = 0.1
30
30
  # big_element坐标修正
31
- _yAmend = 0.045
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
- x *= _xUnit
44
- y *= _yUnit
45
- z *= _zUnit
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
- x /= _xUnit
60
- y /= _yUnit
61
- z /= _zUnit
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 -= _yAmend
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 + _yAmend, z
83
+ return x, y + _Y_AMEND, z
85
84
 
86
85
  # 获取坐标原点
87
86
  def get_OriginPosition() -> position:
88
- return position(_xOrigin, _yOrigin, _zOrigin)
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": _xUnit,
98
- "y": _yUnit,
99
- "z": _zUnit
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._tools import roundData, randString, position
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 (_elementXYZ.is_elementXYZ() is True and elementXYZ is None):
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"] = f"pl{CircuitBase.__index}"
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 RuntimeError('illegal argument')
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 RuntimeError('illegal argument')
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 (_elementXYZ.is_elementXYZ() is True and elementXYZ is None):
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
- def format_Position(self) -> tuple:
109
- if not isinstance(self._position, tuple) or self._position.__len__() != 3:
110
- raise RuntimeError("Position must be a tuple of length three but gets some other value")
111
- self._position = position(*roundData(self._position.x, self._position.y, self._position.z))
112
- return self._position
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._index # type: ignore -> define self._index in metaclass
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": "", "Identifier": Generate, "IsBroken": False,
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"][u"开关"] = 1.0
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"other_chord_notes={self.notes})"
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 RuntimeError("Pin is not belong to any element")
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
- def crt_Element(
16
- name: str, x: numType = 0, y: numType = 0, z: numType = 0, elementXYZ: Optional[bool] = None
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 RuntimeError("Wrong parameter type")
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('-', '_')}({x},{y},{z})")
38
-
39
- # 获取对应坐标的self
40
- def get_Element(
41
- x: NnumType=None, y: NnumType=None, z: NnumType=None, *, index: NnumType=None
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 position_Element(x: numType, y: numType, z: numType):
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('illegal argument')
57
+ raise TypeError
51
58
 
52
59
  position = _tools.roundData(x, y, z)
53
60
  if position not in _Expe.elements_Position.keys():
54
- raise errors.ElementNotExistError(f"{position} do not exist")
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 index_Element(index: int):
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
- raise errors.getElementError
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 position_Element(x, y, z)
82
+ return position_get(x, y, z)
72
83
  elif index is not None:
73
- return index_Element(index)
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
 
@@ -0,0 +1,2 @@
1
+ class ElementBase:
2
+ pass
@@ -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 getElementError(Exception):
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
- self.Elements: list = [] # List[CircuitBase]
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
- # 如果obj是逻辑输入
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
- # 如果obj是8位输入器
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
- self.Wires = [
294
- Wire(
295
- Pin(self.get_element_from_identifier(wire_dict["Source"]), wire_dict["SourcePin"]),
296
- Pin(self.get_element_from_identifier(wire_dict["Target"]), wire_dict["TargetPin"]),
297
- wire_dict["ColorName"][0] # e.g. ""
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
- f"{self.VisionCenter.x},{self.VisionCenter.z},{self.VisionCenter.y}"
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
- # 仅供with时使用
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:
@@ -1,4 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
- from .unionLogic import *
2
+ from .logic import *
3
3
  # 模块化电路引脚连接
4
4
  from .wires import *
@@ -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.unit import crt_Wires, D_WaterLamp
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
- self.midifile: str = midifile
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 translate_to_piece(self, div_time: numType = 100, max_notes: Optional[int] = 800) -> "Piece":
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"from mido import MidiFile, MidiTrack, MetaMessage, Message\n"
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 translate_to_midi(self, filepath="temp.mid") -> Midi:
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)) or
589
- isinstance(y, (int, float)) or
590
- isinstance(z, (int, float)) or
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: physicsLab
3
- Version: 1.4.5
3
+ Version: 1.4.7
4
4
  Summary: Python API for Physics-Lab-AR
5
5
  Home-page: https://gitee.com/script2000/physicsLab
6
6
  Author: Goodenough
@@ -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
@@ -3,7 +3,7 @@ import setuptools
3
3
 
4
4
  setuptools.setup(
5
5
  name="physicsLab",
6
- version="1.4.5",
6
+ version="1.4.7",
7
7
  license="MIT",
8
8
  author="Goodenough",
9
9
  author_email="2381642961@qq.com",
File without changes
File without changes
File without changes