physicsLab 2.0.0__tar.gz → 2.0.2__tar.gz

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