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