salabim 25.0.9.post3__py3-none-any.whl → 25.0.9.post4__py3-none-any.whl
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.
- salabim/salabim.py +33 -153
- {salabim-25.0.9.post3.dist-info → salabim-25.0.9.post4.dist-info}/METADATA +1 -1
- {salabim-25.0.9.post3.dist-info → salabim-25.0.9.post4.dist-info}/RECORD +5 -5
- {salabim-25.0.9.post3.dist-info → salabim-25.0.9.post4.dist-info}/WHEEL +0 -0
- {salabim-25.0.9.post3.dist-info → salabim-25.0.9.post4.dist-info}/top_level.txt +0 -0
salabim/salabim.py
CHANGED
@@ -46,22 +46,15 @@ from pathlib import Path
|
|
46
46
|
|
47
47
|
from typing import Any, Union, Iterable, Tuple, List, Callable, TextIO, Dict, Set, Type, Hashable, Optional
|
48
48
|
|
49
|
-
# module = salabim # for PythonInExcel runner
|
50
|
-
|
51
49
|
dataframe = None # to please PyLance
|
52
50
|
|
53
51
|
ColorType = Union[str, Iterable[float]]
|
54
52
|
|
55
53
|
Pythonista = sys.platform == "ios"
|
56
54
|
Windows = sys.platform.startswith("win")
|
57
|
-
PyDroid = sys.platform == "linux" and any("pydroid" in v for v in os.environ.values())
|
58
55
|
PyPy = platform.python_implementation() == "PyPy"
|
59
56
|
Chromebook = "penguin" in platform.uname()
|
60
|
-
|
61
|
-
Xlwings=sys.platform=="emscripten"
|
62
|
-
Xlwings1="xlwings" in sys.modules
|
63
|
-
|
64
|
-
Embedded = False # (not ("__file__" in globals())) or ((sys.platform == "emscripten") and not Xlwings) # Python In Excel or pyoide (AnacondaCode or xlwings lite)
|
57
|
+
Xlwings = "xlwings" in sys.modules
|
65
58
|
|
66
59
|
_color_name_to_ANSI = dict(
|
67
60
|
dark_black="\033[0;30m",
|
@@ -117,71 +110,6 @@ def a_log(*args):
|
|
117
110
|
|
118
111
|
class g: ...
|
119
112
|
|
120
|
-
|
121
|
-
if Embedded:
|
122
|
-
_pie_result = []
|
123
|
-
|
124
|
-
def pie_result():
|
125
|
-
return _pie_result
|
126
|
-
|
127
|
-
class b64_file_handler(io.IOBase):
|
128
|
-
"""
|
129
|
-
special purpose file handler for Python in Excel
|
130
|
-
|
131
|
-
Parameters
|
132
|
-
----------
|
133
|
-
name : str
|
134
|
-
name of file
|
135
|
-
|
136
|
-
result : list
|
137
|
-
b64 information will be added as string(s)
|
138
|
-
|
139
|
-
blocksize : int
|
140
|
-
b64 blocksize (default: 30000)
|
141
|
-
|
142
|
-
mode : str
|
143
|
-
if 't' (default): open for test
|
144
|
-
if 'b': open for binary
|
145
|
-
|
146
|
-
Note
|
147
|
-
----
|
148
|
-
can be used as a file or in a context manager
|
149
|
-
"""
|
150
|
-
|
151
|
-
def __init__(self, name, result, blocksize=30000, mode="t"):
|
152
|
-
self._name = name
|
153
|
-
self._result = result
|
154
|
-
self._blocksize = blocksize
|
155
|
-
if mode not in ("t", "b"):
|
156
|
-
raise ValueError(f"wrong mode ({mode})")
|
157
|
-
self._mode = mode
|
158
|
-
self._buffer = []
|
159
|
-
|
160
|
-
def __enter__(self):
|
161
|
-
return self
|
162
|
-
|
163
|
-
def __exit__(self, *args):
|
164
|
-
self.close()
|
165
|
-
|
166
|
-
def write(self, s):
|
167
|
-
self._buffer.append(s)
|
168
|
-
|
169
|
-
def close(self):
|
170
|
-
if self._mode == "b":
|
171
|
-
b = b"".join(self._buffer)
|
172
|
-
else:
|
173
|
-
b = "".join(self._buffer).encode("utf-8")
|
174
|
-
n = self._blocksize
|
175
|
-
b64 = base64.b64encode(b).decode("utf-8")
|
176
|
-
self._result.append(f"<file name={self._name}>")
|
177
|
-
while b64:
|
178
|
-
b64_n = b64[:n]
|
179
|
-
self._result.append(b64_n)
|
180
|
-
b64 = b64[n:]
|
181
|
-
self._result.append("</file>")
|
182
|
-
super().close()
|
183
|
-
|
184
|
-
|
185
113
|
if Pythonista:
|
186
114
|
try:
|
187
115
|
import scene # type: ignore
|
@@ -193,7 +121,7 @@ if Pythonista:
|
|
193
121
|
inf = float("inf")
|
194
122
|
nan = float("nan")
|
195
123
|
|
196
|
-
if Pythonista or
|
124
|
+
if Pythonista or Xlwings:
|
197
125
|
_yieldless = False
|
198
126
|
else:
|
199
127
|
_yieldless = True
|
@@ -233,11 +161,9 @@ def yieldless(value: bool = None) -> bool:
|
|
233
161
|
if value is not None:
|
234
162
|
if value:
|
235
163
|
if Pythonista:
|
236
|
-
raise ValueError(
|
237
|
-
if Embedded:
|
238
|
-
raise ValueError('yiedless mode is not allowed under Python in Excel or Anaconda Code')
|
164
|
+
raise ValueError("yiedless mode is not allowed under Pythonista")
|
239
165
|
if Xlwings:
|
240
|
-
raise ValueError(
|
166
|
+
raise ValueError("yiedless mode is not allowed under xlwings lite")
|
241
167
|
_yieldless = value
|
242
168
|
return _yieldless
|
243
169
|
|
@@ -7211,8 +7137,8 @@ class Component:
|
|
7211
7137
|
else:
|
7212
7138
|
self.env.print_trace("", "", self.name() + " create data component", self._modetxt())
|
7213
7139
|
else:
|
7214
|
-
_check_overlapping_parameters(self, "__init__", process_name,process=p)
|
7215
|
-
_check_overlapping_parameters(self, "setup", process_name,process=p)
|
7140
|
+
_check_overlapping_parameters(self, "__init__", process_name, process=p)
|
7141
|
+
_check_overlapping_parameters(self, "setup", process_name, process=p)
|
7216
7142
|
|
7217
7143
|
self.env.print_trace("", "", self.name() + " create", self._modetxt())
|
7218
7144
|
|
@@ -7512,7 +7438,7 @@ by adding at the end:
|
|
7512
7438
|
return return_or_print(result, as_str, file)
|
7513
7439
|
|
7514
7440
|
def _push(self, t, priority, urgent, return_value=None, switch=True):
|
7515
|
-
if t!=inf:
|
7441
|
+
if t != inf:
|
7516
7442
|
self.env._seq += 1
|
7517
7443
|
if urgent:
|
7518
7444
|
seq = -self.env._seq
|
@@ -10658,22 +10584,19 @@ class Environment:
|
|
10658
10584
|
self.stopped = False
|
10659
10585
|
self._paused = False
|
10660
10586
|
self.last_s0 = ""
|
10661
|
-
if
|
10587
|
+
if Xlwings:
|
10662
10588
|
if blind_animation is None:
|
10663
|
-
blind_animation=True
|
10589
|
+
blind_animation = True
|
10664
10590
|
if not blind_animation:
|
10665
10591
|
raise ValueError("blind_animation may not be False under xlwings lite")
|
10666
10592
|
else:
|
10667
10593
|
if blind_animation is None:
|
10668
|
-
blind_animation=False
|
10594
|
+
blind_animation = False
|
10669
10595
|
self._blind_animation = blind_animation
|
10670
10596
|
|
10671
10597
|
if self._blind_animation:
|
10672
10598
|
with self.suppress_trace():
|
10673
10599
|
self._blind_video_maker = _BlindVideoMaker(process="", suppress_trace=True)
|
10674
|
-
if PyDroid:
|
10675
|
-
if g.tkinter_loaded == "?":
|
10676
|
-
g.tkinter_loaded = "tkinter" in sys.modules
|
10677
10600
|
|
10678
10601
|
if Pythonista:
|
10679
10602
|
self._width, self._height = ui.get_screen_size()
|
@@ -11532,8 +11455,6 @@ class Environment:
|
|
11532
11455
|
|
11533
11456
|
If no codec is given, MJPG will be used for .avi files, otherwise .mp4v
|
11534
11457
|
|
11535
|
-
Under PyDroid only .avi files are supported.
|
11536
|
-
|
11537
11458
|
video_repeat : int
|
11538
11459
|
number of times animated gif or png should be repeated
|
11539
11460
|
|
@@ -11855,8 +11776,6 @@ class Environment:
|
|
11855
11776
|
self._video_name = self._video_name[:-5] # get rid of codec
|
11856
11777
|
else:
|
11857
11778
|
codec = "MJPG" if extension.lower() == ".avi" else "mp4v"
|
11858
|
-
if PyDroid and extension.lower() != ".avi":
|
11859
|
-
raise ValueError("PyDroid can only produce .avi videos, not " + extension)
|
11860
11779
|
can_video(try_only=False)
|
11861
11780
|
fourcc = cv2.VideoWriter_fourcc(*codec)
|
11862
11781
|
if video_path.is_file():
|
@@ -11960,59 +11879,34 @@ class Environment:
|
|
11960
11879
|
if self._video_pingpong:
|
11961
11880
|
self._images.extend(self._images[::-1])
|
11962
11881
|
if self._video_repeat == 1: # in case of repeat == 1, loop should not be specified (otherwise, it might show twice)
|
11963
|
-
|
11964
|
-
|
11882
|
+
for _ in range(2): # normally runs only once
|
11883
|
+
try:
|
11965
11884
|
self._images[0].save(
|
11966
|
-
|
11885
|
+
self._video_name,
|
11967
11886
|
disposal=2,
|
11968
11887
|
save_all=True,
|
11969
11888
|
append_images=self._images[1:],
|
11970
11889
|
duration=round(1000 / self._real_fps),
|
11971
11890
|
optimize=False,
|
11972
|
-
format="GIF",
|
11973
11891
|
)
|
11974
|
-
|
11975
|
-
|
11976
|
-
|
11977
|
-
self._images[0].save(
|
11978
|
-
self._video_name,
|
11979
|
-
disposal=2,
|
11980
|
-
save_all=True,
|
11981
|
-
append_images=self._images[1:],
|
11982
|
-
duration=round(1000 / self._real_fps),
|
11983
|
-
optimize=False,
|
11984
|
-
)
|
11985
|
-
break
|
11986
|
-
except ValueError: # prevent bug in Python 3.13
|
11987
|
-
self._images = [image.convert("RGB") for image in self._images]
|
11892
|
+
break
|
11893
|
+
except ValueError: # prevent bug in Python 3.13
|
11894
|
+
self._images = [image.convert("RGB") for image in self._images]
|
11988
11895
|
|
11989
11896
|
else:
|
11990
|
-
|
11991
|
-
|
11897
|
+
for _ in range(2): # normally runs only once
|
11898
|
+
try:
|
11992
11899
|
self._images[0].save(
|
11993
|
-
|
11900
|
+
self._video_name,
|
11994
11901
|
disposal=2,
|
11995
11902
|
save_all=True,
|
11996
11903
|
append_images=self._images[1:],
|
11997
11904
|
loop=self._video_repeat,
|
11998
11905
|
duration=round(1000 / self._real_fps),
|
11999
11906
|
optimize=False,
|
12000
|
-
format="GIF",
|
12001
11907
|
)
|
12002
|
-
|
12003
|
-
|
12004
|
-
try:
|
12005
|
-
self._images[0].save(
|
12006
|
-
self._video_name,
|
12007
|
-
disposal=2,
|
12008
|
-
save_all=True,
|
12009
|
-
append_images=self._images[1:],
|
12010
|
-
loop=self._video_repeat,
|
12011
|
-
duration=round(1000 / self._real_fps),
|
12012
|
-
optimize=False,
|
12013
|
-
)
|
12014
|
-
except ValueError: # prevent bug in Python 3.13
|
12015
|
-
self._images = [image.convert("RGB") for image in self._images]
|
11908
|
+
except ValueError: # prevent bug in Python 3.13
|
11909
|
+
self._images = [image.convert("RGB") for image in self._images]
|
12016
11910
|
|
12017
11911
|
del self._images
|
12018
11912
|
elif self._video_out == "png":
|
@@ -13604,13 +13498,8 @@ class Environment:
|
|
13604
13498
|
mode = "RGB"
|
13605
13499
|
else:
|
13606
13500
|
raise ValueError("extension " + extension + " not supported")
|
13607
|
-
|
13608
|
-
|
13609
|
-
format = "jpeg" if extension == ".jpg" else extension[1:]
|
13610
|
-
self._capture_image(mode, video_mode).save(f, format=format)
|
13611
|
-
else:
|
13612
|
-
filename_path.parent.mkdir(parents=True, exist_ok=True)
|
13613
|
-
self._capture_image(mode, video_mode).save(str(filename))
|
13501
|
+
filename_path.parent.mkdir(parents=True, exist_ok=True)
|
13502
|
+
self._capture_image(mode, video_mode).save(str(filename))
|
13614
13503
|
|
13615
13504
|
def modelname_width(self):
|
13616
13505
|
if Environment.cached_modelname_width[0] != self._modelname:
|
@@ -14621,16 +14510,14 @@ class Environment:
|
|
14621
14510
|
note that the header is only printed if trace=True
|
14622
14511
|
"""
|
14623
14512
|
len_s1 = len(self.time_to_str(0))
|
14624
|
-
self.print_trace((len_s1 - 4) * " " + "time", "current component", "action", "information", "
|
14625
|
-
self.print_trace(len_s1 * "-", 20 * "-", 35 * "-", 48 * "-",
|
14513
|
+
self.print_trace((len_s1 - 4) * " " + "time", "current component", "action", "information", "line#")
|
14514
|
+
self.print_trace(len_s1 * "-", 20 * "-", 35 * "-", 48 * "-", 6 * "-")
|
14626
14515
|
for ref in range(len(self._source_files)):
|
14627
14516
|
for fullfilename, iref in self._source_files.items():
|
14628
14517
|
if ref == iref:
|
14629
14518
|
self._print_legend(iref)
|
14630
14519
|
|
14631
14520
|
def _print_legend(self, ref):
|
14632
|
-
if Embedded:
|
14633
|
-
return
|
14634
14521
|
if ref:
|
14635
14522
|
s = "line numbers prefixed by " + chr(ord("A") + ref - 1) + " refer to"
|
14636
14523
|
else:
|
@@ -14647,8 +14534,6 @@ class Environment:
|
|
14647
14534
|
return self.filename_lineno_to_str(frameinfo.filename, frameinfo.lineno)
|
14648
14535
|
|
14649
14536
|
def filename_lineno_to_str(self, filename, lineno):
|
14650
|
-
if Embedded:
|
14651
|
-
return "n/a"
|
14652
14537
|
if Path(filename).name == Path(__file__).name: # internal salabim address
|
14653
14538
|
return "n/a"
|
14654
14539
|
ref = self._source_files.get(filename)
|
@@ -25118,7 +25003,7 @@ def _set_name(name, _nameserialize, object):
|
|
25118
25003
|
object._name = name
|
25119
25004
|
|
25120
25005
|
|
25121
|
-
def _check_overlapping_parameters(obj, method_name0, method_name1,process=None):
|
25006
|
+
def _check_overlapping_parameters(obj, method_name0, method_name1, process=None):
|
25122
25007
|
"""
|
25123
25008
|
this function is a helper to see whether __init__, setup and process parameters overlap
|
25124
25009
|
|
@@ -25136,12 +25021,12 @@ def _check_overlapping_parameters(obj, method_name0, method_name1,process=None):
|
|
25136
25021
|
process: method
|
25137
25022
|
used for process check: should be the process methoc
|
25138
25023
|
"""
|
25139
|
-
method0=getattr(obj, method_name0)
|
25024
|
+
method0 = getattr(obj, method_name0)
|
25140
25025
|
|
25141
25026
|
if process is None:
|
25142
|
-
method1=getattr(obj, method_name1)
|
25027
|
+
method1 = getattr(obj, method_name1)
|
25143
25028
|
else:
|
25144
|
-
method1=process
|
25029
|
+
method1 = process
|
25145
25030
|
|
25146
25031
|
overlapping_parameters = set(inspect.signature(method0).parameters) & set(inspect.signature(method1).parameters)
|
25147
25032
|
if overlapping_parameters:
|
@@ -25152,7 +25037,7 @@ def _check_overlapping_parameters(obj, method_name0, method_name1,process=None):
|
|
25152
25037
|
|
25153
25038
|
@functools.lru_cache()
|
25154
25039
|
def _screen_dimensions():
|
25155
|
-
if
|
25040
|
+
if Xlwings:
|
25156
25041
|
return 1024, 768
|
25157
25042
|
if Pythonista:
|
25158
25043
|
screen_width, screen_height = ui.get_screen_size()
|
@@ -27180,7 +27065,7 @@ def _std_fonts():
|
|
27180
27065
|
|
27181
27066
|
|
27182
27067
|
def fonts():
|
27183
|
-
if
|
27068
|
+
if Xlwings:
|
27184
27069
|
return []
|
27185
27070
|
if not hasattr(fonts, "font_list"):
|
27186
27071
|
fonts.font_list = []
|
@@ -27453,7 +27338,7 @@ def can_animate(try_only: bool = True) -> bool:
|
|
27453
27338
|
except ImportError:
|
27454
27339
|
ImageGrab = None
|
27455
27340
|
|
27456
|
-
if not Pythonista and not
|
27341
|
+
if not Pythonista and not Xlwings:
|
27457
27342
|
from PIL import ImageTk
|
27458
27343
|
except ImportError:
|
27459
27344
|
if try_only:
|
@@ -27462,12 +27347,7 @@ def can_animate(try_only: bool = True) -> bool:
|
|
27462
27347
|
|
27463
27348
|
g.dummy_image = Image.new("RGBA", (1, 1), (0, 0, 0, 0))
|
27464
27349
|
|
27465
|
-
if not Pythonista and not
|
27466
|
-
if PyDroid:
|
27467
|
-
if not g.tkinter_loaded:
|
27468
|
-
if try_only:
|
27469
|
-
return False
|
27470
|
-
raise ImportError("PyDroid animation requires that the main program imports tkinter")
|
27350
|
+
if not Pythonista and not Xlwings:
|
27471
27351
|
try:
|
27472
27352
|
import tkinter
|
27473
27353
|
import tkinter.font
|
@@ -3,8 +3,8 @@ salabim/LICENSE.txt,sha256=eTPlcDJz4G0096Qv-wfMjm1Wxbd4ilDlsYg5rN4HjWQ,1106
|
|
3
3
|
salabim/__init__.py,sha256=r7qPLvlmX0dkZDyjuTo8Jo3ex3sD1L4pmK6K5ib9vyw,56
|
4
4
|
salabim/calibri.ttf,sha256=RWpf8Uo31RfvGGNaSt9-2sXSuN87AVE_NFMRsV3LhBk,1330156
|
5
5
|
salabim/mplus-1m-regular.ttf,sha256=EuFHr90BJjuAn_r5MleJFN-WfkeWJ4tf7DweI5zr8tU,289812
|
6
|
-
salabim/salabim.py,sha256=
|
7
|
-
salabim-25.0.9.
|
8
|
-
salabim-25.0.9.
|
9
|
-
salabim-25.0.9.
|
10
|
-
salabim-25.0.9.
|
6
|
+
salabim/salabim.py,sha256=hL-5b2WhppIVwwldm-O5-pCFOEHRoFt1DhoIhCHf9-w,1121320
|
7
|
+
salabim-25.0.9.post4.dist-info/METADATA,sha256=t2mmahEmxd0puFxYki_G4vcb1k05dVyw9F-IQuNjs2A,3405
|
8
|
+
salabim-25.0.9.post4.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
|
9
|
+
salabim-25.0.9.post4.dist-info/top_level.txt,sha256=UE6zVlbi3F6T5ma1a_5TrojMaF21GYKDt9svvm0U4cQ,8
|
10
|
+
salabim-25.0.9.post4.dist-info/RECORD,,
|
File without changes
|
File without changes
|