v2sim 1.3.0__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.
Files changed (178) hide show
  1. v2sim/__init__.py +21 -0
  2. v2sim/gui/cmpbox/__init__.py +152 -0
  3. v2sim/gui/cmpbox/_lang/en_US.lang +14 -0
  4. v2sim/gui/cmpbox/_lang/zh_CN.lang +14 -0
  5. v2sim/gui/com_no_vx.py +33 -0
  6. v2sim/gui/common.py +5 -0
  7. v2sim/gui/evtq.py +100 -0
  8. v2sim/gui/langhelper.py +34 -0
  9. v2sim/gui/mainbox/__init__.py +991 -0
  10. v2sim/gui/mainbox/_lang/en.lang +157 -0
  11. v2sim/gui/mainbox/_lang/zh_CN.lang +157 -0
  12. v2sim/gui/mainbox/controls/__init__.py +9 -0
  13. v2sim/gui/mainbox/controls/_lang/en.lang +34 -0
  14. v2sim/gui/mainbox/controls/_lang/zh_CN.lang +34 -0
  15. v2sim/gui/mainbox/controls/lipad.py +57 -0
  16. v2sim/gui/mainbox/controls/network.py +710 -0
  17. v2sim/gui/mainbox/controls/pdfe.py +65 -0
  18. v2sim/gui/mainbox/controls/prope.py +82 -0
  19. v2sim/gui/mainbox/controls/rle.py +82 -0
  20. v2sim/gui/mainbox/controls/scrtv.py +290 -0
  21. v2sim/gui/mainbox/controls/sfe.py +79 -0
  22. v2sim/gui/mainbox/controls/sid.py +48 -0
  23. v2sim/gui/mainbox/controls/utils.py +40 -0
  24. v2sim/gui/mainbox/cscsveditor.py +80 -0
  25. v2sim/gui/mainbox/cseditor.py +375 -0
  26. v2sim/gui/mainbox/loadingbox.py +33 -0
  27. v2sim/gui/mainbox/plugin.py +68 -0
  28. v2sim/gui/mainbox/utils.py +42 -0
  29. v2sim/gui/parabox/__init__.py +172 -0
  30. v2sim/gui/parabox/_lang/en.lang +51 -0
  31. v2sim/gui/parabox/_lang/zh_CN.lang +51 -0
  32. v2sim/gui/parabox/lgb.py +78 -0
  33. v2sim/gui/parabox/pareditor.py +53 -0
  34. v2sim/gui/parabox/utils.py +33 -0
  35. v2sim/gui/plgbox/__init__.py +150 -0
  36. v2sim/gui/plgbox/_lang/en_US.lang +19 -0
  37. v2sim/gui/plgbox/_lang/zh_CN.lang +19 -0
  38. v2sim/gui/progbox/__init__.py +45 -0
  39. v2sim/gui/v2sim.png +0 -0
  40. v2sim/gui/viewerbox/__init__.py +465 -0
  41. v2sim/gui/viewerbox/_lang/en_US.lang +95 -0
  42. v2sim/gui/viewerbox/_lang/zh_CN.lang +95 -0
  43. v2sim/gui/viewerbox/gridpage.py +89 -0
  44. v2sim/gui/viewerbox/optbox.py +46 -0
  45. v2sim/gui/viewerbox/plotpad.py +45 -0
  46. v2sim/gui/viewerbox/plotpage.py +225 -0
  47. v2sim/gui/viewerbox/srd.py +39 -0
  48. v2sim/gui/viewerbox/statepage.py +104 -0
  49. v2sim/gui/viewerbox/trips.py +284 -0
  50. v2sim/gui/welcomebox/__init__.py +225 -0
  51. v2sim/gui/welcomebox/_lang/en_US.lang +22 -0
  52. v2sim/gui/welcomebox/_lang/zh_CN.lang +22 -0
  53. v2sim/gui/welcomebox/v2sim.png +0 -0
  54. v2sim/locale/__init__.py +1 -0
  55. v2sim/locale/lang.py +264 -0
  56. v2sim/locale/zh_CN.py +207 -0
  57. v2sim/osmhelper/__init__.py +1 -0
  58. v2sim/osmhelper/osmBuild.py +140 -0
  59. v2sim/osmhelper/osmGet.py +324 -0
  60. v2sim/osmhelper/osmWebWizard.py +551 -0
  61. v2sim/osmhelper/ptlines2flows.py +610 -0
  62. v2sim/osmhelper/readme.md +2 -0
  63. v2sim/osmhelper/tileGet.py +283 -0
  64. v2sim/osmhelper/typemap/navteqPolyconvert.typ.xml +9 -0
  65. v2sim/osmhelper/typemap/opendriveNetconvert.typ.xml +35 -0
  66. v2sim/osmhelper/typemap/opendriveNetconvertBicycle.typ.xml +3 -0
  67. v2sim/osmhelper/typemap/opendriveNetconvertPedestrians.typ.xml +3 -0
  68. v2sim/osmhelper/typemap/osmNetconvert.typ.xml +45 -0
  69. v2sim/osmhelper/typemap/osmNetconvertAerialway.typ.xml +5 -0
  70. v2sim/osmhelper/typemap/osmNetconvertAirport.typ.xml +9 -0
  71. v2sim/osmhelper/typemap/osmNetconvertBicycle.typ.xml +6 -0
  72. v2sim/osmhelper/typemap/osmNetconvertBidiRail.typ.xml +7 -0
  73. v2sim/osmhelper/typemap/osmNetconvertExtraRail.typ.xml +5 -0
  74. v2sim/osmhelper/typemap/osmNetconvertPedestrians.typ.xml +10 -0
  75. v2sim/osmhelper/typemap/osmNetconvertPedestriansNES.typ.xml +11 -0
  76. v2sim/osmhelper/typemap/osmNetconvertRailUsage.typ.xml +17 -0
  77. v2sim/osmhelper/typemap/osmNetconvertShips.typ.xml +5 -0
  78. v2sim/osmhelper/typemap/osmNetconvertUrbanDe.typ.xml +13 -0
  79. v2sim/osmhelper/typemap/osmPolyconvert.typ.xml +66 -0
  80. v2sim/osmhelper/typemap/osmPolyconvertRail.typ.xml +50 -0
  81. v2sim/osmhelper/typemap/visumPolyconvert.typ.xml +8 -0
  82. v2sim/osmhelper/webWizard/SimpleWebSocketServer.py +716 -0
  83. v2sim/osmhelper/webWizard/__init__.py +17 -0
  84. v2sim/osmhelper/webWizard/images/bicycle.png +0 -0
  85. v2sim/osmhelper/webWizard/images/bus.png +0 -0
  86. v2sim/osmhelper/webWizard/images/favicon.ico +0 -0
  87. v2sim/osmhelper/webWizard/images/generate.png +0 -0
  88. v2sim/osmhelper/webWizard/images/map.png +0 -0
  89. v2sim/osmhelper/webWizard/images/motorcycle.png +0 -0
  90. v2sim/osmhelper/webWizard/images/passenger.png +0 -0
  91. v2sim/osmhelper/webWizard/images/pedestrian.png +0 -0
  92. v2sim/osmhelper/webWizard/images/rail.png +0 -0
  93. v2sim/osmhelper/webWizard/images/rail_urban.png +0 -0
  94. v2sim/osmhelper/webWizard/images/road.png +0 -0
  95. v2sim/osmhelper/webWizard/images/ship.png +0 -0
  96. v2sim/osmhelper/webWizard/images/tram.png +0 -0
  97. v2sim/osmhelper/webWizard/images/truck.png +0 -0
  98. v2sim/osmhelper/webWizard/index.html +138 -0
  99. v2sim/osmhelper/webWizard/jquery-3.5.1.min.js +2 -0
  100. v2sim/osmhelper/webWizard/lib.js +62 -0
  101. v2sim/osmhelper/webWizard/script.js +540 -0
  102. v2sim/osmhelper/webWizard/style.css +214 -0
  103. v2sim/plotkit/__init__.py +2 -0
  104. v2sim/plotkit/example.txt +16 -0
  105. v2sim/plotkit/plot.py +728 -0
  106. v2sim/plotkit/reader.py +251 -0
  107. v2sim/plugins/__init__.py +9 -0
  108. v2sim/plugins/base.py +278 -0
  109. v2sim/plugins/ocur.py +94 -0
  110. v2sim/plugins/pdn.py +182 -0
  111. v2sim/plugins/pool.py +158 -0
  112. v2sim/plugins/v2g.py +91 -0
  113. v2sim/probtable/duration_of_parking/H.csv +96 -0
  114. v2sim/probtable/duration_of_parking/H_spr.csv +96 -0
  115. v2sim/probtable/duration_of_parking/H_spr_weekday.csv +96 -0
  116. v2sim/probtable/duration_of_parking/H_spr_weekend.csv +96 -0
  117. v2sim/probtable/duration_of_parking/O_spr.csv +96 -0
  118. v2sim/probtable/duration_of_parking/O_spr_weekday.csv +96 -0
  119. v2sim/probtable/duration_of_parking/O_spr_weekend.csv +96 -0
  120. v2sim/probtable/duration_of_parking/R_spr.csv +96 -0
  121. v2sim/probtable/duration_of_parking/R_spr_weekday.csv +96 -0
  122. v2sim/probtable/duration_of_parking/R_spr_weekend.csv +96 -0
  123. v2sim/probtable/duration_of_parking/W_spr.csv +96 -0
  124. v2sim/probtable/duration_of_parking/W_spr_weekday.csv +96 -0
  125. v2sim/probtable/duration_of_parking/W_spr_weekend.csv +96 -0
  126. v2sim/probtable/ev_types.csv +7 -0
  127. v2sim/probtable/soc_dist.csv +101 -0
  128. v2sim/probtable/space_transfer_probability/H_spr_weekday.csv +5 -0
  129. v2sim/probtable/space_transfer_probability/H_spr_weekend.csv +5 -0
  130. v2sim/probtable/space_transfer_probability/O_spr_weekday.csv +5 -0
  131. v2sim/probtable/space_transfer_probability/O_spr_weekend.csv +5 -0
  132. v2sim/probtable/space_transfer_probability/R_spr_weekday.csv +5 -0
  133. v2sim/probtable/space_transfer_probability/R_spr_weekend.csv +5 -0
  134. v2sim/probtable/space_transfer_probability/W_spr_weekday.csv +5 -0
  135. v2sim/probtable/space_transfer_probability/W_spr_weekend.csv +5 -0
  136. v2sim/sim_core.py +744 -0
  137. v2sim/statistics/__init__.py +10 -0
  138. v2sim/statistics/base.py +81 -0
  139. v2sim/statistics/logcs.py +75 -0
  140. v2sim/statistics/logev.py +45 -0
  141. v2sim/statistics/loggr.py +174 -0
  142. v2sim/statistics/manager.py +210 -0
  143. v2sim/tools/cmd_advplot.py +24 -0
  144. v2sim/tools/cmd_csquery.py +24 -0
  145. v2sim/tools/cmd_gen_cs.py +27 -0
  146. v2sim/tools/cmd_gen_trip.py +29 -0
  147. v2sim/tools/cmd_plot.py +198 -0
  148. v2sim/tools/gui_cmp.py +6 -0
  149. v2sim/tools/gui_main.py +44 -0
  150. v2sim/tools/gui_osm.py +7 -0
  151. v2sim/tools/gui_para.py +5 -0
  152. v2sim/tools/gui_plgman.py +10 -0
  153. v2sim/tools/gui_viewer.py +5 -0
  154. v2sim/tools/sim_para.py +278 -0
  155. v2sim/tools/sim_single.py +122 -0
  156. v2sim/traffic/__init__.py +9 -0
  157. v2sim/traffic/cs.py +672 -0
  158. v2sim/traffic/cslist.py +336 -0
  159. v2sim/traffic/ev.py +460 -0
  160. v2sim/traffic/evdict.py +85 -0
  161. v2sim/traffic/inst.py +712 -0
  162. v2sim/traffic/net.py +390 -0
  163. v2sim/traffic/params.py +81 -0
  164. v2sim/traffic/seg.py +231 -0
  165. v2sim/traffic/trip.py +435 -0
  166. v2sim/traffic/utils.py +345 -0
  167. v2sim/traffic/win_vis.py +1 -0
  168. v2sim/trafficgen/__init__.py +9 -0
  169. v2sim/trafficgen/core.py +508 -0
  170. v2sim/trafficgen/csquery.py +213 -0
  171. v2sim/trafficgen/misc.py +214 -0
  172. v2sim/trafficgen/poly.py +67 -0
  173. v2sim/trafficgen/tripgen.py +440 -0
  174. v2sim-1.3.0.dist-info/METADATA +73 -0
  175. v2sim-1.3.0.dist-info/RECORD +178 -0
  176. v2sim-1.3.0.dist-info/WHEEL +4 -0
  177. v2sim-1.3.0.dist-info/entry_points.txt +11 -0
  178. v2sim-1.3.0.dist-info/licenses/LICENSE +29 -0
v2sim/__init__.py ADDED
@@ -0,0 +1,21 @@
1
+ from .locale import *
2
+ from .traffic import *
3
+ from .trafficgen import *
4
+ from .plotkit import *
5
+ from .plugins import *
6
+ from .statistics import *
7
+ from .sim_core import (
8
+ load_external_components,
9
+ get_sim_params,
10
+ simulate_multi,
11
+ simulate_single,
12
+ V2SimInstance,
13
+ MsgPack,
14
+ PLUGINS_FILE,
15
+ RESULTS_FOLDER,
16
+ TRIP_EVENT_LOG,
17
+ SIM_INFO_LOG,
18
+ PLUGINS_DIR,
19
+ )
20
+
21
+ __version__ = "1.3.0"
@@ -0,0 +1,152 @@
1
+ from v2sim.gui.com_no_vx import *
2
+ from v2sim.gui.langhelper import *
3
+
4
+ import os
5
+ from PIL import Image, ImageTk
6
+
7
+
8
+ _ = LangLib.Load(__file__)
9
+
10
+
11
+ class CmpBox(Tk):
12
+ def __init__(self):
13
+ super().__init__()
14
+
15
+ self.title(_("TITLE"))
16
+ self.geometry("1024x768")
17
+ self.bind("<Configure>", self.on_resize)
18
+
19
+ self.folder1 = None
20
+ self.folder2 = None
21
+ self.original_image1 = None
22
+ self.original_image2 = None
23
+ self.folder_buf = "./results"
24
+
25
+ self.create_widgets()
26
+
27
+ def create_widgets(self):
28
+ self.menu = Menu(self)
29
+ self.config(menu=self.menu)
30
+ self.filemenu = Menu(self.menu, tearoff=0)
31
+ self.filemenu.add_command(label=_("OPEN_FOLDER1"), command=self.open_folder1)
32
+ self.filemenu.add_command(label=_("OPEN_FOLDER2"), command=self.open_folder2)
33
+ self.filemenu.add_separator()
34
+ self.filemenu.add_command(label=_("EXIT"), command=self.destroy)
35
+ self.menu.add_cascade(label=_("FILE"), menu=self.filemenu)
36
+ add_lang_menu(self.menu)
37
+
38
+ self.sidebar = Frame(self)
39
+ self.sidebar.pack(side=LEFT, fill=Y)
40
+
41
+ self.folder1_button = Button(self.sidebar, text=_("OPEN_FOLDER1"), command=self.open_folder1)
42
+ self.folder1_button.pack(pady=10)
43
+
44
+ self.folder2_button = Button(self.sidebar, text=_("OPEN_FOLDER2"), command=self.open_folder2)
45
+ self.folder2_button.pack(pady=10)
46
+
47
+ self.folder1_label = Label(self.sidebar, text=_("LB_FOLDER1").format(_("TO_BE_SELECTED")))
48
+ self.folder1_label.pack(pady=10)
49
+
50
+ self.folder2_label = Label(self.sidebar, text=_("LB_FOLDER2").format(_("TO_BE_SELECTED")))
51
+ self.folder2_label.pack(pady=10)
52
+
53
+ self.file_listbox = Listbox(self.sidebar)
54
+ self.file_listbox.pack(fill=BOTH, expand=True, pady=10)
55
+ self.file_listbox.bind("<<ListboxSelect>>", self.on_file_select)
56
+
57
+ self.image_frame = Frame(self)
58
+ self.image_frame.pack(side=RIGHT, fill=X, expand=True)
59
+
60
+ self.image1_label = Label(self.image_frame)
61
+ self.image1_label.pack(side=TOP, padx=10, pady=10)
62
+
63
+ self.image2_label = Label(self.image_frame)
64
+ self.image2_label.pack(side=TOP, padx=10, pady=10)
65
+
66
+ def open_folder1(self):
67
+ new_folder = filedialog.askdirectory(initialdir=self.folder_buf, title=_("AD_TITLE1"))
68
+ if new_folder:
69
+ folder_fig = os.path.join(new_folder, "figures")
70
+ if os.path.exists(folder_fig):
71
+ self.folder1 = folder_fig
72
+ self.folder1_label.config(text=_("LB_FOLDER1").format(os.path.basename(new_folder)))
73
+ self.update_file_list()
74
+ self.folder_buf = str(Path(new_folder).parent)
75
+ else:
76
+ MB.showerror(_("ERROR"), _("NO_FIG"))
77
+
78
+ def open_folder2(self):
79
+ new_folder = filedialog.askdirectory(initialdir=self.folder_buf, title=_("AD_TITLE2"))
80
+ if new_folder:
81
+ folder_fig = os.path.join(new_folder, "figures")
82
+ if os.path.exists(folder_fig):
83
+ self.folder2 = folder_fig
84
+ self.folder2_label.config(text=_("LB_FOLDER2").format(os.path.basename(new_folder)))
85
+ self.update_file_list()
86
+ self.folder_buf = str(Path(new_folder).parent)
87
+ else:
88
+ MB.showerror(_("ERROR"), _("NO_FIG"))
89
+
90
+ def update_file_list(self):
91
+ self.file_listbox.delete(0, END)
92
+ if self.folder1 and self.folder2:
93
+ files1 = set(os.listdir(self.folder1))
94
+ files2 = set(os.listdir(self.folder2))
95
+ common_files = files1.union(files2)
96
+ for file in sorted(common_files):
97
+ if file.lower().endswith(('png', 'jpg', 'jpeg', 'gif')): # 只列出图片文件
98
+ self.file_listbox.insert(END, file)
99
+
100
+ def on_file_select(self, event):
101
+ selected_index = self.file_listbox.curselection()
102
+ if selected_index:
103
+ file_name = self.file_listbox.get(selected_index)
104
+ self.display_images(file_name)
105
+
106
+ def resize(self):
107
+ sz = (self.winfo_width() - 200, self.winfo_height() // 2 - 20)
108
+ if self.original_image1 is not None:
109
+ resized_image1 = self.original_image1.copy()
110
+ resized_image1.thumbnail(sz)
111
+ image1 = ImageTk.PhotoImage(resized_image1)
112
+
113
+ self.image1_label.config(image=image1,text="")
114
+ self.image1 = image1
115
+ else:
116
+ self.image1_label.config(image='',text=_("NO_IMAGE"))
117
+ self.image1 = None
118
+
119
+ if self.original_image2 is not None:
120
+ resized_image2 = self.original_image2.copy()
121
+ resized_image2.thumbnail(sz)
122
+ image2 = ImageTk.PhotoImage(resized_image2)
123
+
124
+ self.image2_label.config(image=image2,text="")
125
+ self.image2 = image2
126
+ else:
127
+ self.image2_label.config(image='',text=_("NO_IMAGE"))
128
+ self.image2 = None
129
+
130
+
131
+ def on_resize(self, event):
132
+ self.resize()
133
+
134
+ def display_images(self, file_name:str):
135
+ if self.folder1 is None: return
136
+ img1_path = os.path.join(self.folder1, file_name)
137
+ if self.folder2 is None: return
138
+ img2_path = os.path.join(self.folder2, file_name)
139
+
140
+ try:
141
+ if os.path.exists(img1_path):
142
+ self.original_image1 = Image.open(img1_path)
143
+ else:
144
+ self.original_image1 = None
145
+ if os.path.exists(img2_path):
146
+ self.original_image2 = Image.open(img2_path)
147
+ else:
148
+ self.original_image2 = None
149
+ except Exception as e:
150
+ MB.showerror(_("ERROR"), _("LOAD_FAILED").format(str(e)))
151
+
152
+ self.resize()
@@ -0,0 +1,14 @@
1
+ TITLE=Case comparer
2
+ ERROR=Error
3
+ OPEN_FOLDER1=Open folder 1
4
+ OPEN_FOLDER2=Open folder 2
5
+ TO_BE_SELECTED=(To be selected)
6
+ LB_FOLDER1=Folder 1: {}
7
+ LB_FOLDER2=Folder 2: {}
8
+ AD_TITLE1=Select folder 1
9
+ AD_TITLE2=Select folder 2
10
+ NO_FIG=No 'figures' folder in the selected folder! Please plot the data with gui_viewer.py first!
11
+ LOAD_FAILED=Cannot load image: {}
12
+ NO_IMAGE=(No image)
13
+ EXIT=Exit
14
+ FILE=File
@@ -0,0 +1,14 @@
1
+ TITLE=案例比较器
2
+ ERROR=错误
3
+ OPEN_FOLDER1=打开文件夹1
4
+ OPEN_FOLDER2=打开文件夹2
5
+ TO_BE_SELECTED=未选择
6
+ LB_FOLDER1=文件夹1: {}
7
+ LB_FOLDER2=文件夹2: {}
8
+ AD_TITLE1=选择文件夹1
9
+ AD_TITLE2=选择文件夹2
10
+ NO_FIG=所选文件夹中没有figures文件夹! 请先使用gui_viewer.py画图!
11
+ LOAD_FAILED=无法加载图片: {}
12
+ NO_IMAGE=无图像
13
+ EXIT=退出
14
+ FILE=文件
v2sim/gui/com_no_vx.py ADDED
@@ -0,0 +1,33 @@
1
+ # Load Path
2
+ from pathlib import Path
3
+
4
+ # Load Event Queue
5
+ from .evtq import EventQueue
6
+
7
+ # Load Language Library
8
+ from feasytools import LangLib, LangConfig
9
+ LangConfig.SetAppName("v2sim")
10
+
11
+ # High DPI awareness for Windows
12
+ import platform
13
+ if platform.system() == "Windows":
14
+ import ctypes
15
+ ctypes.windll.shcore.SetProcessDpiAwareness(1)
16
+
17
+ # Load Tk controls
18
+ from tkinter import (
19
+ Toplevel, messagebox as MB, BooleanVar, StringVar, IntVar, Canvas, Event, Tk, Menu, filedialog, Text, Listbox, PhotoImage, Widget,
20
+ NO, YES, NORMAL, DISABLED, END, BOTH, X, Y, LEFT, RIGHT, TOP, BOTTOM, W, E
21
+ )
22
+ from tkinter.ttk import Treeview, Button, LabelFrame, Checkbutton, Combobox, Frame, Label, Entry, Spinbox, Scrollbar, Radiobutton, Notebook, OptionMenu
23
+
24
+ # Load Type Hints
25
+ from typing import Dict, List, Set, Tuple, Any, Union, Optional, Iterable, Callable, Literal
26
+
27
+ # Set exports
28
+ __all__ = [
29
+ "LangLib", "LangConfig", "EventQueue", "Path", "Dict", "List", "Set", "Tuple", "Any", "Union", "Optional", "Iterable", "Callable", "Literal",
30
+ "Toplevel", "MB", "BooleanVar", "StringVar", "IntVar", "Canvas", "Event", "Tk", "Menu", "filedialog", "Text", "Listbox", "PhotoImage", "Widget",
31
+ "NO", "YES", "NORMAL", "DISABLED", "END", "X", "Y", "BOTH", "LEFT", "RIGHT", "TOP", "BOTTOM", "W", "E", "platform",
32
+ "Treeview", "Button", "LabelFrame", "Checkbutton", "Combobox", "Frame", "Label", "Entry", "Spinbox", "Scrollbar", "Radiobutton", "Notebook", "OptionMenu",
33
+ ]
v2sim/gui/common.py ADDED
@@ -0,0 +1,5 @@
1
+ from .com_no_vx import *
2
+
3
+ # Load V2Sim common items
4
+ from v2sim import ConfigItem, EditMode, ConfigItemDict, ConfigDict, CONFIG_DIR
5
+ CONFIG_DIR.mkdir(parents=True, exist_ok=True)
v2sim/gui/evtq.py ADDED
@@ -0,0 +1,100 @@
1
+ import threading
2
+ import time
3
+ import traceback
4
+ from collections import deque
5
+ from queue import Queue
6
+ from typing import Callable, Dict, Tuple
7
+
8
+ class EventQueue:
9
+ def __init__(self, parent, maxsize:int = 0, interval_ms:int = 100, max_proc_ms:int = 90):
10
+ self._Q = Queue(maxsize)
11
+ self._parent = parent
12
+ self._evt:Dict[str, Callable] = {}
13
+ self._interval_ms = interval_ms
14
+ self._max_proc_ns = max_proc_ms * 1e6 # Convert to nanoseconds
15
+ self._delegates:deque[Tuple[Callable, Tuple, Dict]] = deque()
16
+ self.do() # Start processing events immediately
17
+
18
+ def do(self):
19
+ """Process all events in the queue."""
20
+ prod_next = True
21
+ st = time.time_ns()
22
+
23
+ cnt = 0
24
+ while self._delegates:
25
+ func, args, kwargs = self._delegates.popleft()
26
+ try:
27
+ func(*args, **kwargs)
28
+ except Exception as e:
29
+ print(f"Error in delegate function '{func.__name__}': {e}")
30
+
31
+ cnt = 0
32
+ while not self._Q.empty() and (time.time_ns() - st < self._max_proc_ns) and cnt < 100:
33
+ name, args, kwargs = self._Q.get()
34
+ if name == "__quit__":
35
+ self._Q.task_done()
36
+ prod_next = False
37
+ break
38
+ if name in self._evt and self._evt[name] is not None:
39
+ try:
40
+ self._evt[name](*args, **kwargs)
41
+ except Exception as e:
42
+ traceback.print_exc()
43
+ print(f"Error processing event '{name}': {e}")
44
+ else:
45
+ print(f"Event '{name}' is not registered.")
46
+ self._Q.task_done()
47
+ self._parent.update() # Ensure the GUI updates after processing each event
48
+ cnt += 1
49
+ if prod_next:
50
+ intv = self._interval_ms
51
+ if cnt >= 100:
52
+ intv = max(intv // 10, 1)
53
+ self._parent.after(intv, self.do)
54
+
55
+ def register(self, name:str, callback:Callable):
56
+ """Register an event handler for a specific event name."""
57
+ if name in self._evt:
58
+ raise ValueError(f"Event '{name}' is already registered.")
59
+ self._evt[name] = callback
60
+
61
+ def setcallback(self, name:str, callback:Callable):
62
+ """Set or update the callback for an event."""
63
+ if name not in self._evt:
64
+ raise ValueError(f"Event '{name}' is not registered.")
65
+ self._evt[name] = callback
66
+
67
+ def trigger(self, name:str, *args, **kwargs):
68
+ """Trigger an event by its name with optional arguments."""
69
+ if name not in self._evt:
70
+ raise ValueError(f"Event '{name}' is not registered.")
71
+ self._Q.put((name, args, kwargs))
72
+
73
+ def submit(self, name:str, func:Callable, *args, **kwargs):
74
+ """Run a function asychoronously and submit the results to trigger an event."""
75
+ def _run_and_trigger(name, func, *args, **kwargs):
76
+ try:
77
+ result = func(*args, **kwargs)
78
+ if result is None:
79
+ result = ()
80
+ elif not isinstance(result, tuple):
81
+ result = (result,)
82
+ self.trigger(name, *result)
83
+ except Exception as e:
84
+ traceback.print_exc()
85
+ print(f"Error in submitting function '{func.__name__}' for event '{name}': {e}")
86
+ threading.Thread(target=_run_and_trigger, args=(name, func, *args), kwargs=kwargs).start()
87
+
88
+ def asyncrun(self, func:Callable, *args, **kwargs):
89
+ """Run a no-return function asynchronously"""
90
+ def _run(func, *args, **kwargs):
91
+ try:
92
+ func(*args, **kwargs)
93
+ except Exception as e:
94
+ traceback.print_exc()
95
+ print(f"Error in delegating function '{func.__name__}': {e}")
96
+ threading.Thread(target=_run, args=(func, *args), kwargs=kwargs).start()
97
+
98
+ def delegate(self, func:Callable, *args, **kwargs):
99
+ """Run a no-return function on the main thread."""
100
+ self._delegates.append((func, args, kwargs))
@@ -0,0 +1,34 @@
1
+ from v2sim.gui.com_no_vx import *
2
+
3
+ _ = LangLib(["zh_CN", "en_US"])
4
+ _.SetLangLib("zh_CN",
5
+ MB_INFO = "信息",
6
+ MENU_LANG = "语言",
7
+ MENU_LANG_AUTO = "(自动检测)",
8
+ LANG_RESTART = "语言已更改,请重启程序以应用更改。",
9
+ )
10
+
11
+ _.SetLangLib("en_US",
12
+ MB_INFO = "Information",
13
+ MENU_LANG = "Language",
14
+ MENU_LANG_AUTO = "(Auto Detect)",
15
+ LANG_RESTART = "Language has been changed. Please restart the application to apply the changes.",
16
+ )
17
+
18
+ def setLang(lang_code:str):
19
+ title = _["MB_INFO"]
20
+ info = _["LANG_RESTART"]
21
+ def _f():
22
+ nonlocal lang_code, title, info
23
+ LangConfig.SetLangCode(lang_code)
24
+ MB.showinfo(title, info)
25
+ return _f
26
+
27
+ def add_lang_menu(parent: Menu):
28
+ menuLang = Menu(parent, tearoff=False)
29
+ parent.add_cascade(label=_["MENU_LANG"], menu=menuLang)
30
+ menuLang.add_command(label=_["MENU_LANG_AUTO"], command=setLang("<auto>"))
31
+ menuLang.add_command(label="English (United States)", command=setLang("en_US"))
32
+ menuLang.add_command(label="中文 (简体)", command=setLang("zh_CN"))
33
+
34
+ __all__ = ["add_lang_menu"]