mg-pso-gui 0.1.172__py3-none-any.whl → 0.1.175__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.
- {mg_pso_gui-0.1.172.dist-info → mg_pso_gui-0.1.175.dist-info}/METADATA +1 -1
- {mg_pso_gui-0.1.172.dist-info → mg_pso_gui-0.1.175.dist-info}/RECORD +11 -10
- mgpsogui/gui/HomePage.py +7 -0
- mgpsogui/gui/OptionManager.py +12 -1
- mgpsogui/gui/VisualizeTab/MatrixEditor.py +64 -0
- mgpsogui/gui/VisualizeTab/SideBar.py +77 -0
- mgpsogui/gui/VisualizeTab/VisualizeTab.py +1 -1
- mgpsogui/util/GraphGenerator.py +20 -2
- {mg_pso_gui-0.1.172.dist-info → mg_pso_gui-0.1.175.dist-info}/WHEEL +0 -0
- {mg_pso_gui-0.1.172.dist-info → mg_pso_gui-0.1.175.dist-info}/entry_points.txt +0 -0
- {mg_pso_gui-0.1.172.dist-info → mg_pso_gui-0.1.175.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,8 @@
|
|
1
1
|
mgpsogui/__init__.py,sha256=q7AfBjeJABnFtbsZnsObpUwaXKPDVYtz46G6MKXLF74,42
|
2
2
|
mgpsogui/mgpsogui.py,sha256=NIZmyNcbwC8EgSwf1ubdMUSJscrIEgoD4jLYziqHQ-k,148
|
3
3
|
mgpsogui/start.yaml,sha256=ZjCVLb-MLqAxrGRm9kA7_SDpa-45EuKIELNQ2QqCAiU,4713
|
4
|
-
mgpsogui/gui/HomePage.py,sha256=
|
5
|
-
mgpsogui/gui/OptionManager.py,sha256=
|
4
|
+
mgpsogui/gui/HomePage.py,sha256=cnqM5NspTVaujmJd3dg7qidUeWctEzp-9mzR-RM52k8,22730
|
5
|
+
mgpsogui/gui/OptionManager.py,sha256=gGRwwzBMN7Bu9ATugWiM9OG7BRpa-mvhNQatjqUU1Ig,12946
|
6
6
|
mgpsogui/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
7
|
mgpsogui/gui/PlatformTab/PlatformTab.py,sha256=q8x_tvVgSoyGINuQbivXRThF3gaSSfg8X_dsLSPv4F0,10492
|
8
8
|
mgpsogui/gui/PlatformTab/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -18,8 +18,9 @@ mgpsogui/gui/SetupTab/SetupTab.py,sha256=8f_xqmMaD8grmSKFFUHm8zIGEDrSUzU_sNW6CdR
|
|
18
18
|
mgpsogui/gui/SetupTab/StaticParameterView.py,sha256=iEG-UpBBlAJabZo3MG768oLqOROjUPc23tKOSd47IUc,2739
|
19
19
|
mgpsogui/gui/SetupTab/StepView.py,sha256=_gdfiYr_GqqbQfSYLvdllIYCHdGsy1339cALfYLDAEQ,5909
|
20
20
|
mgpsogui/gui/SetupTab/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
21
|
-
mgpsogui/gui/VisualizeTab/
|
22
|
-
mgpsogui/gui/VisualizeTab/
|
21
|
+
mgpsogui/gui/VisualizeTab/MatrixEditor.py,sha256=a7_j7I5yGqtr-HYso4FUzCGEhi0WR2HjwCEky2T0fic,2849
|
22
|
+
mgpsogui/gui/VisualizeTab/SideBar.py,sha256=4m3LtrnsLgJ9SthlQGmFbysyhept5bTK6ilGaVCBurU,21925
|
23
|
+
mgpsogui/gui/VisualizeTab/VisualizeTab.py,sha256=_SiRDUvYJ7VUmMZ_VzylWP2tFygbLzWeUCGW3TU3yZM,3466
|
23
24
|
mgpsogui/gui/VisualizeTab/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
24
25
|
mgpsogui/gui/images/IGOW 4 Logo.png,sha256=JixNXz5gOEj898VF-_PHthAlGU-6W-y9ucb4EVNPtjs,433752
|
25
26
|
mgpsogui/gui/images/collapse.png,sha256=yicb16LaY7Nl5q9V8HHlht-Fbg5xU-l4_LJ_Arfmu1c,2989
|
@@ -32,7 +33,7 @@ mgpsogui/gui/images/stop.png,sha256=JPuxXQerCGpLikcp7cAj3iLCOjULMYYZ2sZe0lArh68,
|
|
32
33
|
mgpsogui/gui/images/test.png,sha256=MUnVpRK-isxhEHzx4Q6Yh0M6FRZD1qvgCHH2XmiSBbk,3642
|
33
34
|
mgpsogui/gui/images/trash.png,sha256=j8cf0kWbJd-4Jp20lUVV1o1NSeQ4v1Ej4gfcIA3DVRQ,2958
|
34
35
|
mgpsogui/gui/images/up.png,sha256=AQvFWCUqSQNaQ1E6LKZ9zNfSvW6t4mgy8uswdg9T2Hg,2457
|
35
|
-
mgpsogui/util/GraphGenerator.py,sha256=
|
36
|
+
mgpsogui/util/GraphGenerator.py,sha256=mqbFEy5BcpXButIUFtI62cJ1gUHQhN6J3R2ttORiuK4,18058
|
36
37
|
mgpsogui/util/PSORunner.py,sha256=sWgickFGl97p3ybWz97LR5qeYhnNqryKx5nWAa-NhwA,6306
|
37
38
|
mgpsogui/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
38
39
|
mgpsogui/util/debug.py,sha256=Swzny9_1S-CCONWGssj9iDoSpbwu5Vr4f95g_9L8bec,19759
|
@@ -58,8 +59,8 @@ mgpsogui/util/recosu/utils/trace_writer.py,sha256=V9BJlOjCbNYGoXGEk3CF5wjifBxvar
|
|
58
59
|
mgpsogui/util/recosu/utils/utils.py,sha256=QB8vftq3142ekG0ORjz0ZBHU5YknXbR0oTsrxrPAsF0,3951
|
59
60
|
mgpsogui/util/recosu/utils/plot/__init__.py,sha256=h1KjM7_tNDv351pcwt8A6Ibb1jhwWyx5Gbu-zj-sI3Q,71
|
60
61
|
mgpsogui/util/recosu/utils/plot/cost_steps.py,sha256=1Ce11AJyweWkmvjXPxEygzS-h8yVLmQEDLS53yjPLqQ,3779
|
61
|
-
mg_pso_gui-0.1.
|
62
|
-
mg_pso_gui-0.1.
|
63
|
-
mg_pso_gui-0.1.
|
64
|
-
mg_pso_gui-0.1.
|
65
|
-
mg_pso_gui-0.1.
|
62
|
+
mg_pso_gui-0.1.175.dist-info/METADATA,sha256=gdqL-2HHJyI9G3ziO4mYvrYo-3s7m-OUg7UEVAA4GPU,9456
|
63
|
+
mg_pso_gui-0.1.175.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
64
|
+
mg_pso_gui-0.1.175.dist-info/entry_points.txt,sha256=jg82VOFjR1XDGrchs1wJSCqKYE4Ozv12aBcCSp--koA,117
|
65
|
+
mg_pso_gui-0.1.175.dist-info/top_level.txt,sha256=y7JuS9xJN5YdxUsQ3PSVjN8MzQAnR146bP3ZN3PYWdE,9
|
66
|
+
mg_pso_gui-0.1.175.dist-info/RECORD,,
|
mgpsogui/gui/HomePage.py
CHANGED
@@ -85,6 +85,13 @@ class App(customtkinter.CTk):
|
|
85
85
|
self.selected_y2 = tk.StringVar()
|
86
86
|
self.selected_y2.set("NONE")
|
87
87
|
|
88
|
+
self.figure_style = tk.StringVar()
|
89
|
+
self.figure_style.set("Scatter")
|
90
|
+
|
91
|
+
self.matrix_values = []
|
92
|
+
self.matrix_values.append(tk.StringVar())
|
93
|
+
self.matrix_values[0].set("NONE")
|
94
|
+
|
88
95
|
self.running_config = None
|
89
96
|
self.selected_graph_name = None
|
90
97
|
|
mgpsogui/gui/OptionManager.py
CHANGED
@@ -13,7 +13,8 @@ class OptionManager():
|
|
13
13
|
"url": sv(),
|
14
14
|
"mode": sv(),
|
15
15
|
"files": {},
|
16
|
-
"calibration_parameters": []
|
16
|
+
"calibration_parameters": [],
|
17
|
+
"figure_parameters": []}
|
17
18
|
self.steps = []
|
18
19
|
self.service_parameters = {}
|
19
20
|
|
@@ -23,6 +24,7 @@ class OptionManager():
|
|
23
24
|
self.arguments['mode'].set("Optimization: MG-PSO")
|
24
25
|
self.arguments['files'] = {}
|
25
26
|
self.arguments['calibration_parameters'].clear()
|
27
|
+
self.arguments['figure_parameters'].clear()
|
26
28
|
self.steps = []
|
27
29
|
self.service_parameters = {}
|
28
30
|
|
@@ -183,6 +185,12 @@ class OptionManager():
|
|
183
185
|
obj["name"].set(key)
|
184
186
|
obj["value"].set(value)
|
185
187
|
self.arguments["calibration_parameters"].append(obj)
|
188
|
+
|
189
|
+
def add_figure_param(self, key, value):
|
190
|
+
obj = {"name": sv(), "value": sv()}
|
191
|
+
obj["name"].set(key)
|
192
|
+
obj["value"].set(value)
|
193
|
+
self.arguments["figure_parameters"].append(obj)
|
186
194
|
|
187
195
|
def move_argument_up(self, index):
|
188
196
|
if index > 0:
|
@@ -208,6 +216,9 @@ class OptionManager():
|
|
208
216
|
|
209
217
|
def remove_calibration_parameter(self, index):
|
210
218
|
self.arguments["calibration_parameters"].pop(index)
|
219
|
+
|
220
|
+
def remove_figure_parameter(self, index):
|
221
|
+
self.arguments["figure_parameters"].pop(index)
|
211
222
|
|
212
223
|
def remove_step(self, index):
|
213
224
|
self.steps.pop(index)
|
@@ -0,0 +1,64 @@
|
|
1
|
+
from customtkinter import CTkScrollableFrame
|
2
|
+
from customtkinter import CTkFrame
|
3
|
+
from customtkinter import CTkLabel
|
4
|
+
from customtkinter import CTkButton
|
5
|
+
from customtkinter import CTkEntry
|
6
|
+
from customtkinter import CTkOptionMenu
|
7
|
+
import tkinter as tk
|
8
|
+
|
9
|
+
global option_manager
|
10
|
+
|
11
|
+
class MatrixEditor(CTkScrollableFrame):
|
12
|
+
def __init__(self, *args,
|
13
|
+
option_manager: None,
|
14
|
+
home_page: None,
|
15
|
+
columns: None,
|
16
|
+
**kwargs):
|
17
|
+
super().__init__(*args, **kwargs)
|
18
|
+
|
19
|
+
self.option_manager = option_manager
|
20
|
+
self.home_page = home_page
|
21
|
+
self.columns = columns
|
22
|
+
self.key_values = option_manager.get_arguments()['figure_parameters']
|
23
|
+
self.edit_mode = False
|
24
|
+
|
25
|
+
self.render()
|
26
|
+
|
27
|
+
def clear(self):
|
28
|
+
self.containerFrame.destroy()
|
29
|
+
|
30
|
+
def set_columns(self, columns):
|
31
|
+
self.columns = columns
|
32
|
+
|
33
|
+
def toggle_edit_mode(self):
|
34
|
+
self.clear()
|
35
|
+
self.edit_mode = not self.edit_mode
|
36
|
+
self.render()
|
37
|
+
|
38
|
+
def render(self):
|
39
|
+
row = 0
|
40
|
+
index = 0
|
41
|
+
|
42
|
+
self.containerFrame = CTkFrame(self)
|
43
|
+
self.containerFrame.grid(row=0, column=0, padx=(5, 5), pady=(5, 5), sticky="nsew")
|
44
|
+
self.containerFrame.grid_columnconfigure((0, 1), weight=1)
|
45
|
+
|
46
|
+
for key_value_pair in self.key_values:
|
47
|
+
CTkLabel(self.containerFrame, text=self.key_values[index]["name"].get()).grid(row=row, column=0, padx=5, pady=5, sticky="")
|
48
|
+
|
49
|
+
if self.edit_mode:
|
50
|
+
return_func = lambda index=index: (self.clear(), self.option_manager.remove_figure_parameter(index), self.home_page.update_graph(), self.render())
|
51
|
+
CTkButton(self.containerFrame, text="Remove", command=return_func).grid(row=row, column=1, padx=(5, 5), pady=(5, 5), sticky="ew")
|
52
|
+
else:
|
53
|
+
bb = CTkOptionMenu(self.containerFrame, values=self.columns, variable=self.key_values[index]["value"], command=self.home_page.update_graph)
|
54
|
+
bb.grid(row=row, column=1, padx=(5, 5), pady=(5, 5), sticky="ew")
|
55
|
+
row += 1
|
56
|
+
index += 1
|
57
|
+
|
58
|
+
if self.edit_mode:
|
59
|
+
CTkButton(self.containerFrame, text="Exit", command=self.toggle_edit_mode).grid(row=row, column=0, padx=(5, 5), pady=(5, 5), sticky="ew")
|
60
|
+
else:
|
61
|
+
CTkButton(self.containerFrame, text="Edit", command=self.toggle_edit_mode).grid(row=row, column=0, padx=(5, 5), pady=(5, 5), sticky="ew")
|
62
|
+
|
63
|
+
add_key_func = lambda: (self.clear(), self.option_manager.add_figure_param("Fig " + str(len(self.key_values)) , self.columns[2]), self.home_page.update_graph(), self.render())
|
64
|
+
CTkButton(self.containerFrame, text="Add Figure", command=add_key_func).grid(row=row, column=1, padx=(5, 5), pady=(5, 5), sticky="ew")
|
@@ -9,6 +9,8 @@ from customtkinter import CTkOptionMenu
|
|
9
9
|
from PIL import Image
|
10
10
|
import os
|
11
11
|
|
12
|
+
from . import MatrixEditor as me
|
13
|
+
|
12
14
|
import pandas as pd
|
13
15
|
|
14
16
|
class SideBar(CTkScrollableFrame):
|
@@ -264,6 +266,81 @@ class SideBar(CTkScrollableFrame):
|
|
264
266
|
if (self.home_page.csv_y2_selector.get() not in columns):
|
265
267
|
self.home_page.csv_y2_selector.set(columns[3])
|
266
268
|
|
269
|
+
style_label = CTkLabel(self.containerFrame, text="Figure Style:")
|
270
|
+
style_label.grid(row=8, column=0, padx=(20, 20), pady=(20, 5), sticky="w")
|
271
|
+
|
272
|
+
self.home_page.figure_style_selector = CTkOptionMenu(self.containerFrame, values=["Scatter", "Bars", "Lines", "Area", "Box"], variable=self.home_page.figure_style, command=self.home_page.update_graph)
|
273
|
+
self.home_page.figure_style_selector.grid(row=9, column=0, padx=(20, 20), pady=(5, 5), sticky="ew")
|
274
|
+
|
275
|
+
elif (selected_graph == "Matrix Editor"):
|
276
|
+
|
277
|
+
info = self.option_manager.get_project_data()
|
278
|
+
folder = os.path.join(info['path'], info['name'])
|
279
|
+
if not os.path.exists(folder):
|
280
|
+
os.makedirs(folder)
|
281
|
+
|
282
|
+
# Get all CSV files in the folder and add their paths to a list
|
283
|
+
path_map = {}
|
284
|
+
name_list = []
|
285
|
+
for root, dirs, files in os.walk(folder):
|
286
|
+
for file in files:
|
287
|
+
if file.endswith(".csv"):
|
288
|
+
name = file.replace(".csv", "")
|
289
|
+
name_list.append(name)
|
290
|
+
path_map[name] = os.path.join(root, file)
|
291
|
+
|
292
|
+
if (len(name_list) == 0):
|
293
|
+
name_list.append("No files found...")
|
294
|
+
else:
|
295
|
+
if (self.home_page.selected_csv.get() not in name_list):
|
296
|
+
self.home_page.selected_csv.set(name_list[0])
|
297
|
+
|
298
|
+
file_label = CTkLabel(self.containerFrame, text="CSV File:")
|
299
|
+
file_label.grid(row=0, column=0, padx=(20, 20), pady=(20, 5), sticky="w")
|
300
|
+
|
301
|
+
self.home_page.csv_file_selector = CTkOptionMenu(self.containerFrame, values=name_list, variable=self.home_page.selected_csv, command=self.home_page.update_graph)
|
302
|
+
self.home_page.csv_file_selector.grid(row=1, column=0, padx=(20, 20), pady=(5, 5), sticky="ew")
|
303
|
+
|
304
|
+
selected_file = self.home_page.selected_csv.get()
|
305
|
+
if (selected_file in path_map and selected_file != self.home_page.open_file):
|
306
|
+
self.home_page.csv_data = pd.read_csv(path_map[selected_file])
|
307
|
+
print(self.home_page.csv_data)
|
308
|
+
self.home_page.open_file = selected_file
|
309
|
+
|
310
|
+
if (self.home_page.csv_data is not None):
|
311
|
+
# Get all column names of CSV
|
312
|
+
columns = self.home_page.csv_data.columns
|
313
|
+
|
314
|
+
style_label = CTkLabel(self.containerFrame, text="Figure Style:")
|
315
|
+
style_label.grid(row=2, column=0, padx=(20, 20), pady=(20, 5), sticky="w")
|
316
|
+
|
317
|
+
self.home_page.figure_style_selector = CTkOptionMenu(self.containerFrame, values=["Scatter", "Bars", "Lines", "Area", "Box"], variable=self.home_page.figure_style, command=self.home_page.update_graph)
|
318
|
+
self.home_page.figure_style_selector.grid(row=3, column=0, padx=(20, 20), pady=(5, 5), sticky="ew")
|
319
|
+
|
320
|
+
x_axis_label = CTkLabel(self.containerFrame, text="X Axis:")
|
321
|
+
x_axis_label.grid(row=4, column=0, padx=(20, 20), pady=(40, 5), sticky="w")
|
322
|
+
|
323
|
+
self.home_page.csv_x_selector = CTkOptionMenu(self.containerFrame, values=columns, variable=self.home_page.selected_x, command=self.home_page.update_graph)
|
324
|
+
self.home_page.csv_x_selector.grid(row=5, column=0, padx=(20, 20), pady=(5, 5), sticky="ew")
|
325
|
+
|
326
|
+
if (self.home_page.csv_x_selector.get() not in columns):
|
327
|
+
self.home_page.csv_x_selector.set(columns[1])
|
328
|
+
|
329
|
+
self.matrix_editor = me.MatrixEditor(self.containerFrame, option_manager=self.option_manager, home_page=self.home_page, columns=columns)
|
330
|
+
self.matrix_editor.grid(row=6, column=0, padx=(10, 10), pady=(10, 0), sticky="nsew")
|
331
|
+
self.matrix_editor.grid_columnconfigure(0, weight=1)
|
332
|
+
self.matrix_editor.grid_rowconfigure(0, weight=1)
|
333
|
+
|
334
|
+
|
335
|
+
#y1_axis_label = CTkLabel(self.containerFrame, text="Y Axis:")
|
336
|
+
#y1_axis_label.grid(row=6, column=0, padx=(20, 20), pady=(20, 5), sticky="w")
|
337
|
+
|
338
|
+
#self.home_page.csv_y1_selector = CTkOptionMenu(self.containerFrame, values=columns, variable=self.home_page.selected_y1, command=self.home_page.update_graph)
|
339
|
+
#self.home_page.csv_y1_selector.grid(row=7, column=0, padx=(20, 20), pady=(5, 5), sticky="ew")
|
340
|
+
|
341
|
+
#if (self.home_page.csv_y1_selector.get() not in columns):
|
342
|
+
# self.home_page.csv_y1_selector.set(columns[2])
|
343
|
+
|
267
344
|
def load_special_csv(self, file_path):
|
268
345
|
file_metadata = {}
|
269
346
|
data_metadata = {}
|
@@ -61,7 +61,7 @@ def create_tab(self, tab):
|
|
61
61
|
self.graph_container.grid_columnconfigure(0, weight=1)
|
62
62
|
self.graph_container.grid_rowconfigure(0, weight=1)
|
63
63
|
|
64
|
-
self.graph_selector = customtkinter.CTkOptionMenu(self.graph_sidebar, values=["Best Cost Stacked", "Best Cost by Round", "Custom CSV", "Compare CSV", "Sampling CSV"], variable=self.graph_selector_value, command=self.update_graph)
|
64
|
+
self.graph_selector = customtkinter.CTkOptionMenu(self.graph_sidebar, values=["Best Cost Stacked", "Best Cost by Round", "Custom CSV", "Compare CSV", "Sampling CSV", "Matrix Editor"], variable=self.graph_selector_value, command=self.update_graph)
|
65
65
|
self.graph_selector.grid(row=0, column=0, padx=(20, 20), pady=(20, 20), sticky="nsew")
|
66
66
|
|
67
67
|
# Create SideBar
|
mgpsogui/util/GraphGenerator.py
CHANGED
@@ -36,6 +36,9 @@ def generate_graphs(HomePage):
|
|
36
36
|
elif (selected_graph == "Sampling CSV"):
|
37
37
|
HomePage.selected_graph_name = "sampling_csv"
|
38
38
|
sampling_csv(HomePage, HomePage.option_manager)
|
39
|
+
elif (selected_graph == "Matrix Editor"):
|
40
|
+
HomePage.selected_graph_name = "matrix_editor"
|
41
|
+
pass
|
39
42
|
|
40
43
|
image_path = os.path.join(folder, HomePage.selected_graph_name + ".png")
|
41
44
|
|
@@ -444,6 +447,8 @@ def compare_csv(homepage, option_manager):
|
|
444
447
|
def sampling_csv(homepage, option_manager):
|
445
448
|
fig = go.Figure()
|
446
449
|
|
450
|
+
style = homepage.figure_style.get()
|
451
|
+
|
447
452
|
data = homepage.csv_data
|
448
453
|
|
449
454
|
x = homepage.csv_x_selector.get()
|
@@ -466,8 +471,21 @@ def sampling_csv(homepage, option_manager):
|
|
466
471
|
|
467
472
|
yy2_unit = ""
|
468
473
|
|
469
|
-
|
470
|
-
|
474
|
+
if (style == "Scatter"):
|
475
|
+
fig.add_trace(go.Scatter(x=xx, y=yy, name=val, mode='markers'))
|
476
|
+
fig.add_trace(go.Scatter(x=xx, y=yy2, name=val2, yaxis='y2', mode='markers'))
|
477
|
+
elif (style == "Bars"):
|
478
|
+
fig.add_trace(go.Bar(x=xx, y=yy, name=val))
|
479
|
+
fig.add_trace(go.Bar(x=xx, y=yy2, name=val2, yaxis='y2'))
|
480
|
+
elif (style == "Lines"):
|
481
|
+
fig.add_trace(go.Scatter(x=xx, y=yy, name=val))
|
482
|
+
fig.add_trace(go.Scatter(x=xx, y=yy2, name=val2, yaxis='y2'))
|
483
|
+
elif (style == "Area"):
|
484
|
+
fig.add_trace(go.Scatter(x=xx, y=yy, name=val, fill='tozeroy'))
|
485
|
+
fig.add_trace(go.Scatter(x=xx, y=yy2, name=val2, yaxis='y2', fill='tozeroy'))
|
486
|
+
elif (style == "Box"):
|
487
|
+
fig.add_trace(go.Box(x=xx, y=yy, name=val))
|
488
|
+
fig.add_trace(go.Box(x=xx, y=yy2, name=val2, yaxis='y2'))
|
471
489
|
|
472
490
|
fig.update_layout(
|
473
491
|
title="",
|
File without changes
|
File without changes
|
File without changes
|