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 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.9"
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
- PyDroid = sys.platform == "linux" and any("pydroid" in v for v in os.environ.values())
54
+ MacOS = platform.system == "Darwin"
58
55
  PyPy = platform.python_implementation() == "PyPy"
59
56
  Chromebook = "penguin" in platform.uname()
60
- #Xlwings="xlwings" in sys.modules
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 Embedded or Xlwings:
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('yiedless mode is not allowed under Pythonista')
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('yiedless mode is not allowed under xlwings lite')
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 = 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 Embedded or Xlwings:
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
- if Embedded:
11964
- with b64_file_handler(self._video_name, mode="b", result=_pie_result) as f:
11964
+ for _ in range(2): # normally runs only once
11965
+ try:
11965
11966
  self._images[0].save(
11966
- f,
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
- else:
11975
- for _ in range(2): # normally runs only once
11976
- try:
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
- if Embedded:
11991
- with b64_file_handler(self._video_name, mode="b", result=_pie_result) as f:
11979
+ for _ in range(2): # normally runs only once
11980
+ try:
11992
11981
  self._images[0].save(
11993
- f,
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
- else:
12003
- for _ in range(2): # normally runs only once
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
- if Embedded:
13608
- with b64_file_handler(str(filename), mode="b", result=_pie_result) as f:
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._x0 / scale
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._x0 + self._x1) / 2) / scale
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._x1 / scale
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._y1 / scale
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._y0 + self._y1) / 2) / scale
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._y0 / scale
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", "" if (Embedded) else "line#")
14625
- self.print_trace(len_s1 * "-", 20 * "-", 35 * "-", 48 * "-", "" if (Embedded) else 6 * "-")
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 = 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 Embedded or Xlwings:
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 Embedded or Xlwings:
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 (Embedded or Xlwings):
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 (Embedded or Xlwings):
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: salabim
3
- Version: 25.0.9.post3
3
+ Version: 25.0.10
4
4
  Summary: salabim - discrete event simulation in Python
5
5
  Author-email: Ruud van der Ham <rt.van.der.ham@gmail.com>
6
6
  Project-URL: Homepage, https://salabim.org
@@ -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,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.3.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -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,,