salabim 23.3.7__tar.gz → 23.3.8__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: salabim
3
- Version: 23.3.7
3
+ Version: 23.3.8
4
4
  Summary: discrete event simulation in Python
5
5
  Home-page: https://github.com/salabim/salabim
6
6
  Download-URL: https://github.com/salabim/salabim
@@ -1,12 +1,70 @@
1
1
  salabim changelog
2
2
 
3
- version 23.3.7
3
+ version 23.3.8 2023-09-05
4
+ ==========================
5
+ Enhanced UI functionality (0)
6
+ -----------------------------
7
+ The UI window layout has been changed to be more useful with
8
+ narrower windows.
9
+ Some more updates to the appearance.
10
+
11
+ When the UI is started, the time in the animation window is no longer disabled.
12
+ The user can always switch that off with Environment.show_time(False).
13
+
14
+ Environment.start_ui() now has useful defaults for window_size and window_position.
15
+
16
+ Environment.start_ui() now has a parameter default_actions, which is True by default.
17
+ If False, there are no actions like, pause/go, speed, etc. defined, so the user has to
18
+ specify the required actions with the actions parameter.
19
+ This is useful to use a different layout or leave out certain elements. Be sure to
20
+ use the same keys to be able to use the programmed interactions. However, you
21
+ can leave out elements.
22
+
23
+ It is recommended to use the standard actions as a template:
24
+
25
+ [sg.Text("", key="-TIME-", metadata=[1, 2], size=200)],
26
+ [sg.Button("Pause", key="-PAUSE-GO-", metadata=[1, 2]), sg.Button("Stop", key="-STOP-", button_color=("white", "firebrick3"), metadata=[1, 2])],
27
+ [sg.Checkbox("Pause at each step", False, key="-PAUSE-AT-EACH-STEP-", enable_events=True, metadata=[1, 2])],
28
+ [sg.Text(f"Pause at{env.get_time_unit(template='(t)')}", key="-PAUSE-AT-TEXT-", size=17), sg.Input("", key="-PAUSE-AT-", size=(10, 20))],
29
+ [sg.Text(f"Pause each{env.get_time_unit(template='(d)')}", key="-PAUSE-EACH-TEXT-", size=17), sg.Input("", key="-PAUSE-EACH-", size=(10, 20))],
30
+ [
31
+ sg.Text("Speed", key="-SPEED-TEXT-", metadata=[1]),
32
+ sg.Button("/2", key="-SPEED/2-", metadata=[1]),
33
+ sg.Button("*2", key="-SPEED*2-", metadata=[1]),
34
+ sg.Input("", key="-SPEED-", size=(7, 10)),
35
+ ],
36
+ [sg.Checkbox("Trace", env.trace(), key="-TRACE-", metadata=[1, 2], enable_events=True)],
37
+ [sg.Checkbox("Synced", env.synced(), key="-SYNCED-", metadata=[1], enable_events=True)],
38
+ [sg.Checkbox("Animate", True, key="-ANIMATE-", metadata=[1, 2], enable_events=True)],
39
+
40
+
41
+ The simulation did not stop exactly at the time given in 'Pause at'. Fixed.
42
+
43
+ Added demos (0)
44
+ ---------------
45
+ The program demo_ui.py shows a pretty standard UI with some extra elements.
46
+
47
+ The program demo_horizontal_ui.py demonstrates the same functionality, but now
48
+ with a horizontal UI window. Note that this requires quite a bit more
49
+ code than the standard one.
50
+ But it demonstrates what is possible.
51
+
52
+ Bug fix (0)
53
+ -----------
54
+ Component.to_store_store did not always return the right store. Fixed.
55
+ Thanks to Florian Förster for reporting this bug and the fix.
56
+
57
+ Bug fix (1)
58
+ -----------
59
+ Store.to_store_requesters() returned the wrong queue. Fixed.
60
+
61
+ version 23.3.7 2023-08-22
4
62
  ==========================
5
63
  Bug fix (0)
6
64
  -----------
7
65
  Environment.paused(True) did not call set_start_animation(), which is required. Fixed.
8
66
 
9
- Big fix (1)
67
+ Bug fix (1)
10
68
  -----------
11
69
  Removing and showing an animated monitor did not restore the labels and label lines. Fixed.
12
70
 
@@ -1,13 +1,13 @@
1
- # _ _ _ ____ _____ _____ _____
2
- # ___ __ _ | | __ _ | |__ (_) _ __ ___ |___ \ |___ / |___ / |___ |
3
- # / __| / _` || | / _` || '_ \ | || '_ ` _ \ __) | |_ \ |_ \ / /
4
- # \__ \| (_| || || (_| || |_) || || | | | | | / __/ ___) | _ ___) | _ / /
5
- # |___/ \__,_||_| \__,_||_.__/ |_||_| |_| |_| |_____||____/ (_)|____/ (_) /_/
1
+ # _ _ _ ____ _____ _____ ___
2
+ # ___ __ _ | | __ _ | |__ (_) _ __ ___ |___ \ |___ / |___ / ( _ )
3
+ # / __| / _` || | / _` || '_ \ | || '_ ` _ \ __) | |_ \ |_ \ / _ \
4
+ # \__ \| (_| || || (_| || |_) || || | | | | | / __/ ___) | _ ___) | _ | (_) |
5
+ # |___/ \__,_||_| \__,_||_.__/ |_||_| |_| |_| |_____||____/ (_)|____/ (_) \___/
6
6
  # Discrete event simulation in Python
7
7
  #
8
8
  # see www.salabim.org for more information, the documentation and license information
9
9
 
10
- __version__ = "23.3.7"
10
+ __version__ = "23.3.8"
11
11
 
12
12
  import heapq
13
13
  import random
@@ -3923,11 +3923,10 @@ class AnimateMonitor(DynamicClass):
3923
3923
  self.ao_now_line.remove()
3924
3924
  for ao in self.ao_label_texts:
3925
3925
  ao.remove()
3926
- self.ao_label_texts=[]
3926
+ self.ao_label_texts = []
3927
3927
  for ao in self.ao_label_lines:
3928
3928
  ao.remove()
3929
- self.ao_label_lines=[]
3930
-
3929
+ self.ao_label_lines = []
3931
3930
 
3932
3931
  self.env.sys_objects.discard(self)
3933
3932
 
@@ -5627,7 +5626,7 @@ class Store(Queue):
5627
5626
  -------
5628
5627
  queue holding all from_store requesting components : Queue
5629
5628
  """
5630
- return self._from_store_requesters
5629
+ return self._to_store_requesters
5631
5630
 
5632
5631
  def rescan(self):
5633
5632
  """
@@ -7099,7 +7098,7 @@ by adding at the end:
7099
7098
  """
7100
7099
  size_x = 50
7101
7100
  size_y = 50
7102
- ao0 = AnimateRectangle(text=str(self.sequence_number()), textcolor="bg", spec=(-20, -20, 20, 20), linewidth=0, fillcolor="fg") # ***
7101
+ ao0 = AnimateRectangle(text=str(self.sequence_number()), textcolor="bg", spec=(-20, -20, 20, 20), linewidth=0, fillcolor="fg")
7103
7102
  return (size_x, size_y, ao0)
7104
7103
 
7105
7104
  def animation3d_objects(self, id: Any) -> Tuple:
@@ -9421,7 +9420,7 @@ by adding:
9421
9420
  requester.status._value = scheduled
9422
9421
  requester._reschedule(requester.env._now, 0, False, f"to_store ({store.name()}) honor ", False, s0=requester.env.last_s0)
9423
9422
  requester._to_store_item = None
9424
- requester._to_store_store = self
9423
+ requester._to_store_store = store
9425
9424
  return self
9426
9425
 
9427
9426
  def priority(self, q: "Queue", priority: float = None) -> float:
@@ -9698,7 +9697,7 @@ by adding:
9698
9697
  if self.isdata():
9699
9698
  return "N/A"
9700
9699
  if self._process is None:
9701
- s0 = "" # ***
9700
+ s0 = ""
9702
9701
  frame = _get_caller_frame()
9703
9702
  lineno = inspect.getframeinfo(frame).lineno
9704
9703
  s0 = rpad(str(lineno) + "+", 6)
@@ -23620,7 +23619,7 @@ class Environment:
23620
23619
  if animate != self._animate:
23621
23620
  frame_changed = True
23622
23621
  self._animate = animate
23623
- if self._ui:
23622
+ if self._ui and "-ANIMATE-" in self._ui_keys:
23624
23623
  self._ui_window["-ANIMATE-"].update(animate)
23625
23624
 
23626
23625
  self._scale = self._width / (self._x1 - self._x0)
@@ -23735,8 +23734,11 @@ class Environment:
23735
23734
  g.animation_env._animate = self._animate
23736
23735
  if not Pythonista:
23737
23736
  if g.animation_env.root is not None: # for blind animation to work properly
23738
- g.animation_env.root.destroy()
23739
- g.animation_env.root = None
23737
+ if self._ui:
23738
+ self.root.withdraw()
23739
+ else:
23740
+ g.animation_env.root.destroy()
23741
+ g.animation_env.root = None
23740
23742
  g.animation_env = None
23741
23743
 
23742
23744
  if self._blind_animation:
@@ -25054,8 +25056,9 @@ class Environment:
25054
25056
  if not self.animate():
25055
25057
  if value:
25056
25058
  self.animate(True)
25057
- self._paused = True # ***
25058
- self.env._ui_window["-ANIMATE-"].update(False) # this is required as the self.animate() also sets the value
25059
+ self._paused = True
25060
+ if "-ANIMATE-" in self._ui_keys:
25061
+ self.env._ui_window["-ANIMATE-"].update(False) # this is required as the self.animate() also sets the value
25059
25062
 
25060
25063
  return self._paused
25061
25064
 
@@ -25556,7 +25559,6 @@ class Environment:
25556
25559
  screen_coordinates=True,
25557
25560
  xy_anchor="ne",
25558
25561
  env=self,
25559
- visible=lambda: not self._ui,
25560
25562
  )
25561
25563
  ao.text = self.clocktext
25562
25564
 
@@ -25640,7 +25642,6 @@ class Environment:
25640
25642
  else:
25641
25643
  fps = 0
25642
25644
  s += f"fps={fps:.1f}"
25643
-
25644
25645
  if self._show_time:
25645
25646
  if s != "":
25646
25647
  s += " "
@@ -26003,15 +26004,50 @@ class Environment:
26003
26004
  """
26004
26005
  return self._sequence_number
26005
26006
 
26006
- def get_time_unit(self) -> str:
26007
+ def get_time_unit(self, template: str = None) -> str:
26007
26008
  """
26008
26009
  gets time unit
26009
26010
 
26011
+ Parameters
26012
+ ----------
26013
+ template : str
26014
+ normally only used in UI functions
26015
+
26016
+ default: just return time_unit (including n/a)
26017
+
26018
+ if "d", time_unit as duration
26019
+
26020
+ if "t", time_unit as time
26021
+
26022
+ if "(d)", time_unit as (duration)
26023
+
26024
+ if "(t)", time_unit as (time)
26025
+
26026
+ Note that n/a is suppressed and an extra space is added at the front
26027
+ if result is not the null strinf
26028
+
26010
26029
  Returns
26011
26030
  -------
26012
26031
  Current time unit dimension (default "n/a") : str
26013
26032
  """
26014
- return self._time_unit_name
26033
+ if template is None:
26034
+ return self._time_unit_name
26035
+ if template not in "d t (d) (t)".split():
26036
+ raise ValueError
26037
+
26038
+ result = ""
26039
+ if "t" in template and self._datetime0:
26040
+ if "(" in template:
26041
+ result = "yyyy-mm-dd"
26042
+ else:
26043
+ if self._time_unit_name != "n/a":
26044
+ result = self._time_unit_name
26045
+ if result:
26046
+ if "(" in template:
26047
+ result = f" ({result})"
26048
+ else:
26049
+ result = f" {result}"
26050
+ return result
26015
26051
 
26016
26052
  def years(self, t: float) -> float:
26017
26053
  """
@@ -26751,19 +26787,56 @@ class Environment:
26751
26787
  def stop_ui(self):
26752
26788
  if self._ui:
26753
26789
  animate = self.animate()
26754
- self.pauser.cancel()
26790
+ if not self.pauser.isdata():
26791
+ self.pauser.cancel()
26755
26792
  self._ui = False
26756
26793
  self._ui_window.close()
26757
26794
  self.animate(animate)
26758
26795
  self._ui = False
26759
26796
 
26760
- def start_ui(self, window_size: Tuple, window_position: Tuple, actions=None, user_handle_event=None):
26797
+ def start_ui(
26798
+ self,
26799
+ window_size: Tuple = (None, None),
26800
+ window_position: Tuple = (None, None),
26801
+ elements: List = None,
26802
+ user_handle_event: Callable = None,
26803
+ default_elements: bool = True,
26804
+ actions: List = None,
26805
+ ):
26806
+ """
26807
+ start the PySimpleGUI UI
26808
+
26809
+ Parameters
26810
+ ----------
26811
+ window_size : tuple
26812
+ width (int) ; default (None): 300
26813
+
26814
+ height (int) ; default (None): 600
26815
+
26816
+ window_position : tuple
26817
+ x (int) ; default (None): width of animation
26818
+
26819
+ y (int) ; default (None): 0
26820
+
26821
+ elements : list
26822
+ extra elements to add (refer to PySimpleGUI reference)
26823
+
26824
+ user_handle_event : callable
26825
+ default: no handler
26826
+
26827
+ default_elements : bool
26828
+ if True (default), UI will start with the standard elements
26829
+
26830
+ if False, no standard elements will be used. Use elements
26831
+ to add required standard elements
26832
+ """
26761
26833
  global sg
26762
26834
 
26763
26835
  try:
26764
26836
  import PySimpleGUI as sg
26765
26837
  except ImportError:
26766
26838
  raise ImportError("PySimpleGUI required for ui. Install with pip install PySimpleGUI")
26839
+ self.remove_topleft_buttons()
26767
26840
 
26768
26841
  self.animation_parameters(use_toplevel=True)
26769
26842
  self.pauser = _Pauser(at=inf)
@@ -26775,35 +26848,56 @@ class Environment:
26775
26848
  else:
26776
26849
  self.user_handle_event = user_handle_event
26777
26850
 
26778
- frame0 = [
26779
- [sg.Text("", key="-TIME-", metadata=[1, 2])],
26780
- [sg.Button("Pause", key="-PAUSE-GO-", metadata=[1, 2]), sg.Button("Stop", key="-STOP-", button_color=("white", "firebrick3"), metadata=[1, 2])],
26781
- [
26782
- sg.Checkbox("Pause at each step", False, key="-PAUSE-AT-EACH-STEP-", enable_events=True, metadata=[1, 2]),
26783
- sg.Text("Pause at", key="-PAUSE-AT-TEXT-"),
26784
- sg.Input("", key="-PAUSE-AT-", size=(10, 20)),
26785
- sg.Text("Pause each", key="-PAUSE-EACH-TEXT-"),
26786
- sg.Input("", key="-PAUSE-EACH-", size=(10, 20)),
26787
- ],
26788
- [
26789
- sg.Button("Speed/2", key="-SPEED/2-", metadata=[1]),
26790
- sg.Button("Speed*2", key="-SPEED*2-", metadata=[1]),
26791
- sg.Text("Speed", key="-SPEED-TEXT-"),
26792
- sg.Input("", key="-SPEED-", size=(4, 10)),
26793
- ],
26794
- [sg.Checkbox("Trace", self.trace(), key="-TRACE-", metadata=[1, 2], enable_events=True)],
26795
- [sg.Checkbox("Synced", self.synced(), key="-SYNCED-", metadata=[1], enable_events=True)],
26796
- [sg.Checkbox("Animate", True, key="-ANIMATE-", metadata=[1, 2], enable_events=True)],
26797
- ]
26851
+ if default_elements:
26852
+ frame0 = [
26853
+ [sg.Text("", key="-TIME-", metadata=[1, 2], size=200)],
26854
+ [sg.Button("Pause", key="-PAUSE-GO-", metadata=[1, 2]), sg.Button("Stop", key="-STOP-", button_color=("white", "firebrick3"), metadata=[1, 2])],
26855
+ [sg.Checkbox("Pause at each step", False, key="-PAUSE-AT-EACH-STEP-", enable_events=True, metadata=[1, 2])],
26856
+ [sg.Text(f"Pause at{self.get_time_unit(template='(t)')}", key="-PAUSE-AT-TEXT-", size=17), sg.Input("", key="-PAUSE-AT-", size=(10, 20))],
26857
+ [sg.Text(f"Pause each{self.get_time_unit(template='(d)')}", key="-PAUSE-EACH-TEXT-", size=17), sg.Input("", key="-PAUSE-EACH-", size=(10, 20))],
26858
+ [
26859
+ sg.Text("Speed", key="-SPEED-TEXT-", metadata=[1]),
26860
+ sg.Button("/2", key="-SPEED/2-", metadata=[1]),
26861
+ sg.Button("*2", key="-SPEED*2-", metadata=[1]),
26862
+ sg.Input("", key="-SPEED-", size=(7, 10)),
26863
+ ],
26864
+ [sg.Checkbox("Trace", self.trace(), key="-TRACE-", metadata=[1, 2], enable_events=True)],
26865
+ [sg.Checkbox("Synced", self.synced(), key="-SYNCED-", metadata=[1], enable_events=True)],
26866
+ [sg.Checkbox("Animate", True, key="-ANIMATE-", metadata=[1, 2], enable_events=True)],
26867
+ ]
26868
+ else:
26869
+ frame0 = []
26870
+
26871
+ if elements:
26872
+ if frame0:
26873
+ frame0.append([sg.HorizontalSeparator()])
26874
+ frame0.extend(elements)
26875
+
26798
26876
  if actions:
26799
- frame0.append([sg.HorizontalSeparator()])
26877
+ if frame0:
26878
+ frame0.append([sg.HorizontalSeparator()])
26800
26879
  frame0.extend(actions)
26801
- layout = [[sg.Frame("salabim", frame0, pad=((0, 0), (20, 0)))]]
26880
+
26881
+ layout = [[sg.Frame("", frame0, pad=((0, 0), (20, 0)))]]
26882
+
26883
+ window_size = list(window_size)
26884
+ if window_size[0] is None:
26885
+ window_size[0] = 300
26886
+ if window_size[1] is None:
26887
+ window_size[1] = self.height() + 33
26888
+
26889
+ window_position = list(window_position)
26890
+
26891
+ if window_position[0] is None:
26892
+ window_position[0] = self.width() + 10
26893
+ if window_position[1] is None:
26894
+ window_position[1] = 0
26802
26895
 
26803
26896
  self._last_animate = "?"
26804
26897
  self._last_paused = "?"
26805
- self._ui_window = sg.Window("salabim control", layout, size=window_size, location=window_position)
26898
+ self._ui_window = sg.Window("", no_titlebar=True, layout=layout, size=window_size, location=window_position)
26806
26899
  self._ui_window.finalize()
26900
+ self._ui_keys = {key.key for key in self._ui_window.element_list()}
26807
26901
 
26808
26902
  self.pause_at = inf
26809
26903
  self._pause_at_each_step = False
@@ -26813,7 +26907,8 @@ class Environment:
26813
26907
  return self._ui_window
26814
26908
 
26815
26909
  def set_pause_go_button(self):
26816
- self._ui_window["-PAUSE-GO-"].Update("Go" if self._paused else "Pause")
26910
+ if "-PAUSE-GO-" in self._ui_keys:
26911
+ self._ui_window["-PAUSE-GO-"].Update("Go" if self._paused else "Pause")
26817
26912
 
26818
26913
  def _handle_ui_event(self):
26819
26914
  if self._last_animate != self._animate or self._last_paused != self._paused:
@@ -26833,42 +26928,47 @@ class Environment:
26833
26928
 
26834
26929
  event, values = self._ui_window.read(timeout=0)
26835
26930
 
26836
- t = self.pauser.scheduled_time()
26837
- s = [self.clocktext(self.t())]
26838
- if t != inf and not self._paused:
26839
- s.append(f" | Pause at {self.clocktext(t)}")
26840
- if self.animate():
26841
- s.append(f" | Speed={self.speed():.3f}")
26842
-
26843
26931
  if values is None:
26844
26932
  return
26845
26933
 
26846
- if values["-SPEED-"] == "":
26847
- self._ui_window["-SPEED-"].update(str(self.speed()))
26934
+ if "-SPEED-" in self._ui_keys:
26935
+ if values["-SPEED-"] == "":
26936
+ self._ui_window["-SPEED-"].update(str(self.speed()))
26848
26937
 
26849
- self._ui_window["-TIME-"].Update("".join(s))
26938
+ if "-TIME-" in self._ui_keys:
26939
+ t = self.pauser.scheduled_time()
26940
+ s = [f"t={self.time_to_str(self.t()).lstrip()}{self.get_time_unit(template='t')}"]
26941
+ if t != inf and not self._paused:
26942
+ s.append(f" | Pause at {self.time_to_str(t).lstrip()}{self.get_time_unit(template='t')}")
26943
+ if self.animate():
26944
+ s.append(f" | Speed={self.speed():.3f}")
26945
+
26946
+ self._ui_window["-TIME-"].Update("".join(s))
26850
26947
 
26851
26948
  if event in ("__TIMEOUT__", sg.WIN_CLOSED, "Exit"):
26852
26949
  return
26853
26950
 
26854
26951
  if event == "-STOP-":
26855
- ch = sg.popup_yes_no(f"{chr(160):>50}", "", title="Stop?") # chr(160) is a non breaking blank, equivalent to "\0xA0")
26952
+ ch = sg.popup_yes_no(f"{chr(160):>50}", title="Stop?") # chr(160) is a non breaking blank, equivalent to "\0xA0")
26856
26953
  if ch == "Yes":
26857
26954
  sys.exit()
26858
26955
 
26859
26956
  if event == "-PAUSE-GO-":
26860
- if not self.animate():
26861
- animate = values["-ANIMATE-"]
26862
- self.animate(True)
26863
- self.paused(False)
26864
- self._ui_window["-ANIMATE-"].update(animate) # this is required as the self.animate() also sets the value
26865
-
26957
+ # if not self.animate():
26958
+ # self.animate(True)
26959
+ # self.paused(False)
26960
+ # if "-ANIMATE-" in self._ui_keys:
26961
+ # self._ui_window["-ANIMATE-"].update(animate) # this is required as the self.animate() also sets the value
26962
+
26866
26963
  self.set_start_animation()
26867
26964
  if self._paused:
26868
- pause_at_str = values["-PAUSE-AT-"]
26965
+ if "-PAUSE-AT-" in self._ui_keys:
26966
+ pause_at_str = values["-PAUSE-AT-"]
26967
+ else:
26968
+ pause_at_str = ""
26869
26969
  if pause_at_str != "":
26870
26970
  try:
26871
- self.pause_at = self.spec_to_time(pause_at_str) # ***
26971
+ self.pause_at = self.spec_to_time(pause_at_str)
26872
26972
  except ValueError:
26873
26973
  self.pause_at = None
26874
26974
  else:
@@ -26877,7 +26977,10 @@ class Environment:
26877
26977
  if self.pause_at is None:
26878
26978
  sg.popup(f"Pause not valid")
26879
26979
  else:
26880
- pause_each_str = values["-PAUSE-EACH-"]
26980
+ if "-PAUSE-EACH-" in self._ui_keys:
26981
+ pause_each_str = values["-PAUSE-EACH-"]
26982
+ else:
26983
+ pause_each_str = ""
26881
26984
  if pause_each_str.strip() != "":
26882
26985
  try:
26883
26986
  pause_each = self.spec_to_duration(pause_each_str)
@@ -26891,46 +26994,54 @@ class Environment:
26891
26994
  sg.popup(f"Pause interval not valid")
26892
26995
  else:
26893
26996
  if self.pause_at > self.t():
26894
- self.animate(values["-ANIMATE-"])
26997
+ if "-ANIMATE-" in self._ui_keys:
26998
+ self.animate(values["-ANIMATE-"])
26895
26999
  self.paused(False)
26896
27000
  if self.pauser.scheduled_time() != self.pause_at:
26897
27001
  self.pauser.activate(at=self.pause_at)
26898
27002
  else:
26899
- sg.popup(f"Pause at should be > {self.env.clocktext(self.env.t())}")
27003
+ sg.popup(f"Pause at should be > {self.time_to_str(self.t()).lstrip()}")
26900
27004
  else:
26901
27005
  self.paused(True)
26902
27006
  self._ui_window[event].Update("Pause")
26903
27007
 
26904
- new_speed = float(values["-SPEED-"])
26905
- if new_speed != self.speed():
26906
- self.speed(new_speed)
26907
- self.set_start_animation()
27008
+ if "-SPEED-" in self._ui_keys:
27009
+ new_speed = float(values["-SPEED-"])
27010
+ if new_speed != self.speed():
27011
+ self.speed(new_speed)
27012
+ self.set_start_animation()
26908
27013
 
26909
27014
  if event == "-SPEED*2-":
26910
27015
  self.speed(self.speed() * 2)
26911
- self._ui_window["-SPEED-"].update(str(self.speed()))
27016
+ if "-SPEED-" in self._ui_keys:
27017
+ self._ui_window["-SPEED-"].update(str(self.speed()))
26912
27018
  self.set_start_animation()
26913
27019
 
26914
27020
  if event == "-SPEED/2-":
26915
27021
  self.speed(self.speed() / 2)
26916
- self._ui_window["-SPEED-"].update(str(self.speed()))
27022
+ if "-SPEED-" in self._ui_keys:
27023
+ self._ui_window["-SPEED-"].update(str(self.speed()))
26917
27024
  self.set_start_animation()
26918
27025
 
26919
27026
  if event == "-SYNCED-":
26920
- self.synced(values["-SYNCED-"])
27027
+ if "-SYNCED-" in self._ui_keys:
27028
+ self.synced(values["-SYNCED-"])
26921
27029
 
26922
27030
  if event == "-PAUSE-AT-EACH-STEP-":
26923
- self._pause_at_each_step = values["-PAUSE-AT-EACH-STEP-"]
27031
+ if "-PAUSE-AT-EACH-STEP-" in self._ui_keys:
27032
+ self._pause_at_each_step = values["-PAUSE-AT-EACH-STEP-"]
26924
27033
 
26925
27034
  if event == "-TRACE-":
26926
- self.trace(values["-TRACE-"])
27035
+ if "-TRACE-" in self._ui_keys:
27036
+ self.trace(values["-TRACE-"])
26927
27037
 
26928
27038
  if event == "-ANIMATE-":
26929
- if values["-ANIMATE-"]:
26930
- self.animate(True)
26931
- self.paused(True)
26932
- else:
26933
- _AnimateOff(urgent=True)
27039
+ if "-ANIMATE-" in self._ui_keys:
27040
+ if values["-ANIMATE-"]:
27041
+ self.animate(True)
27042
+ self.paused(True)
27043
+ else:
27044
+ _AnimateOff(urgent=True)
26934
27045
 
26935
27046
  self.user_handle_event(env=self, window=self._ui_window, event=event, values=values)
26936
27047
 
@@ -26938,14 +27049,18 @@ class Environment:
26938
27049
  class _Pauser(Component):
26939
27050
  def process(self):
26940
27051
  event, values = self.env._ui_window.read(timeout=0)
26941
- animate = values["-ANIMATE-"]
27052
+ if "-ANIMATE-" in self.env._ui_keys:
27053
+ animate = values["-ANIMATE-"]
26942
27054
  self.env.animation_start_time = self.env._now
27055
+ self.env._t = self.env._now
26943
27056
  self.env.animate(True)
26944
27057
  self.env.paused(True)
26945
27058
  self.env.set_pause_go_button()
26946
- if values["-PAUSE-AT-"] != "" and self.env._now >= self.env.spec_to_time(values["-PAUSE-AT-"]):
26947
- self.env._ui_window["-PAUSE-AT-"].Update("")
26948
- self.env._ui_window["-ANIMATE-"].update(animate) # this is required as the self.animate() also sets the value
27059
+ if "-PAUSE-AT-" in self.env._ui_keys:
27060
+ if values["-PAUSE-AT-"] != "" and self.env._now >= self.env.spec_to_time(values["-PAUSE-AT-"]):
27061
+ self.env._ui_window["-PAUSE-AT-"].Update("")
27062
+ if "-ANIMATE-" in self.env._ui_keys:
27063
+ self.env._ui_window["-ANIMATE-"].update(animate) # this is required as the self.animate() also sets the value
26949
27064
 
26950
27065
 
26951
27066
  class _AnimateOff(Component):
@@ -29826,7 +29941,7 @@ class AnimateQueue(DynamicClass):
29826
29941
  dimx = _call(animation_objects[0], t, c)
29827
29942
  dimy = _call(animation_objects[1], t, c)
29828
29943
  for ao in animation_objects[2:]:
29829
- ao.screen_coordinates = self.screen_coordinates # ***
29944
+ ao.screen_coordinates = self.screen_coordinates
29830
29945
  if isinstance(ao, AnimateClassic):
29831
29946
  if direction == "t":
29832
29947
  ao.x0 = xt + trajectory.x(t=x * 1.00, _t0=0)
@@ -29836,7 +29951,7 @@ class AnimateQueue(DynamicClass):
29836
29951
  ao.y0 = y
29837
29952
  else:
29838
29953
  if direction == "t":
29839
- ao.x = xt + trajectory.x(t=x * 1.00, _t0=0) # ***
29954
+ ao.x = xt + trajectory.x(t=x * 1.00, _t0=0)
29840
29955
  ao.y = yt + trajectory.y(t=x * 1.00, _t0=0)
29841
29956
  ao.angle = trajectory.angle(t=x * 1.00, _t0=0)
29842
29957
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: salabim
3
- Version: 23.3.7
3
+ Version: 23.3.8
4
4
  Summary: discrete event simulation in Python
5
5
  Home-page: https://github.com/salabim/salabim
6
6
  Download-URL: https://github.com/salabim/salabim
@@ -3,7 +3,7 @@ from setuptools import setup
3
3
  setup(
4
4
  name="salabim",
5
5
  packages=["salabim"],
6
- version="23.3.7",
6
+ version="23.3.8",
7
7
  include_package_data=True,
8
8
  long_description="salabim\n\nsalabim is a discrete event simulation package in Python\nwith builtin *animation*.",
9
9
  description="discrete event simulation in Python",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes