From f658ec48c677389ef1bbe1b4468863b52c1f4280 Mon Sep 17 00:00:00 2001 From: megumiss Date: Mon, 25 Aug 2025 15:23:43 +0800 Subject: [PATCH 1/4] =?UTF-8?q?dashboard=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module/webui/app.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/module/webui/app.py b/module/webui/app.py index 83dc531d..9bda7318 100644 --- a/module/webui/app.py +++ b/module/webui/app.py @@ -437,6 +437,7 @@ def nkas_overview(self) -> None: ) log = RichLog("log") + self._log.dashboard_arg_group = LogRes(self.alas_config).groups with use_scope("logs"): put_scope( @@ -449,8 +450,11 @@ def nkas_overview(self) -> None: "log-bar-btns", [ put_scope("log_scroll_btn"), + put_scope("dashboard_btn"), ], ), + put_html('
'), + put_scope("dashboard"), ], ) put_scope("log", [put_html("")]) From 927443a8dd378614e2bf1d0e86319a67562f6c96 Mon Sep 17 00:00:00 2001 From: megumiss Date: Mon, 25 Aug 2025 18:22:46 +0800 Subject: [PATCH 2/4] dashboard UI --- module/log_res/log_res.py | 70 +++++++++++++++++++ module/webui/app.py | 142 +++++++++++++++++++++++++++++++++++++- module/webui/widgets.py | 11 +++ 3 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 module/log_res/log_res.py diff --git a/module/log_res/log_res.py b/module/log_res/log_res.py new file mode 100644 index 00000000..31afc5f6 --- /dev/null +++ b/module/log_res/log_res.py @@ -0,0 +1,70 @@ +from datetime import datetime +from functools import cached_property + +from module.config.deep import deep_get +from module.logger import logger + + +class LogRes: + """ + set attr---> + Logres(AzurLaneConfig).=resource_value:int + OR ={'Value:int, 'Limit/Total':int}:dict + """ + YellowCoin: list + + def __init__(self, config): + self.__dict__['config'] = config + + def __setattr__(self, key, value): + if key in self.groups: + _key_group = f'Dashboard.{key}' + _mod = False + original = deep_get(self.config.data, keys=_key_group) + if isinstance(value, int): + if original['Value'] != value: + _key = _key_group + '.Value' + self.config.modified[_key] = value + _time = datetime.now().replace(microsecond=0) + _key_time = _key_group + f'.Record' + self.config.modified[_key_time] = _time + elif isinstance(value, dict): + for value_name, _value in value.items(): + if _value == original[value_name]: + continue + _key = _key_group + f'.{value_name}' + self.config.modified[_key] = _value + _key_time = _key_group + f'.Record' + _time = datetime.now().replace(microsecond=0) + self.config.modified[_key_time] = _time + else: + logger.info('No such resource on dashboard') + super().__setattr__(name=key, value=value) + + @cached_property + def groups(self) -> dict: + from module.config.utils import filepath_argument, read_file + return deep_get(d=read_file(filepath_argument("dashboard")), keys='Dashboard') + + """ + def log_res(self, name, modified: dict, update=True): + if name in self.groups: + key = f'Dashboard.{name}' + original = deep_get(self.config.data, keys=key) + _mod = False + for value_name, value in modified.items(): + if value == original[value_name]: + continue + _key = key + f'.{value_name}' + self.config.modified[_key] = value + _mod = True + if _mod: + _key_time = key + f'.Record' + _time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + self.config.modified[_key_time] = _time + if update: + self.config.update() + else: + logger.warning('No such resource!') + return True + """ diff --git a/module/webui/app.py b/module/webui/app.py index 9bda7318..093c91b5 100644 --- a/module/webui/app.py +++ b/module/webui/app.py @@ -1,6 +1,7 @@ import argparse import json import queue +import re import threading import time from datetime import datetime @@ -78,6 +79,7 @@ re_fullmatch, to_pin_value, ) +from module.log_res.log_res import LogRes from module.webui.widgets import ( BinarySwitchButton, RichLog, @@ -91,11 +93,33 @@ patch_executor() task_handler = TaskHandler() +def timedelta_to_text(delta=None): + time_delta_name_suffix_dict = { + 'Y': 'YearsAgo', + 'M': 'MonthsAgo', + 'D': 'DaysAgo', + 'h': 'HoursAgo', + 'm': 'MinutesAgo', + 's': 'SecondsAgo', + } + time_delta_name_prefix = 'Gui.Overview.' + time_delta_name_suffix = 'NoData' + time_delta_display = '' + if isinstance(delta, dict): + for _key in delta: + if delta[_key]: + time_delta_name_suffix = time_delta_name_suffix_dict[_key] + time_delta_display = delta[_key] + break + time_delta_display = str(time_delta_display) + time_delta_name = time_delta_name_prefix + time_delta_name_suffix + return time_delta_display + t(time_delta_name) class NKASGUI(Frame): NKAS_MENU: Dict[str, Dict[str, List[str]]] NKAS_ARGS: Dict[str, Dict[str, Dict[str, Dict[str, str]]]] theme = "default" + _log = RichLog def initial(self) -> None: self.NKAS_MENU = read_file(filepath_args("menu", self.nkas_mod)) @@ -437,7 +461,7 @@ def nkas_overview(self) -> None: ) log = RichLog("log") - self._log.dashboard_arg_group = LogRes(self.alas_config).groups + self._log.dashboard_arg_group = LogRes(self.nkas_config).groups with use_scope("logs"): put_scope( @@ -471,12 +495,27 @@ def nkas_overview(self) -> None: color_off="off", scope="log_scroll_btn", ) - + switch_dashboard = BinarySwitchButton( + label_on=t("Gui.Button.DashboardON"), + label_off=t("Gui.Button.DashboardOFF"), + onclick_on=lambda: self.set_dashboard_display(False), + onclick_off=lambda: self.set_dashboard_display(True), + get_state=lambda: log.display_dashboard, + color_on="off", + color_off="on", + scope="dashboard_btn", + ) self.task_handler.add(switch_scheduler.g(), 1, True) self.task_handler.add(switch_log_scroll.g(), 1, True) + self.task_handler.add(switch_dashboard.g(), 1, True) self.task_handler.add(self.nkas_update_overview_task, 10, True) + self.task_handler.add(self.nkas_update_dashboard, 10, True) self.task_handler.add(log.put_log(self.nkas), 0.25, True) + def set_dashboard_display(self, b): + self._log.set_dashboard_display(b) + self.nkas_update_dashboard(True) + def _init_nkas_config_watcher(self) -> None: def put_queue(path, value): self.modified_config_queue.put({"name": path, "value": value}) @@ -616,6 +655,105 @@ def put_task(func: Function): else: put_text(t("Gui.Overview.NoTask")).style("--overview-notask-text--") + def _update_dashboard(self, num=None, groups_to_display=None): + x = 0 + _num = 10000 if num is None else num + _arg_group = self._log.dashboard_arg_group if groups_to_display is None else groups_to_display + time_now = datetime.now().replace(microsecond=0) + for group_name in _arg_group: + group = deep_get(d=self.nkas_config.data, keys=f'Dashboard.{group_name}') + if group is None: + continue + + value = str(group['Value']) + if 'Limit' in group.keys(): + value_limit = f' / {group["Limit"]}' + value_total = '' + elif 'Total' in group.keys(): + value_total = f' ({group["Total"]})' + value_limit = '' + elif group_name == 'Pt': + value_limit = ' / ' + re.sub(r'[,.\'",。]', '', + str(deep_get(self.nkas_config.data, 'EventGeneral.EventGeneral.PtLimit'))) + if value_limit == ' / 0': + value_limit = '' + else: + value_limit = '' + value_total = '' + # value = value + value_limit + value_total + + value_time = group['Record'] + if value_time is None or value_time == datetime(2020, 1, 1, 0, 0, 0): + value_time = datetime(2023, 1, 1, 0, 0, 0) + + # Handle time delta + if value_time == datetime(2023, 1, 1, 0, 0, 0): + value = 'None' + delta = timedelta_to_text() + else: + delta = timedelta_to_text(time_delta(value_time - time_now)) + if group_name not in self._log.last_display_time.keys(): + self._log.last_display_time[group_name] = '' + if self._log.last_display_time[group_name] == delta and not self._log.first_display: + continue + self._log.last_display_time[group_name] = delta + + # if self._log.first_display: + # Handle width + # value_width = len(value) * 0.7 + 0.6 if value != 'None' else 4.5 + # value_width = str(value_width/1.12) + 'rem' if self.is_mobile else str(value_width) + 'rem' + value_limit = '' if value == 'None' else value_limit + # limit_width = len(value_limit) * 0.7 + # limit_width = str(limit_width) + 'rem' + value_total = '' if value == 'None' else value_total + limit_style = '--dashboard-limit--' if value_limit else '--dashboard-total--' + value_limit = value_limit if value_limit else value_total + # Handle dot color + _color = f"""background-color:{deep_get(d=group, keys='Color').replace('^', '#')}""" + color = f'
' + with use_scope(group_name, clear=True): + put_row( + [ + put_html(color), + put_scope( + f"{group_name}_group", + [ + put_column( + [ + put_row( + [ + put_text(value + ).style(f'--dashboard-value--'), + put_text(value_limit + ).style(limit_style), + ], + ).style('grid-template-columns:min-content auto;align-items: baseline;'), + put_text( + t(f'Gui.Overview.{group_name}') + " - " + delta + ).style('---dashboard-help--') + ], + size="auto auto", + ), + ], + ), + ], + size="20px 1fr" + ).style("height: 1fr"), + x += 1 + if x >= _num: + break + if self._log.first_display: + self._log.first_display = False + + def nkas_update_dashboard(self, _clear=False): + if not self.visible: + return + with use_scope("dashboard", clear=_clear): + if not self._log.display_dashboard: + self._update_dashboard(num=4, groups_to_display=['Oil', 'Coin', 'Gem', 'Pt']) + elif self._log.display_dashboard: + self._update_dashboard() + @use_scope("content", clear=True) def nkas_daemon_overview(self, task: str) -> None: self.init_menu(name=task) diff --git a/module/webui/widgets.py b/module/webui/widgets.py index 05b76e56..643edc48 100644 --- a/module/webui/widgets.py +++ b/module/webui/widgets.py @@ -74,6 +74,8 @@ def set_scroll(self, b: bool) -> None: class RichLog: + last_display_time: dict + def __init__(self, scope, font_width="0.559") -> None: self.scope = scope self.font_width = font_width @@ -93,6 +95,10 @@ def __init__(self, scope, font_width="0.559") -> None: # self._callback_thread = None # self._width = 80 self.keep_bottom = True + self.display_dashboard = False + self.first_display = True + self.last_display_time = {} + self.dashboard_arg_group = None if State.theme == "dark": self.terminal_theme = DARK_TERMINAL_THEME else: @@ -138,6 +144,11 @@ def set_scroll(self, b: bool) -> None: # use for lambda callback function self.keep_bottom = b + def set_dashboard_display(self, b: bool) -> None: + # use for lambda callback function. Copied. + self.display_dashboard = b + self.first_display = True + def get_width(self): js = """ let canvas = document.createElement('canvas'); From 7e017e627632543f6fe3b361960fcd3f19530914 Mon Sep 17 00:00:00 2001 From: megumiss Date: Tue, 26 Aug 2025 13:54:17 +0800 Subject: [PATCH 3/4] dashboard UI --- module/config/argument/argument.yaml | 51 ++++++ module/config/config_generated.py | 63 ++++++++ module/config/i18n/zh-CN.json | 228 +++++++++++++++++++++++++++ module/webui/app.py | 1 + 4 files changed, 343 insertions(+) diff --git a/module/config/argument/argument.yaml b/module/config/argument/argument.yaml index b0a1a971..87bc4b78 100644 --- a/module/config/argument/argument.yaml +++ b/module/config/argument/argument.yaml @@ -1,3 +1,54 @@ +Oil: + Value: 0 + Limit: 0 + Color: ^000000 + Record: 2020-01-01 00:00:00 +Coin: + Value: 0 + Limit: 0 + Color: ^FFAA33 + Record: 2020-01-01 00:00:00 +Gem: + Value: 0 + Color: ^FF3333 + Record: 2020-01-01 00:00:00 +Pt: + Value: 0 + Color: ^00BFFF + Record: 2020-01-01 00:00:00 +YellowCoin: + Value: 0 + Color: ^FF8800 + Record: 2020-01-01 00:00:00 +PurpleCoin: + Value: 0 + Color: ^7700BB + Record: 2020-01-01 00:00:00 +ActionPoint: + Value: 0 + Total: 0 + Color: ^0000FF + Record: 2020-01-01 00:00:00 +Merit: + Value: 0 + Color: ^FFFF00 + Record: 2020-01-01 00:00:00 +Cube: + Value: 0 + Color: ^33FFFF + Record: 2020-01-01 00:00:00 +Core: + Value: 0 + Color: ^AAAAAA + Record: 2020-01-01 00:00:00 +Medal: + Value: 0 + Color: ^FFDD00 + Record: 2020-01-01 00:00:00 +GuildCoin: + Value: 0 + Color: ^AAAAAA + Record: 2020-01-01 00:00:00 Scheduler: Enable: type: checkbox diff --git a/module/config/config_generated.py b/module/config/config_generated.py index 0e6ecc63..5962c94d 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -9,6 +9,69 @@ class GeneratedConfig: Auto generated configuration """ + # Group `Oil` + Oil_Value = 0 + Oil_Limit = 0 + Oil_Color = '^000000' + Oil_Record = datetime.datetime(2020, 1, 1, 0, 0) + + # Group `Coin` + Coin_Value = 0 + Coin_Limit = 0 + Coin_Color = '^FFAA33' + Coin_Record = datetime.datetime(2020, 1, 1, 0, 0) + + # Group `Gem` + Gem_Value = 0 + Gem_Color = '^FF3333' + Gem_Record = datetime.datetime(2020, 1, 1, 0, 0) + + # Group `Pt` + Pt_Value = 0 + Pt_Color = '^00BFFF' + Pt_Record = datetime.datetime(2020, 1, 1, 0, 0) + + # Group `YellowCoin` + YellowCoin_Value = 0 + YellowCoin_Color = '^FF8800' + YellowCoin_Record = datetime.datetime(2020, 1, 1, 0, 0) + + # Group `PurpleCoin` + PurpleCoin_Value = 0 + PurpleCoin_Color = '^7700BB' + PurpleCoin_Record = datetime.datetime(2020, 1, 1, 0, 0) + + # Group `ActionPoint` + ActionPoint_Value = 0 + ActionPoint_Total = 0 + ActionPoint_Color = '^0000FF' + ActionPoint_Record = datetime.datetime(2020, 1, 1, 0, 0) + + # Group `Merit` + Merit_Value = 0 + Merit_Color = '^FFFF00' + Merit_Record = datetime.datetime(2020, 1, 1, 0, 0) + + # Group `Cube` + Cube_Value = 0 + Cube_Color = '^33FFFF' + Cube_Record = datetime.datetime(2020, 1, 1, 0, 0) + + # Group `Core` + Core_Value = 0 + Core_Color = '^AAAAAA' + Core_Record = datetime.datetime(2020, 1, 1, 0, 0) + + # Group `Medal` + Medal_Value = 0 + Medal_Color = '^FFDD00' + Medal_Record = datetime.datetime(2020, 1, 1, 0, 0) + + # Group `GuildCoin` + GuildCoin_Value = 0 + GuildCoin_Color = '^AAAAAA' + GuildCoin_Record = datetime.datetime(2020, 1, 1, 0, 0) + # Group `Scheduler` Scheduler_Enable = False # True, False Scheduler_NextRun = datetime.datetime(1989, 12, 27, 0, 0) diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index 4c84de08..a814890d 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -179,6 +179,234 @@ "help": "" } }, + "Oil": { + "_info": { + "name": "Oil._info.name", + "help": "Oil._info.help" + }, + "Value": { + "name": "Oil.Value.name", + "help": "Oil.Value.help" + }, + "Limit": { + "name": "Oil.Limit.name", + "help": "Oil.Limit.help" + }, + "Color": { + "name": "Oil.Color.name", + "help": "Oil.Color.help" + }, + "Record": { + "name": "Oil.Record.name", + "help": "Oil.Record.help" + } + }, + "Coin": { + "_info": { + "name": "Coin._info.name", + "help": "Coin._info.help" + }, + "Value": { + "name": "Coin.Value.name", + "help": "Coin.Value.help" + }, + "Limit": { + "name": "Coin.Limit.name", + "help": "Coin.Limit.help" + }, + "Color": { + "name": "Coin.Color.name", + "help": "Coin.Color.help" + }, + "Record": { + "name": "Coin.Record.name", + "help": "Coin.Record.help" + } + }, + "Gem": { + "_info": { + "name": "Gem._info.name", + "help": "Gem._info.help" + }, + "Value": { + "name": "Gem.Value.name", + "help": "Gem.Value.help" + }, + "Color": { + "name": "Gem.Color.name", + "help": "Gem.Color.help" + }, + "Record": { + "name": "Gem.Record.name", + "help": "Gem.Record.help" + } + }, + "Pt": { + "_info": { + "name": "Pt._info.name", + "help": "Pt._info.help" + }, + "Value": { + "name": "Pt.Value.name", + "help": "Pt.Value.help" + }, + "Color": { + "name": "Pt.Color.name", + "help": "Pt.Color.help" + }, + "Record": { + "name": "Pt.Record.name", + "help": "Pt.Record.help" + } + }, + "YellowCoin": { + "_info": { + "name": "YellowCoin._info.name", + "help": "YellowCoin._info.help" + }, + "Value": { + "name": "YellowCoin.Value.name", + "help": "YellowCoin.Value.help" + }, + "Color": { + "name": "YellowCoin.Color.name", + "help": "YellowCoin.Color.help" + }, + "Record": { + "name": "YellowCoin.Record.name", + "help": "YellowCoin.Record.help" + } + }, + "PurpleCoin": { + "_info": { + "name": "PurpleCoin._info.name", + "help": "PurpleCoin._info.help" + }, + "Value": { + "name": "PurpleCoin.Value.name", + "help": "PurpleCoin.Value.help" + }, + "Color": { + "name": "PurpleCoin.Color.name", + "help": "PurpleCoin.Color.help" + }, + "Record": { + "name": "PurpleCoin.Record.name", + "help": "PurpleCoin.Record.help" + } + }, + "ActionPoint": { + "_info": { + "name": "ActionPoint._info.name", + "help": "ActionPoint._info.help" + }, + "Value": { + "name": "ActionPoint.Value.name", + "help": "ActionPoint.Value.help" + }, + "Total": { + "name": "ActionPoint.Total.name", + "help": "ActionPoint.Total.help" + }, + "Color": { + "name": "ActionPoint.Color.name", + "help": "ActionPoint.Color.help" + }, + "Record": { + "name": "ActionPoint.Record.name", + "help": "ActionPoint.Record.help" + } + }, + "Merit": { + "_info": { + "name": "Merit._info.name", + "help": "Merit._info.help" + }, + "Value": { + "name": "Merit.Value.name", + "help": "Merit.Value.help" + }, + "Color": { + "name": "Merit.Color.name", + "help": "Merit.Color.help" + }, + "Record": { + "name": "Merit.Record.name", + "help": "Merit.Record.help" + } + }, + "Cube": { + "_info": { + "name": "Cube._info.name", + "help": "Cube._info.help" + }, + "Value": { + "name": "Cube.Value.name", + "help": "Cube.Value.help" + }, + "Color": { + "name": "Cube.Color.name", + "help": "Cube.Color.help" + }, + "Record": { + "name": "Cube.Record.name", + "help": "Cube.Record.help" + } + }, + "Core": { + "_info": { + "name": "Core._info.name", + "help": "Core._info.help" + }, + "Value": { + "name": "Core.Value.name", + "help": "Core.Value.help" + }, + "Color": { + "name": "Core.Color.name", + "help": "Core.Color.help" + }, + "Record": { + "name": "Core.Record.name", + "help": "Core.Record.help" + } + }, + "Medal": { + "_info": { + "name": "Medal._info.name", + "help": "Medal._info.help" + }, + "Value": { + "name": "Medal.Value.name", + "help": "Medal.Value.help" + }, + "Color": { + "name": "Medal.Color.name", + "help": "Medal.Color.help" + }, + "Record": { + "name": "Medal.Record.name", + "help": "Medal.Record.help" + } + }, + "GuildCoin": { + "_info": { + "name": "GuildCoin._info.name", + "help": "GuildCoin._info.help" + }, + "Value": { + "name": "GuildCoin.Value.name", + "help": "GuildCoin.Value.help" + }, + "Color": { + "name": "GuildCoin.Color.name", + "help": "GuildCoin.Color.help" + }, + "Record": { + "name": "GuildCoin.Record.name", + "help": "GuildCoin.Record.help" + } + }, "Scheduler": { "_info": { "name": "任务设置", diff --git a/module/webui/app.py b/module/webui/app.py index 093c91b5..631adb49 100644 --- a/module/webui/app.py +++ b/module/webui/app.py @@ -461,6 +461,7 @@ def nkas_overview(self) -> None: ) log = RichLog("log") + self._log = log self._log.dashboard_arg_group = LogRes(self.nkas_config).groups with use_scope("logs"): From 8634e3b818f5765724e6dae173368071786d4e0b Mon Sep 17 00:00:00 2001 From: megumiss Date: Tue, 26 Aug 2025 14:49:59 +0800 Subject: [PATCH 4/4] dashboard UI --- assets/gui/css/dark-nkas.css | 14 +- assets/gui/css/light-nkas.css | 12 ++ assets/gui/css/nkas-mobile.css | 30 ++++ assets/gui/css/nkas.css | 75 +++++++++- config/template.json | 68 +++++++++ module/config/argument/args.json | 202 ++++++++++++++++++++++++++ module/config/argument/dashboard.yaml | 18 +++ module/config/argument/gui.yaml | 2 + module/config/config_updater.py | 16 +- module/config/i18n/zh-CN.json | 22 +++ module/config/utils.py | 33 ++++- module/webui/app.py | 1 + 12 files changed, 487 insertions(+), 6 deletions(-) create mode 100644 module/config/argument/dashboard.yaml diff --git a/assets/gui/css/dark-nkas.css b/assets/gui/css/dark-nkas.css index b836157e..05a38be5 100644 --- a/assets/gui/css/dark-nkas.css +++ b/assets/gui/css/dark-nkas.css @@ -149,7 +149,19 @@ pre.rich-traceback-code { border-bottom: 1px solid #36393f; } +#pywebio-scope-dashboard input { + background-color: #2f3136 !important; +} + *[style*="--arg-help--"], [id^="pywebio-scope-group_"] > p + p { color: #adb5bd; -} \ No newline at end of file +} + +*[style*="--dashboard-help--"]{ + color: #adb5bd; +} + +*[style*="--dashboard-limit--"]{ + color: #adb5bd; +} diff --git a/assets/gui/css/light-nkas.css b/assets/gui/css/light-nkas.css index b65067f1..af578939 100644 --- a/assets/gui/css/light-nkas.css +++ b/assets/gui/css/light-nkas.css @@ -60,6 +60,10 @@ input[type="checkbox"] { accent-color: #7a77bb; } +#pywebio-scope-dashboard input { + background-color: white !important; +} + select { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAAAXNSR0IArs4c6QAAC85JREFUeF7tnUezdVURhh+qmDixnGOVVjmxyj+AOsGBEyeGKrMYMGfBCIYqE2LOWVTMOeeMGRQjZhBMmAOCAXO9sE7V+eC711699z5n791vj3vtu/vp9Zy1bh+43zE4TMAEDiRwjNmYgAkcTMCCeHeYwCEELIi3hwlYEO8BE8gR8AmS4+ZVRQhYkCKNdpk5AhYkx82rihCwIEUa7TJzBCxIjptXFSFgQYo02mXmCFiQHDevKkLAghRptMvMEbAgOW5eVYSABSnSaJeZI2BBcty8qggBC1Kk0S4zR8CC5Lh5VRECFqRIo11mjoAFyXHzqiIELEiRRrvMHAELkuPmVUUIWJAijXaZOQIWJMfNq4oQsCBFGu0ycwQsSI6bVxUhYEGKNNpl5ghYkBw3rypCwIIUabTLzBGwIDluXlWEgAUp0miXmSNgQXLcvKoIAQtSpNEuM0fAguS4eVURAhakSKNdZo6ABclx86oiBCxIkUa7zBwBC5Lj5lVFCFiQIo12mTkCFiTHzauKELAgRRrtMnMELEiOm1cVIWBBijTaZeYIWJAcN68qQsCCFGm0y8wRsCA5bl5VhIAFKdJol5kjYEFy3LyqCAELUqTRLjNHwILkuHlVEQIWpEijXWaOgAXJcfOqIgQsSJFGu8wcAQuS4+ZVRQhYkCKNdpk5AhYkx82rihCwIEUa7TJzBCxIjptXFSFgQYo02mXmCFiQHDevKkLAghRptMvMEZizILcAjgOuA1wIfBy4LFemV62AwLWBGwPHA2cD5+1iP8xVkE8DJ1ytqecCpwGfXEGzXUIfgZOBU4Drbi27GLgH8Jm+R/Vlz1GQC4AbHFLG7YC395Xp7AUTeBtw20Pe/0bAd6eqb26CnA6cGij29oDAOdZN4K2APhAPi0uAm08lydwEOR/QJ0Ik7gAIoGOdBN4C6IMwEvpQPSOS2JszN0H+ClyrowhL0gFrQak9cqis9wG3nKK+uQlyEXD9zkLvCAioYx0E3gzog68nXgI8sGdBNHdugrwLuHX05bfy7gQIrGPZBDJyqOLJPiTnJsgNgU+07z96W21JeonNK/9NbaP3vpXGvDfrXRTNn5sgem+N9LITqjsDAu1YFoE3AvqAy4R+99DvIJPEHAVRoRrtZSdUdwEE3LEMAlk5/tv2yTumLHOugliSKbs+n2e/AdCp3xs7kUMvNWdB9H6ag2cnVCcCaoBjngReD+i0743/tJPjnb0LM/lzF0Q1aeSXnVDdFVAjHPMikJXj300OTTt3EksQxJLsZCvs7Ie8DtDp3hs7l2MJV6xtiJp1ZydUdwPUGMd+CZwF6FTvjX+1k+PdvQuH5i/lBNnUqVFgdkJlSYbulmHrs3L8s/0uunM5lnaCjCHJ3QE1yrFbAq8F9AHVG5JDI//39C4cK39pJ8imbo0GsxMq/U82aphjNwReA+iDqTf+0eR4b+/CMfOXKogYaESYnVBZkjF30cHPyspxRbtW7VWOpV6xttsxRJKTADXQMQ2BV7f/Jbb36ZJD16rJ/vORnhda8gmyqVMjw+yE6p6AGukYl0BWjr83Od4/7uvkn7YGQVS9JcnvgbFXngnodO6Nv7Vr1WzkWMMVa7sJmq9nJ1T3AtRYxzACrwJ0KveG5NC16gO9C6fOX8sJsuGkUWJ2QnVvQA125Ahk5dD/Zi05Ppj7sdOuWpsgomVJpt0zR3v6KwGdwr3xl3atmqUca7tibTdHc/fshOo+gBruiBF4BaDTtzckh06OD/Uu3GX+Gk+QDT9915GdUN0XUOMdhxPIynF5k+PDcwe8ZkHE3pJMtwNfDui07Y3FyLHmK9Z20zRyzE6o7gdoIziOJPAyQKdsb+iPj+ta9ZHehfvKX/sJsuGq0WN2QmVJxpHjz+0X8sXIUeUEGUOS+wP61KweLwX0gdEbkkMnx0d7F+47v8oJsuGsUWR2QvUAQBukamTluLTJ8bElgqsmiHqkkWR2QlVVEv1pT52ivfGndq1apBzVrljbzR0iif4GrDZMlXgxoA+G3pAculbpXwZbbFQ8QTbN0ogyO6F6EKCNs/bIyvHHJof+jOyio7IgapwlOXj7vij5F9P/0K5Vi5ej8hVre1tonp+dUD0Y0EZaW7wQ0CnZG5JD16rV/DuS1U+QzQbQ6DI7oXoIoA21lsjK8fsmx6fWAsInyJGdtCTwAkCnYm/8rl2rViWHBbnmNtAoMzuheihXbbClxvMBnYa9ITl0rdI/3b268BXrmi3VSDM7oXoYoI22tMjK8dsmx6T/Vvk+YVqQo9OvJMnzAJ1+vbF6OXzFOnxL6AvB7ITqZEAbb+7xXECnXm/8pp0cZ/cuXFq+T5DDO6ZRZ3ZCNXdJsnL8uv1Cvno5fILEPs6GSHIKoI04t3gOIIF7Q3LoF/LP9i5car5PkFjnNPrMTqgeDmhDziWycvyqyfG5uRSyi/ewIHHKGoFmJ1RzkeTZgE613vhlu1aVksNXrN5tctX3BFlJHgFog+4rngVI1N6QHLpWfb534RryfYL0d1Ej0eyE6pGANuquIyvHJU2OL+z6hefy8yxIrhNLkuSZgE6v3vhFu1aVlcNXrN4tc2S+vj/ITqgeBWjjTh3PAHRq9Ybk0LXqi70L15bvE2RYRzUqzU6oHg1oA08VWTl+3uT40lQvtqTnWpDh3ZqjJE8HdEr1xs/atcpyNHIWpHcLHT1fo9PshOoxgDb0WHEGoNOpNySHrlVf7l245nwLMl53NULNTqjGkiQrx0/byWE5rrYfLMh4guhJQyQ5FdAGz8bTAInWGz9pcpzTu7BCvgUZv8saqWYnVKcB2ui9cTogwXpDcuhadW7vwir5FmSaTmu0mp1Q9UqSlePidnJYjkP2gAWZRhA9dYgkjwW08f9fPBWQUL0hOXRyfKV3YbV8CzJtxzVqzU6oHgdIgIPiKYBE6o2Lmhxf7V1YMd+CTN/1KSTJyvHjdq2yHMG+W5AgqIFp+l4iO6F6PCAhNvFkQKdLb0gOXavO611YOd+C7K77GsFmJlR6wycAEiMrx4VNjq/trtx1/CQLsts+DpFEf1rnhMTrXtCuVZYjAc+CJKANXKLvKyITqoE/5srlkkPXqq+P8bCKz7Ag++m6RrOHTajGeKsfNTm+McbDqj7Dguyv81NK8sN2rbIcA/trQQYCHLhc32NsT6gGPu7K5ZJD16pvjvGw6s+wIPvfARrZajo1RvygnRyWYwyagAUZCeTAx4whyfebHN8a+C5evkXAgsxnO+gLwSclX0dy6Fr17eR6LzuAgAWZ19bQF4JP7Hyl77WTw3J0goukW5AIpd3m9EiiUe5tgPN3+4p1fpoFmWevI3904VLgpsB35lnCOt7Kgsy3j2cCJx3wepcDx1uO6ZtnQaZnPOQn3AQ4C7gecCxwWftjbrcCrhjyYK+NEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEbAgMU7OKkrAghRtvMuOEfgf9hZk2PNubUAAAAAASUVORK5CYII="); } @@ -151,4 +155,12 @@ pre.rich-traceback-code { *[style*="--arg-help--"], [id^="pywebio-scope-group_"] > p + p { color: #777777; +} + +*[style*="--dashboard-help--"]{ + color: #777777; +} + +*[style*="--dashboard-limit--"]{ + color: #777777; } \ No newline at end of file diff --git a/assets/gui/css/nkas-mobile.css b/assets/gui/css/nkas-mobile.css index 9acad6eb..eb941214 100644 --- a/assets/gui/css/nkas-mobile.css +++ b/assets/gui/css/nkas-mobile.css @@ -35,6 +35,14 @@ grid-template-columns: 1fr auto; grid-template-rows: 1fr auto; } +#pywebio-scope-dashboard { + font-weight: 400; + width: 100%; + display: grid; + grid-auto-flow: row; + grid-template-columns: repeat(auto-fit,minmax(6rem,1fr)); + overflow: hidden; +} #pywebio-scope-_groups { grid-template-columns: 0fr 1fr; @@ -59,4 +67,26 @@ #pywebio-scope-waiting, #pywebio-scope-log { overflow-y: auto; +} + +#output-container .status-point { + margin: 45% 50% 55% 50%; + width: .4rem; + height: .4rem; + border-radius: 50%; +} + +*[style*="--dashboard-limit--"] { + font-size: .8rem; + padding: 0.2rem 0 0 0; +} +*[style*="--dashboard-value--"] { + font-size: 1rem; + margin: 0 0 0 0.4rem !important; +} +*[style*="--dashboard-total--"] { + font-size: 1rem; +} +*[style*="--dashboard-help--"] { + font-size: .6rem; } \ No newline at end of file diff --git a/assets/gui/css/nkas.css b/assets/gui/css/nkas.css index 94e69648..2f4cf504 100644 --- a/assets/gui/css/nkas.css +++ b/assets/gui/css/nkas.css @@ -133,6 +133,13 @@ footer { font-size: 0.875rem; } +.status-point { + margin: 37% 50% 63% 50%; + width: .75rem; + height: .75rem; + border-radius: 50%; +} + input[type="checkbox"] { width: 1.25rem; height: 1.25rem; @@ -399,11 +406,31 @@ pre.rich-traceback-code { justify-content: space-between; } +#pywebio-scope-log-bar { + flex-wrap: wrap; +} + #pywebio-scope-log-bar-btns { display: grid; grid-auto-flow: column; } +#pywebio-scope-log-bar .hr-group { + width: 100%; +} + +#pywebio-scope-dashboard { + font-weight: 400; + width: 100%; + display: grid; + grid-auto-flow: row; + grid-template-columns: repeat(auto-fit,minmax(10rem,1fr)); +} + +#pywebio-scope-dashboard .form-control{ + padding: 0 0 0; +} + #pywebio-scope-log { line-height: 1.2; font-size: 0.85rem; @@ -523,4 +550,50 @@ pre.rich-traceback-code { width: 1.5rem; height: 1.5rem; border: .2em solid currentColor; -} \ No newline at end of file +} + +/**[style*="--dashboard-value--"] {*/ +/* font-size: 1.3rem;*/ +/* font-weight: 400;*/ +/* margin: 0 0 -0.2rem 0.6rem !important;*/ +/* overflow-wrap: break-word;*/ +/* overflow: visible;*/ +/* border-bottom: 0;*/ +/*}*/ + +*[style*="--dashboard-value--"] { + font-size: 1.2rem; + font-weight: 400; + margin: 0 0 0 0.6rem !important; + font-family: + "Arial", + serif; +} + +*[style*="--dashboard-total--"] { + font-size: 1.2rem; + font-weight: 400; + margin: 0 0 0 0.15rem !important; + font-family: + "Arial", + serif; +} + +*[style*="--dashboard-help--"] { + font-size: .8rem; + margin: 0 0 0 0.6rem !important; + font-family: + "Arial", + serif; +} + +*[style*="--dashboard-limit--"] { + font-weight: 400; + font-size: .9rem; + margin: 0 0 0 0 !important; + vertical-align: text-bottom; + overflow-wrap: normal; + font-family: + "Arial", + serif; +} diff --git a/config/template.json b/config/template.json index 69c251f1..b7698a0e 100644 --- a/config/template.json +++ b/config/template.json @@ -1,4 +1,72 @@ { + "Dashboard": { + "Oil": { + "Value": 0, + "Limit": 0, + "Color": "^000000", + "Record": "2020-01-01 00:00:00" + }, + "Coin": { + "Value": 0, + "Limit": 0, + "Color": "^FFAA33", + "Record": "2020-01-01 00:00:00" + }, + "Gem": { + "Value": 0, + "Color": "^FF3333", + "Record": "2020-01-01 00:00:00" + }, + "Pt": { + "Value": 0, + "Color": "^00BFFF", + "Record": "2020-01-01 00:00:00" + }, + "Cube": { + "Value": 0, + "Color": "^33FFFF", + "Record": "2020-01-01 00:00:00" + }, + "ActionPoint": { + "Value": 0, + "Total": 0, + "Color": "^0000FF", + "Record": "2020-01-01 00:00:00" + }, + "YellowCoin": { + "Value": 0, + "Color": "^FF8800", + "Record": "2020-01-01 00:00:00" + }, + "PurpleCoin": { + "Value": 0, + "Color": "^7700BB", + "Record": "2020-01-01 00:00:00" + }, + "Core": { + "Value": 0, + "Color": "^AAAAAA", + "Record": "2020-01-01 00:00:00" + }, + "Medal": { + "Value": 0, + "Color": "^FFDD00", + "Record": "2020-01-01 00:00:00" + }, + "Merit": { + "Value": 0, + "Color": "^FFFF00", + "Record": "2020-01-01 00:00:00" + }, + "GuildCoin": { + "Value": 0, + "Color": "^AAAAAA", + "Record": "2020-01-01 00:00:00" + }, + "Storage": { + "Storage": {} + } + }, "NKAS": { "Emulator": { "Serial": "auto", diff --git a/module/config/argument/args.json b/module/config/argument/args.json index a763fc94..c392ef1a 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -1,4 +1,206 @@ { + "Dashboard": { + "Oil": { + "Value": { + "type": "input", + "value": 0 + }, + "Limit": { + "type": "input", + "value": 0 + }, + "Color": { + "type": "input", + "value": "^000000" + }, + "Record": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" + } + }, + "Coin": { + "Value": { + "type": "input", + "value": 0 + }, + "Limit": { + "type": "input", + "value": 0 + }, + "Color": { + "type": "input", + "value": "^FFAA33" + }, + "Record": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" + } + }, + "Gem": { + "Value": { + "type": "input", + "value": 0 + }, + "Color": { + "type": "input", + "value": "^FF3333" + }, + "Record": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" + } + }, + "Pt": { + "Value": { + "type": "input", + "value": 0 + }, + "Color": { + "type": "input", + "value": "^00BFFF" + }, + "Record": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" + } + }, + "Cube": { + "Value": { + "type": "input", + "value": 0 + }, + "Color": { + "type": "input", + "value": "^33FFFF" + }, + "Record": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" + } + }, + "ActionPoint": { + "Value": { + "type": "input", + "value": 0 + }, + "Total": { + "type": "input", + "value": 0 + }, + "Color": { + "type": "input", + "value": "^0000FF" + }, + "Record": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" + } + }, + "YellowCoin": { + "Value": { + "type": "input", + "value": 0 + }, + "Color": { + "type": "input", + "value": "^FF8800" + }, + "Record": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" + } + }, + "PurpleCoin": { + "Value": { + "type": "input", + "value": 0 + }, + "Color": { + "type": "input", + "value": "^7700BB" + }, + "Record": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" + } + }, + "Core": { + "Value": { + "type": "input", + "value": 0 + }, + "Color": { + "type": "input", + "value": "^AAAAAA" + }, + "Record": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" + } + }, + "Medal": { + "Value": { + "type": "input", + "value": 0 + }, + "Color": { + "type": "input", + "value": "^FFDD00" + }, + "Record": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" + } + }, + "Merit": { + "Value": { + "type": "input", + "value": 0 + }, + "Color": { + "type": "input", + "value": "^FFFF00" + }, + "Record": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" + } + }, + "GuildCoin": { + "Value": { + "type": "input", + "value": 0 + }, + "Color": { + "type": "input", + "value": "^AAAAAA" + }, + "Record": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" + } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } + } + }, "NKAS": { "Emulator": { "Serial": { diff --git a/module/config/argument/dashboard.yaml b/module/config/argument/dashboard.yaml new file mode 100644 index 00000000..93dc59ed --- /dev/null +++ b/module/config/argument/dashboard.yaml @@ -0,0 +1,18 @@ +# -------------------- +# Define argument group of dashboard-only arguments. +# -------------------- + +# ==================== Dashboard ==================== +Dashboard: + - Oil + - Coin + - Gem + - Pt + - Cube + - ActionPoint + - YellowCoin + - PurpleCoin + - Core + - Medal + - Merit + - GuildCoin \ No newline at end of file diff --git a/module/config/argument/gui.yaml b/module/config/argument/gui.yaml index f92bf8ae..55709475 100644 --- a/module/config/argument/gui.yaml +++ b/module/config/argument/gui.yaml @@ -14,6 +14,8 @@ Button: Stop: ScrollON: ScrollOFF: + DashboardON: + DashboardOFF: ClearLog: Setting: CheckUpdate: diff --git a/module/config/config_updater.py b/module/config/config_updater.py index 7d07b2db..9b835ebf 100644 --- a/module/config/config_updater.py +++ b/module/config/config_updater.py @@ -104,6 +104,14 @@ def gui(self): """ return read_file(filepath_argument('gui')) + @cached_property + def dashboard(self): + """ + + - + """ + return read_file(filepath_argument('dashboard')) + @cached_property @timer def args(self): @@ -118,10 +126,12 @@ def args(self): """ # Construct args data = {} - for path, groups in deep_iter(self.task, depth=3): - if 'tasks' not in path: + # Add dashboard to args + dashboard_and_task = {**self.dashboard,**self.task} + for path, groups in deep_iter(dashboard_and_task, depth=3): + if 'tasks' not in path and 'Dashboard' not in path: continue - task = path[2] + task = path[2] if 'tasks' in path else path[0] # Add storage to all task groups.append('Storage') for group in groups: diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index a814890d..dfc83337 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -967,6 +967,8 @@ "Stop": "停止", "ScrollON": "自动滚动 开", "ScrollOFF": "自动滚动 关", + "DashboardON": "折叠", + "DashboardOFF": "展开", "ClearLog": "清空日志", "Setting": "设置", "CheckUpdate": "检查更新", @@ -999,6 +1001,26 @@ }, "Overview": { "Scheduler": "调度器", + "Dashboard": "Gui.Overview.Dashboard", + "SecondsAgo": "秒前", + "MinutesAgo": "分钟前", + "HoursAgo": "小时前", + "DaysAgo": "天前", + "MonthsAgo": "月前", + "YearsAgo": "年前", + "NoData": "无数据", + "Oil": "石油", + "Coin": "物资", + "Gem": "钻石", + "Cube": "魔方", + "Pt": "活动PT", + "YellowCoin": "大世界黄币", + "PurpleCoin": "大世界紫币", + "ActionPoint": "行动力", + "Merit": "功勋", + "Medal": "勋章", + "Core": "核心数据", + "GuildCoin": "舰队币", "Log": "日志", "Running": "运行中", "Pending": "队列中", diff --git a/module/config/utils.py b/module/config/utils.py index 606d2e4a..36baf8f6 100644 --- a/module/config/utils.py +++ b/module/config/utils.py @@ -396,4 +396,35 @@ def random_id(length=32): Returns: str: Random azurstat id. """ - return ''.join(random.sample(string.ascii_lowercase + string.digits, length)) \ No newline at end of file + return ''.join(random.sample(string.ascii_lowercase + string.digits, length)) + +def time_delta(_timedelta): + """ + Output the delta between two times + + Args: + _timedelta : datetime.timedelta + + Returns: + dict : { + 'Y' : int, + 'M' : int, + 'D' : int, + 'h' : int, + 'm' : int, + 's' : int + } + """ + _time_delta = abs(_timedelta.total_seconds()) + d_base = datetime(2010, 1, 1, 0, 0, 0) + d = datetime(2010, 1, 1, 0, 0, 0)-_timedelta + _time_dict = { + 'Y': d.year - d_base.year, + 'M': d.month - d_base.month, + 'D': d.day - d_base.day, + 'h': d.hour - d_base.hour, + 'm': d.minute - d_base.minute, + 's': d.second - d_base.second + } + + return _time_dict \ No newline at end of file diff --git a/module/webui/app.py b/module/webui/app.py index 631adb49..043b45fe 100644 --- a/module/webui/app.py +++ b/module/webui/app.py @@ -79,6 +79,7 @@ re_fullmatch, to_pin_value, ) +from module.config.utils import time_delta from module.log_res.log_res import LogRes from module.webui.widgets import ( BinarySwitchButton,