salabim 25.0.9.post3__py3-none-any.whl → 25.0.10__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 +141 -174
- {salabim-25.0.9.post3.dist-info → salabim-25.0.10.dist-info}/METADATA +1 -1
- salabim-25.0.10.dist-info/RECORD +10 -0
- {salabim-25.0.9.post3.dist-info → salabim-25.0.10.dist-info}/WHEEL +1 -1
- salabim-25.0.9.post3.dist-info/RECORD +0 -10
- {salabim-25.0.9.post3.dist-info → salabim-25.0.10.dist-info}/top_level.txt +0 -0
salabim/salabim.py
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
#
|
8
8
|
# see www.salabim.org for more information, the documentation and license information
|
9
9
|
|
10
|
-
__version__ = "25.0.
|
10
|
+
__version__ = "25.0.10"
|
11
11
|
import heapq
|
12
12
|
import random
|
13
13
|
import time
|
@@ -43,25 +43,18 @@ import base64
|
|
43
43
|
import zipfile
|
44
44
|
from pathlib import Path
|
45
45
|
|
46
|
-
|
47
46
|
from typing import Any, Union, Iterable, Tuple, List, Callable, TextIO, Dict, Set, Type, Hashable, Optional
|
48
47
|
|
49
|
-
# module = salabim # for PythonInExcel runner
|
50
|
-
|
51
48
|
dataframe = None # to please PyLance
|
52
49
|
|
53
50
|
ColorType = Union[str, Iterable[float]]
|
54
51
|
|
55
52
|
Pythonista = sys.platform == "ios"
|
56
53
|
Windows = sys.platform.startswith("win")
|
57
|
-
|
54
|
+
MacOS = platform.system == "Darwin"
|
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",
|
@@ -118,70 +111,6 @@ def a_log(*args):
|
|
118
111
|
class g: ...
|
119
112
|
|
120
113
|
|
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
114
|
if Pythonista:
|
186
115
|
try:
|
187
116
|
import scene # type: ignore
|
@@ -193,7 +122,7 @@ if Pythonista:
|
|
193
122
|
inf = float("inf")
|
194
123
|
nan = float("nan")
|
195
124
|
|
196
|
-
if Pythonista or
|
125
|
+
if Pythonista or Xlwings:
|
197
126
|
_yieldless = False
|
198
127
|
else:
|
199
128
|
_yieldless = True
|
@@ -233,11 +162,9 @@ def yieldless(value: bool = None) -> bool:
|
|
233
162
|
if value is not None:
|
234
163
|
if value:
|
235
164
|
if Pythonista:
|
236
|
-
raise ValueError(
|
237
|
-
if Embedded:
|
238
|
-
raise ValueError('yiedless mode is not allowed under Python in Excel or Anaconda Code')
|
165
|
+
raise ValueError("yiedless mode is not allowed under Pythonista")
|
239
166
|
if Xlwings:
|
240
|
-
raise ValueError(
|
167
|
+
raise ValueError("yiedless mode is not allowed under xlwings lite")
|
241
168
|
_yieldless = value
|
242
169
|
return _yieldless
|
243
170
|
|
@@ -3736,14 +3663,6 @@ class AnimateMonitor(DynamicClass):
|
|
3736
3663
|
if False, animation monitor is not shown, shown otherwise
|
3737
3664
|
(default True)
|
3738
3665
|
|
3739
|
-
screen_coordinates : bool
|
3740
|
-
use screen_coordinates
|
3741
|
-
|
3742
|
-
if False, the scale parameters are use for positioning and scaling
|
3743
|
-
objects.
|
3744
|
-
|
3745
|
-
if True (default), screen_coordinates will be used.
|
3746
|
-
|
3747
3666
|
Note
|
3748
3667
|
----
|
3749
3668
|
All measures are in screen coordinates
|
@@ -3790,7 +3709,6 @@ class AnimateMonitor(DynamicClass):
|
|
3790
3709
|
layer: Union[float, Callable] = 0,
|
3791
3710
|
visible: Union[bool, Callable] = True,
|
3792
3711
|
keep: Union[bool, Callable] = True,
|
3793
|
-
screen_coordinates: bool = True,
|
3794
3712
|
arg: Any = None,
|
3795
3713
|
):
|
3796
3714
|
super().__init__()
|
@@ -3849,7 +3767,7 @@ class AnimateMonitor(DynamicClass):
|
|
3849
3767
|
self._monitor = monitor
|
3850
3768
|
self.as_level = monitor._level
|
3851
3769
|
self.over3d = over3d
|
3852
|
-
self.screen_coordinates =
|
3770
|
+
self.screen_coordinates = True
|
3853
3771
|
self.register_dynamic_attributes(
|
3854
3772
|
"linecolor linewidth fillcolor bordercolor borderlinewidth titlecolor nowcolor titlefont titlefontsize title "
|
3855
3773
|
"x y offsetx offsety angle vertical_offset parent vertical_scale horizontal_scale width height "
|
@@ -7211,8 +7129,8 @@ class Component:
|
|
7211
7129
|
else:
|
7212
7130
|
self.env.print_trace("", "", self.name() + " create data component", self._modetxt())
|
7213
7131
|
else:
|
7214
|
-
_check_overlapping_parameters(self, "__init__", process_name,process=p)
|
7215
|
-
_check_overlapping_parameters(self, "setup", process_name,process=p)
|
7132
|
+
_check_overlapping_parameters(self, "__init__", process_name, process=p)
|
7133
|
+
_check_overlapping_parameters(self, "setup", process_name, process=p)
|
7216
7134
|
|
7217
7135
|
self.env.print_trace("", "", self.name() + " create", self._modetxt())
|
7218
7136
|
|
@@ -7308,7 +7226,7 @@ by adding at the end:
|
|
7308
7226
|
"""
|
7309
7227
|
size_x = 50
|
7310
7228
|
size_y = 50
|
7311
|
-
ao0 = AnimateRectangle(text=str(self.sequence_number()), textcolor="bg", spec=(-20, -20, 20, 20), linewidth=0, fillcolor="fg")
|
7229
|
+
ao0 = AnimateRectangle(text=str(self.sequence_number()), textcolor="bg", spec=(-20, -20, 20, 20), linewidth=0, fillcolor="fg", screen_coordinates=True)
|
7312
7230
|
return (size_x, size_y, ao0)
|
7313
7231
|
|
7314
7232
|
def animation3d_objects(self, id: Any) -> Tuple:
|
@@ -7512,7 +7430,7 @@ by adding at the end:
|
|
7512
7430
|
return return_or_print(result, as_str, file)
|
7513
7431
|
|
7514
7432
|
def _push(self, t, priority, urgent, return_value=None, switch=True):
|
7515
|
-
if t!=inf:
|
7433
|
+
if t != inf:
|
7516
7434
|
self.env._seq += 1
|
7517
7435
|
if urgent:
|
7518
7436
|
seq = -self.env._seq
|
@@ -10657,23 +10575,22 @@ class Environment:
|
|
10657
10575
|
self._step_pressed = False
|
10658
10576
|
self.stopped = False
|
10659
10577
|
self._paused = False
|
10578
|
+
self._zoom_factor = 1.1
|
10579
|
+
|
10660
10580
|
self.last_s0 = ""
|
10661
|
-
if
|
10581
|
+
if Xlwings:
|
10662
10582
|
if blind_animation is None:
|
10663
|
-
blind_animation=True
|
10583
|
+
blind_animation = True
|
10664
10584
|
if not blind_animation:
|
10665
10585
|
raise ValueError("blind_animation may not be False under xlwings lite")
|
10666
10586
|
else:
|
10667
10587
|
if blind_animation is None:
|
10668
|
-
blind_animation=False
|
10588
|
+
blind_animation = False
|
10669
10589
|
self._blind_animation = blind_animation
|
10670
10590
|
|
10671
10591
|
if self._blind_animation:
|
10672
10592
|
with self.suppress_trace():
|
10673
10593
|
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
10594
|
|
10678
10595
|
if Pythonista:
|
10679
10596
|
self._width, self._height = ui.get_screen_size()
|
@@ -10788,6 +10705,23 @@ class Environment:
|
|
10788
10705
|
"""
|
10789
10706
|
if self._ui:
|
10790
10707
|
self._handle_ui_event()
|
10708
|
+
self._x0_org = self._x0
|
10709
|
+
self._x1_org = self._x1
|
10710
|
+
self._y0_org = self._y0
|
10711
|
+
self._y1_org = self._y1
|
10712
|
+
self._scale_org = self._scale
|
10713
|
+
self._x0 = self._x0z
|
10714
|
+
self._y0 = self._y0z
|
10715
|
+
self._x1 = self._x1z # 0+self._width/self._scale
|
10716
|
+
self._y1 = self._y1z # +self._height/self._scale
|
10717
|
+
self._scale = self._scalez
|
10718
|
+
|
10719
|
+
# midx=self._x0+self._panx*(self._x1-self._x0)
|
10720
|
+
# self._x0, self._x1= midx-(1/self._zoom)*(self._x1-self._x0)/2,midx+(1/self._zoom)*(self._x1-self._x0)/2
|
10721
|
+
|
10722
|
+
# midy=self._y0+self._pany*(self._y1-self._y0)
|
10723
|
+
# self._y0, self._y1 = midy-(1/self._zoom)*(self._y1-self._y0)/2, midy+(1/self._zoom)*(self._y1-self._y0)/2
|
10724
|
+
# self._scale = self._width / (self._x1 - self._x0)
|
10791
10725
|
|
10792
10726
|
def animation_post_tick(self, t: float) -> None:
|
10793
10727
|
"""
|
@@ -10800,7 +10734,11 @@ class Environment:
|
|
10800
10734
|
t : float
|
10801
10735
|
Current (animation) time.
|
10802
10736
|
"""
|
10803
|
-
|
10737
|
+
self._x0 = self._x0_org
|
10738
|
+
self._x1 = self._x1_org
|
10739
|
+
self._y0 = self._y0_org
|
10740
|
+
self._y1 = self._y1_org
|
10741
|
+
self._scale = self._scale
|
10804
10742
|
|
10805
10743
|
def animation_pre_tick_sys(self, t: float) -> None:
|
10806
10744
|
for ao in self.sys_objects.copy(): # copy required as ao's may be removed due to keep
|
@@ -11325,6 +11263,63 @@ class Environment:
|
|
11325
11263
|
def on_closing(self):
|
11326
11264
|
self.an_quit()
|
11327
11265
|
|
11266
|
+
|
11267
|
+
def on_mousewheel(self, event):
|
11268
|
+
x_mouse = self.root.winfo_pointerx() - self.root.winfo_rootx()
|
11269
|
+
y_mouse = self.height() - self.root.winfo_pointery() + self.root.winfo_rooty()
|
11270
|
+
x = (x_mouse / self._scale) + self._x0z
|
11271
|
+
y = (y_mouse / self._scale) + self._y0z
|
11272
|
+
|
11273
|
+
if Windows:
|
11274
|
+
delta = int(event.delta / 120) # normalize to ticks
|
11275
|
+
elif MacOS:
|
11276
|
+
delta = int(event.delta) # already small, usually ±1
|
11277
|
+
else:
|
11278
|
+
delta = 0 # fallback
|
11279
|
+
for _ in range(abs(delta)):
|
11280
|
+
if delta < 0:
|
11281
|
+
zoom_factor = self._zoom_factor
|
11282
|
+
|
11283
|
+
else:
|
11284
|
+
zoom_factor = 1 / self._zoom_factor
|
11285
|
+
|
11286
|
+
# min_zoom = min(
|
11287
|
+
# (self._x0 - x) / (self._x0z - x),
|
11288
|
+
# (self._x0 - x) / (self._x0z - x),
|
11289
|
+
# (self._x1 - x) / (self._x1z - x),
|
11290
|
+
# (self._y0 - y) / (self._y0z - y),
|
11291
|
+
# (self._y1 - y) / (self._y1z - y),
|
11292
|
+
# )
|
11293
|
+
|
11294
|
+
# zoom_factor = min(zoom_factor, min_zoom)
|
11295
|
+
|
11296
|
+
self._scalez /= zoom_factor
|
11297
|
+
self._x0z = x - (x - self._x0z) * zoom_factor
|
11298
|
+
self._y0z = y - (y - self._y0z) * zoom_factor
|
11299
|
+
self._x1z = x - (x - self._x1z) * zoom_factor
|
11300
|
+
self._y1z = y - (y - self._y1z) * zoom_factor
|
11301
|
+
|
11302
|
+
def start_pan(self, event):
|
11303
|
+
g.canvas.config(cursor="fleur") # Change cursor to "move" style
|
11304
|
+
self.lastx = event.x
|
11305
|
+
self.lasty = event.y
|
11306
|
+
self.lastx0 = self._x0z
|
11307
|
+
self.lasty0 = self._y0z
|
11308
|
+
|
11309
|
+
def do_pan(self, event):
|
11310
|
+
dx = -((event.x - self.lastx) / self._scale)
|
11311
|
+
dy = ((event.y - self.lasty) / self._scale)
|
11312
|
+
# self._x0z = max(self._x0,self.lastx0 + dx)
|
11313
|
+
# self._y0z = max(self._y0,self.lasty0 + dy)
|
11314
|
+
self._x0z = self.lastx0 + dx
|
11315
|
+
self._y0z = self.lasty0 + dy
|
11316
|
+
self._x1z = self._x0z + (self._x1 - self._x0)
|
11317
|
+
self._y1z = self._y0z + (self._y1 - self._y0)
|
11318
|
+
|
11319
|
+
|
11320
|
+
def end_pan(self, event):
|
11321
|
+
g.canvas.config(cursor="") # Reset to default
|
11322
|
+
|
11328
11323
|
def animation_parameters(
|
11329
11324
|
self,
|
11330
11325
|
animate: Union[bool, str] = None,
|
@@ -11532,8 +11527,6 @@ class Environment:
|
|
11532
11527
|
|
11533
11528
|
If no codec is given, MJPG will be used for .avi files, otherwise .mp4v
|
11534
11529
|
|
11535
|
-
Under PyDroid only .avi files are supported.
|
11536
|
-
|
11537
11530
|
video_repeat : int
|
11538
11531
|
number of times animated gif or png should be repeated
|
11539
11532
|
|
@@ -11855,8 +11848,6 @@ class Environment:
|
|
11855
11848
|
self._video_name = self._video_name[:-5] # get rid of codec
|
11856
11849
|
else:
|
11857
11850
|
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
11851
|
can_video(try_only=False)
|
11861
11852
|
fourcc = cv2.VideoWriter_fourcc(*codec)
|
11862
11853
|
if video_path.is_file():
|
@@ -11931,12 +11922,22 @@ class Environment:
|
|
11931
11922
|
self.root.bind("<space>", lambda self: g.animation_env.an_menu_go())
|
11932
11923
|
self.root.bind("s", lambda self: g.animation_env.an_single_step())
|
11933
11924
|
self.root.bind("<Control-c>", lambda self: g.animation_env.an_quit())
|
11925
|
+
self.root.bind("<MouseWheel>", self.on_mousewheel)
|
11926
|
+
self.root.bind("<ButtonPress-1>", self.start_pan)
|
11927
|
+
self.root.bind("<B1-Motion>", self.do_pan)
|
11928
|
+
self.root.bind("<ButtonRelease-1>", self.end_pan)
|
11929
|
+
|
11934
11930
|
|
11935
11931
|
g.canvas = tkinter.Canvas(self.root, width=self._width, height=self._height)
|
11936
11932
|
g.canvas.configure(background=self.colorspec_to_hex("bg", False))
|
11937
11933
|
g.canvas.pack()
|
11938
11934
|
g.canvas_objects = []
|
11939
11935
|
g.canvas_object_overflow_image = None
|
11936
|
+
|
11937
|
+
# g.canvas.move("all", 1, 1)
|
11938
|
+
# g.canvas.update()
|
11939
|
+
# g.canvas.move("all", -1, -1)
|
11940
|
+
# g.canvas.update()
|
11940
11941
|
|
11941
11942
|
self.uninstall_uios() # this causes all ui objects to be (re)installed
|
11942
11943
|
|
@@ -11960,59 +11961,34 @@ class Environment:
|
|
11960
11961
|
if self._video_pingpong:
|
11961
11962
|
self._images.extend(self._images[::-1])
|
11962
11963
|
if self._video_repeat == 1: # in case of repeat == 1, loop should not be specified (otherwise, it might show twice)
|
11963
|
-
|
11964
|
-
|
11964
|
+
for _ in range(2): # normally runs only once
|
11965
|
+
try:
|
11965
11966
|
self._images[0].save(
|
11966
|
-
|
11967
|
+
self._video_name,
|
11967
11968
|
disposal=2,
|
11968
11969
|
save_all=True,
|
11969
11970
|
append_images=self._images[1:],
|
11970
11971
|
duration=round(1000 / self._real_fps),
|
11971
11972
|
optimize=False,
|
11972
|
-
format="GIF",
|
11973
11973
|
)
|
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]
|
11974
|
+
break
|
11975
|
+
except ValueError: # prevent bug in Python 3.13
|
11976
|
+
self._images = [image.convert("RGB") for image in self._images]
|
11988
11977
|
|
11989
11978
|
else:
|
11990
|
-
|
11991
|
-
|
11979
|
+
for _ in range(2): # normally runs only once
|
11980
|
+
try:
|
11992
11981
|
self._images[0].save(
|
11993
|
-
|
11982
|
+
self._video_name,
|
11994
11983
|
disposal=2,
|
11995
11984
|
save_all=True,
|
11996
11985
|
append_images=self._images[1:],
|
11997
11986
|
loop=self._video_repeat,
|
11998
11987
|
duration=round(1000 / self._real_fps),
|
11999
11988
|
optimize=False,
|
12000
|
-
format="GIF",
|
12001
11989
|
)
|
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]
|
11990
|
+
except ValueError: # prevent bug in Python 3.13
|
11991
|
+
self._images = [image.convert("RGB") for image in self._images]
|
12016
11992
|
|
12017
11993
|
del self._images
|
12018
11994
|
elif self._video_out == "png":
|
@@ -13419,6 +13395,12 @@ class Environment:
|
|
13419
13395
|
raise SimulationStopped
|
13420
13396
|
else:
|
13421
13397
|
self.root.after(0, self.simulate_and_animate_loop)
|
13398
|
+
self._x0z = self._x0
|
13399
|
+
self._y0z = self._y0
|
13400
|
+
self._x1z = self._x1
|
13401
|
+
self._y1z = self._y1
|
13402
|
+
|
13403
|
+
self._scalez = self._scale
|
13422
13404
|
self.root.mainloop()
|
13423
13405
|
if self._animate and self.running:
|
13424
13406
|
if self._video:
|
@@ -13604,13 +13586,8 @@ class Environment:
|
|
13604
13586
|
mode = "RGB"
|
13605
13587
|
else:
|
13606
13588
|
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))
|
13589
|
+
filename_path.parent.mkdir(parents=True, exist_ok=True)
|
13590
|
+
self._capture_image(mode, video_mode).save(str(filename))
|
13614
13591
|
|
13615
13592
|
def modelname_width(self):
|
13616
13593
|
if Environment.cached_modelname_width[0] != self._modelname:
|
@@ -13933,19 +13910,19 @@ class Environment:
|
|
13933
13910
|
if screen_coordinates:
|
13934
13911
|
return 0
|
13935
13912
|
else:
|
13936
|
-
return self.
|
13913
|
+
return self._x0_org / scale
|
13937
13914
|
|
13938
13915
|
if xy_anchor in ("n", "c", "center", "s"):
|
13939
13916
|
if screen_coordinates:
|
13940
13917
|
return (width / 2) / scale
|
13941
13918
|
else:
|
13942
|
-
return ((self.
|
13919
|
+
return ((self._x0_org + self._x1_org) / 2) / scale
|
13943
13920
|
|
13944
13921
|
if xy_anchor in ("ne", "e", "se", ""):
|
13945
13922
|
if screen_coordinates:
|
13946
13923
|
return width / scale
|
13947
13924
|
else:
|
13948
|
-
return self.
|
13925
|
+
return self._x1_org / scale
|
13949
13926
|
|
13950
13927
|
raise ValueError("incorrect xy_anchor", xy_anchor)
|
13951
13928
|
|
@@ -13960,19 +13937,19 @@ class Environment:
|
|
13960
13937
|
if screen_coordinates:
|
13961
13938
|
return height / scale
|
13962
13939
|
else:
|
13963
|
-
return self.
|
13940
|
+
return self._y1_org / scale
|
13964
13941
|
|
13965
13942
|
if xy_anchor in ("w", "c", "center", "e"):
|
13966
13943
|
if screen_coordinates:
|
13967
13944
|
return (height / 2) / scale
|
13968
13945
|
else:
|
13969
|
-
return ((self.
|
13946
|
+
return ((self._y0_org + self._y1_org) / 2) / scale
|
13970
13947
|
|
13971
13948
|
if xy_anchor in ("sw", "s", "se", ""):
|
13972
13949
|
if screen_coordinates:
|
13973
13950
|
return 0
|
13974
13951
|
else:
|
13975
|
-
return self.
|
13952
|
+
return self._y0_org / scale
|
13976
13953
|
|
13977
13954
|
raise ValueError("incorrect xy_anchor", xy_anchor)
|
13978
13955
|
|
@@ -14621,16 +14598,14 @@ class Environment:
|
|
14621
14598
|
note that the header is only printed if trace=True
|
14622
14599
|
"""
|
14623
14600
|
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 * "-",
|
14601
|
+
self.print_trace((len_s1 - 4) * " " + "time", "current component", "action", "information", "line#")
|
14602
|
+
self.print_trace(len_s1 * "-", 20 * "-", 35 * "-", 48 * "-", 6 * "-")
|
14626
14603
|
for ref in range(len(self._source_files)):
|
14627
14604
|
for fullfilename, iref in self._source_files.items():
|
14628
14605
|
if ref == iref:
|
14629
14606
|
self._print_legend(iref)
|
14630
14607
|
|
14631
14608
|
def _print_legend(self, ref):
|
14632
|
-
if Embedded:
|
14633
|
-
return
|
14634
14609
|
if ref:
|
14635
14610
|
s = "line numbers prefixed by " + chr(ord("A") + ref - 1) + " refer to"
|
14636
14611
|
else:
|
@@ -14647,8 +14622,6 @@ class Environment:
|
|
14647
14622
|
return self.filename_lineno_to_str(frameinfo.filename, frameinfo.lineno)
|
14648
14623
|
|
14649
14624
|
def filename_lineno_to_str(self, filename, lineno):
|
14650
|
-
if Embedded:
|
14651
|
-
return "n/a"
|
14652
14625
|
if Path(filename).name == Path(__file__).name: # internal salabim address
|
14653
14626
|
return "n/a"
|
14654
14627
|
ref = self._source_files.get(filename)
|
@@ -18239,7 +18212,6 @@ class AnimateQueue(DynamicClass):
|
|
18239
18212
|
over3d=None,
|
18240
18213
|
keep=True,
|
18241
18214
|
visible=True,
|
18242
|
-
screen_coordinates=True,
|
18243
18215
|
):
|
18244
18216
|
super().__init__()
|
18245
18217
|
_checkisqueue(queue)
|
@@ -18270,7 +18242,7 @@ class AnimateQueue(DynamicClass):
|
|
18270
18242
|
self.keep = keep
|
18271
18243
|
self.over3d = _default_over3d if over3d is None else over3d
|
18272
18244
|
self.trajectory = trajectory
|
18273
|
-
self.screen_coordinates =
|
18245
|
+
self.screen_coordinates = True
|
18274
18246
|
self.register_dynamic_attributes(
|
18275
18247
|
"xy_anchor x y id max_length direction reverse titleoffsetx titleoffsety titlefont titlefontsize titlecolor title layer visible keep trajectory"
|
18276
18248
|
)
|
@@ -25118,7 +25090,7 @@ def _set_name(name, _nameserialize, object):
|
|
25118
25090
|
object._name = name
|
25119
25091
|
|
25120
25092
|
|
25121
|
-
def _check_overlapping_parameters(obj, method_name0, method_name1,process=None):
|
25093
|
+
def _check_overlapping_parameters(obj, method_name0, method_name1, process=None):
|
25122
25094
|
"""
|
25123
25095
|
this function is a helper to see whether __init__, setup and process parameters overlap
|
25124
25096
|
|
@@ -25136,12 +25108,12 @@ def _check_overlapping_parameters(obj, method_name0, method_name1,process=None):
|
|
25136
25108
|
process: method
|
25137
25109
|
used for process check: should be the process methoc
|
25138
25110
|
"""
|
25139
|
-
method0=getattr(obj, method_name0)
|
25111
|
+
method0 = getattr(obj, method_name0)
|
25140
25112
|
|
25141
25113
|
if process is None:
|
25142
|
-
method1=getattr(obj, method_name1)
|
25114
|
+
method1 = getattr(obj, method_name1)
|
25143
25115
|
else:
|
25144
|
-
method1=process
|
25116
|
+
method1 = process
|
25145
25117
|
|
25146
25118
|
overlapping_parameters = set(inspect.signature(method0).parameters) & set(inspect.signature(method1).parameters)
|
25147
25119
|
if overlapping_parameters:
|
@@ -25152,7 +25124,7 @@ def _check_overlapping_parameters(obj, method_name0, method_name1,process=None):
|
|
25152
25124
|
|
25153
25125
|
@functools.lru_cache()
|
25154
25126
|
def _screen_dimensions():
|
25155
|
-
if
|
25127
|
+
if Xlwings:
|
25156
25128
|
return 1024, 768
|
25157
25129
|
if Pythonista:
|
25158
25130
|
screen_width, screen_height = ui.get_screen_size()
|
@@ -27180,7 +27152,7 @@ def _std_fonts():
|
|
27180
27152
|
|
27181
27153
|
|
27182
27154
|
def fonts():
|
27183
|
-
if
|
27155
|
+
if Xlwings:
|
27184
27156
|
return []
|
27185
27157
|
if not hasattr(fonts, "font_list"):
|
27186
27158
|
fonts.font_list = []
|
@@ -27453,7 +27425,7 @@ def can_animate(try_only: bool = True) -> bool:
|
|
27453
27425
|
except ImportError:
|
27454
27426
|
ImageGrab = None
|
27455
27427
|
|
27456
|
-
if not Pythonista and not
|
27428
|
+
if not Pythonista and not Xlwings:
|
27457
27429
|
from PIL import ImageTk
|
27458
27430
|
except ImportError:
|
27459
27431
|
if try_only:
|
@@ -27462,12 +27434,7 @@ def can_animate(try_only: bool = True) -> bool:
|
|
27462
27434
|
|
27463
27435
|
g.dummy_image = Image.new("RGBA", (1, 1), (0, 0, 0, 0))
|
27464
27436
|
|
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")
|
27437
|
+
if not Pythonista and not Xlwings:
|
27471
27438
|
try:
|
27472
27439
|
import tkinter
|
27473
27440
|
import tkinter.font
|
@@ -0,0 +1,10 @@
|
|
1
|
+
salabim/DejaVuSansMono.ttf,sha256=Z_oIXp5yp1Zaw2y2p3vaxwHhjHpG0MFbmwhxSh4aIEI,335068
|
2
|
+
salabim/LICENSE.txt,sha256=eTPlcDJz4G0096Qv-wfMjm1Wxbd4ilDlsYg5rN4HjWQ,1106
|
3
|
+
salabim/__init__.py,sha256=r7qPLvlmX0dkZDyjuTo8Jo3ex3sD1L4pmK6K5ib9vyw,56
|
4
|
+
salabim/calibri.ttf,sha256=RWpf8Uo31RfvGGNaSt9-2sXSuN87AVE_NFMRsV3LhBk,1330156
|
5
|
+
salabim/mplus-1m-regular.ttf,sha256=EuFHr90BJjuAn_r5MleJFN-WfkeWJ4tf7DweI5zr8tU,289812
|
6
|
+
salabim/salabim.py,sha256=j8z-zIxsbamnr5aghJLzKffv-lzCnXhAkyo3O1nNLGk,1124813
|
7
|
+
salabim-25.0.10.dist-info/METADATA,sha256=0mIkvZXvdvJxlOBnmZta4du6Nf6MhQuiGnfsqgL4iwA,3400
|
8
|
+
salabim-25.0.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
9
|
+
salabim-25.0.10.dist-info/top_level.txt,sha256=UE6zVlbi3F6T5ma1a_5TrojMaF21GYKDt9svvm0U4cQ,8
|
10
|
+
salabim-25.0.10.dist-info/RECORD,,
|
@@ -1,10 +0,0 @@
|
|
1
|
-
salabim/DejaVuSansMono.ttf,sha256=Z_oIXp5yp1Zaw2y2p3vaxwHhjHpG0MFbmwhxSh4aIEI,335068
|
2
|
-
salabim/LICENSE.txt,sha256=eTPlcDJz4G0096Qv-wfMjm1Wxbd4ilDlsYg5rN4HjWQ,1106
|
3
|
-
salabim/__init__.py,sha256=r7qPLvlmX0dkZDyjuTo8Jo3ex3sD1L4pmK6K5ib9vyw,56
|
4
|
-
salabim/calibri.ttf,sha256=RWpf8Uo31RfvGGNaSt9-2sXSuN87AVE_NFMRsV3LhBk,1330156
|
5
|
-
salabim/mplus-1m-regular.ttf,sha256=EuFHr90BJjuAn_r5MleJFN-WfkeWJ4tf7DweI5zr8tU,289812
|
6
|
-
salabim/salabim.py,sha256=_uh3TROL6eI6TrOSM9QrPdMwg-i7qSFRcbqBlAaBanE,1126024
|
7
|
-
salabim-25.0.9.post3.dist-info/METADATA,sha256=NJ9TPT8vmUIHwE-9uBTKKeW3Wh4eAVMf3Yfg3oNwfG8,3405
|
8
|
-
salabim-25.0.9.post3.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
|
9
|
-
salabim-25.0.9.post3.dist-info/top_level.txt,sha256=UE6zVlbi3F6T5ma1a_5TrojMaF21GYKDt9svvm0U4cQ,8
|
10
|
-
salabim-25.0.9.post3.dist-info/RECORD,,
|
File without changes
|