From 0705d56bb49fee6fbfefaa705f68b426511278bb Mon Sep 17 00:00:00 2001 From: Athanasius Date: Sat, 31 Dec 2022 16:08:39 +0000 Subject: [PATCH 1/9] Properly name the main UI tk widgets Unfortunately we can't name the plugin ones, as that's entirely up to their `plugin_app()` code, and widget names can't be changed after creation. NB: Each `plugin_hr_X` frame is `grid()`'d to be *before* the plugin in question, despite being listed *after* in: >>> self.w.children['edmarketconnector'].children --- EDMarketConnector.py | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index fd5bcd5b..556660ce 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -523,15 +523,15 @@ class AppWindow(object): frame.grid(sticky=tk.NSEW) frame.columnconfigure(1, weight=1) - self.cmdr_label = tk.Label(frame) + self.cmdr_label = tk.Label(frame, name='cmdr_label') self.cmdr = tk.Label(frame, compound=tk.RIGHT, anchor=tk.W, name='cmdr') - self.ship_label = tk.Label(frame) + self.ship_label = tk.Label(frame, name='ship_label') self.ship = HyperlinkLabel(frame, compound=tk.RIGHT, url=self.shipyard_url, name='ship') - self.suit_label = tk.Label(frame) + self.suit_label = tk.Label(frame, name='suit_label') self.suit = tk.Label(frame, compound=tk.RIGHT, anchor=tk.W, name='suit') - self.system_label = tk.Label(frame) + self.system_label = tk.Label(frame, name='system_label') self.system = HyperlinkLabel(frame, compound=tk.RIGHT, url=self.system_url, popup_copy=True, name='system') - self.station_label = tk.Label(frame) + self.station_label = tk.Label(frame, name='station_label') self.station = HyperlinkLabel(frame, compound=tk.RIGHT, url=self.station_url, name='station') # system and station text is set/updated by the 'provider' plugins # eddb, edsm and inara. Look for: @@ -560,10 +560,14 @@ class AppWindow(object): self.station.grid(row=ui_row, column=1, sticky=tk.EW) ui_row += 1 + plugin_no = 0 for plugin in plug.PLUGINS: appitem = plugin.get_app(frame) if appitem: - tk.Frame(frame, highlightthickness=1).grid(columnspan=2, sticky=tk.EW) # separator + plugin_no += 1 + tk.Frame( + frame, highlightthickness=1, name=f"plugin_hr_{plugin_no}" + ).grid(columnspan=2, sticky=tk.EW) # separator if isinstance(appitem, tuple) and len(appitem) == 2: ui_row = frame.grid_size()[1] appitem[0].grid(row=ui_row, column=0, sticky=tk.W) @@ -573,8 +577,20 @@ class AppWindow(object): appitem.grid(columnspan=2, sticky=tk.EW) # LANG: Update button in main window - self.button = ttk.Button(frame, text=_('Update'), width=28, default=tk.ACTIVE, state=tk.DISABLED) - self.theme_button = tk.Label(frame, width=32 if sys.platform == 'darwin' else 28, state=tk.DISABLED) + self.button = ttk.Button( + frame, + name='update_button', + text=_('Update'), + width=28, + default=tk.ACTIVE, + state=tk.DISABLED + ) + self.theme_button = tk.Label( + frame, + name='themed_update_button', + width=32 if sys.platform == 'darwin' else 28, + state=tk.DISABLED + ) ui_row = frame.grid_size()[1] self.button.grid(row=ui_row, columnspan=2, sticky=tk.NSEW) From ce1303e9e65035768b3d60265a1f9d8d1f705587 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Sat, 31 Dec 2022 16:16:52 +0000 Subject: [PATCH 2/9] UI: Make a frame to hold each plugin's UI NB: Currently breaks some core/internal plugin code, due to use of `parent.children` and the like. That will get addressed in subsequent commits. --- EDMarketConnector.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index 556660ce..23136e97 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -562,7 +562,12 @@ class AppWindow(object): plugin_no = 0 for plugin in plug.PLUGINS: - appitem = plugin.get_app(frame) + # Per plugin frame + plugin_frame = tk.Frame( + frame, + name=f"plugin_{plugin_no + 1}" + ) + appitem = plugin.get_app(plugin_frame) if appitem: plugin_no += 1 tk.Frame( @@ -576,6 +581,9 @@ class AppWindow(object): else: appitem.grid(columnspan=2, sticky=tk.EW) + else: + plugin_frame.destroy() + # LANG: Update button in main window self.button = ttk.Button( frame, From f2de70bad6d5a56f255e37e0784332d388612452 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Sat, 31 Dec 2022 16:27:22 +0000 Subject: [PATCH 3/9] core plugins: Use `nametowidget()` to find system/station widgets As we're experimenting with per-plugin frames up a level `parent.children` would need to be `parent.parent.children`, but this way it will Just Work without knowledge of the hierarchy. --- plugins/eddb.py | 8 +++++--- plugins/edsm.py | 12 +++++++++--- plugins/inara.py | 8 +++++--- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/plugins/eddb.py b/plugins/eddb.py index 59826a87..a5d0593a 100644 --- a/plugins/eddb.py +++ b/plugins/eddb.py @@ -50,7 +50,7 @@ import EDMCLogging import killswitch import plug from companion import CAPIData -from config import config +from config import appname, config if TYPE_CHECKING: from tkinter import Tk @@ -131,12 +131,14 @@ def plugin_app(parent: 'Tk'): :param parent: The tk parent to place our widgets into. :return: See PLUGINS.md#display """ - this.system_link = parent.children['system'] # system label in main window + # system label in main window + this.system_link = parent.nametowidget(f".{appname.lower()}.system") this.system = None this.system_address = None this.station = None this.station_marketid = None # Frontier MarketID - this.station_link = parent.children['station'] # station label in main window + # station label in main window + this.station_link = parent.nametowidget(f".{appname.lower()}.station") this.station_link['popup_copy'] = lambda x: x != this.STATION_UNDOCKED diff --git a/plugins/edsm.py b/plugins/edsm.py index 635b3559..95d888d8 100644 --- a/plugins/edsm.py +++ b/plugins/edsm.py @@ -49,7 +49,7 @@ import myNotebook import myNotebook as nb # noqa: N813 import plug from companion import CAPIData -from config import applongname, appversion, config, debug_senders, user_agent +from config import applongname, appname, appversion, config, debug_senders, user_agent from edmc_data import DEBUG_WEBSERVER_HOST, DEBUG_WEBSERVER_PORT from EDMCLogging import get_main_logger from ttkHyperlinkLabel import HyperlinkLabel @@ -250,9 +250,15 @@ def plugin_app(parent: tk.Tk) -> None: :param parent: The tk parent to place our widgets into. :return: See PLUGINS.md#display """ - this.system_link = parent.children['system'] # system label in main window + # system label in main window + this.system_link = parent.nametowidget(f".{appname.lower()}.system") + if this.system_link is None: + logger.error("Couldn't look up system widget!!!") + return + this.system_link.bind_all('<>', update_status) - this.station_link = parent.children['station'] # station label in main window + # station label in main window + this.station_link = parent.nametowidget(f".{appname.lower()}.station") def plugin_stop() -> None: diff --git a/plugins/inara.py b/plugins/inara.py index 15fdea5b..941ec8f2 100644 --- a/plugins/inara.py +++ b/plugins/inara.py @@ -43,7 +43,7 @@ import myNotebook as nb # noqa: N813 import plug import timeout_session from companion import CAPIData -from config import applongname, appversion, config, debug_senders +from config import applongname, appname, appversion, config, debug_senders from EDMCLogging import get_main_logger from monitor import monitor from ttkHyperlinkLabel import HyperlinkLabel @@ -218,8 +218,10 @@ def plugin_start3(plugin_dir: str) -> str: def plugin_app(parent: tk.Tk) -> None: """Plugin UI setup Hook.""" this.parent = parent - this.system_link = parent.children['system'] # system label in main window - this.station_link = parent.children['station'] # station label in main window + # system label in main window + this.system_link = parent.nametowidget(f".{appname.lower()}.system") + # station label in main window + this.station_link = parent.nametowidget(f".{appname.lower()}.station") this.system_link.bind_all('<>', update_location) this.system_link.bind_all('<>', update_ship) From 0fd835e9acdcecf2824ea4ff059347fadaf5b277 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Sat, 31 Dec 2022 16:49:29 +0000 Subject: [PATCH 4/9] core plugins: A little more 'children' fixup * eddn: Needs to find status text, although really there should be a helper function for this. * Removed commented out references to same in edsm and inara. --- plugins/eddn.py | 6 +++--- plugins/edsm.py | 1 - plugins/inara.py | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/eddn.py b/plugins/eddn.py index 96c839ad..13bdf108 100644 --- a/plugins/eddn.py +++ b/plugins/eddn.py @@ -46,7 +46,7 @@ import killswitch import myNotebook as nb # noqa: N813 import plug from companion import CAPIData, category_map -from config import applongname, appversion_nobuild, config, debug_senders, user_agent +from config import applongname, appname, appversion_nobuild, config, debug_senders, user_agent from EDMCLogging import get_main_logger from monitor import monitor from myNotebook import Frame @@ -382,7 +382,7 @@ class EDDNSender: logger.info(text) return - self.eddn.parent.children['status']['text'] = text + self.eddn.parent.nametowidget(f".{appname.lower()}.status")['text'] = text def send_message(self, msg: str) -> bool: """ @@ -2501,7 +2501,7 @@ def cmdr_data(data: CAPIData, is_beta: bool) -> Optional[str]: # noqa: CCR001 this.commodities = this.outfitting = this.shipyard = None this.marketId = data['lastStarport']['id'] - status = this.parent.children['status'] + status = this.parent.nametowidget(f".{appname.lower()}.status") old_status = status['text'] if not old_status: status['text'] = _('Sending data to EDDN...') # LANG: Status text shown while attempting to send data diff --git a/plugins/edsm.py b/plugins/edsm.py index 95d888d8..9eb9167a 100644 --- a/plugins/edsm.py +++ b/plugins/edsm.py @@ -628,7 +628,6 @@ entry: {entry!r}''' ): # LANG: The Inara API only accepts Live galaxy data, not Legacy galaxy data logger.info("EDSM only accepts Live galaxy data") - # this.parent.children['status']['text'] = this.legacy_galaxy_last_notified = datetime.now(timezone.utc) return _("EDSM only accepts Live galaxy data") diff --git a/plugins/inara.py b/plugins/inara.py index 941ec8f2..73ee482e 100644 --- a/plugins/inara.py +++ b/plugins/inara.py @@ -382,7 +382,6 @@ def journal_entry( # noqa: C901, CCR001 ): # LANG: The Inara API only accepts Live galaxy data, not Legacy galaxy data logger.info(_("Inara only accepts Live galaxy data")) - # this.parent.children['status']['text'] = this.legacy_galaxy_last_notified = datetime.now(timezone.utc) return _("Inara only accepts Live galaxy data") From a842ce28c618fc1b99bb4bd9aa0a46bfd1c92350 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Sat, 31 Dec 2022 16:52:36 +0000 Subject: [PATCH 5/9] UI: Create a per-plugin frame in the main UI * This means all actual plugin UI will be children of this frame, rather than the main frame. --- EDMarketConnector.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index 23136e97..13b4139f 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -536,7 +536,8 @@ class AppWindow(object): # system and station text is set/updated by the 'provider' plugins # eddb, edsm and inara. Look for: # - # parent.children['system'] / parent.children['station'] + # parent.nametowidget(f".{appname.lower()}.system") + # parent.nametowidget(f".{appname.lower()}.station") ui_row = 1 @@ -562,7 +563,11 @@ class AppWindow(object): plugin_no = 0 for plugin in plug.PLUGINS: - # Per plugin frame + # Per plugin separator + plugin_sep = tk.Frame( + frame, highlightthickness=1, name=f"plugin_hr_{plugin_no + 1}" + ) + # Per plugin frame, for it to use as its parent for own widgets plugin_frame = tk.Frame( frame, name=f"plugin_{plugin_no + 1}" @@ -570,9 +575,11 @@ class AppWindow(object): appitem = plugin.get_app(plugin_frame) if appitem: plugin_no += 1 - tk.Frame( - frame, highlightthickness=1, name=f"plugin_hr_{plugin_no}" - ).grid(columnspan=2, sticky=tk.EW) # separator + plugin_sep.grid(columnspan=2, sticky=tk.EW) + ui_row = frame.grid_size()[1] + plugin_frame.grid( + row=ui_row, columnspan=2, sticky=tk.EW + ) if isinstance(appitem, tuple) and len(appitem) == 2: ui_row = frame.grid_size()[1] appitem[0].grid(row=ui_row, column=0, sticky=tk.W) @@ -582,7 +589,9 @@ class AppWindow(object): appitem.grid(columnspan=2, sticky=tk.EW) else: + # This plugin didn't provide any UI, so drop the frames plugin_frame.destroy() + plugin_sep.destroy() # LANG: Update button in main window self.button = ttk.Button( From 963b115e0e6d971a1e2e3c18bd20c895960aff0f Mon Sep 17 00:00:00 2001 From: Athanasius Date: Sat, 31 Dec 2022 17:12:29 +0000 Subject: [PATCH 6/9] UI: Name the extra 'alternate' frames: titlebar and menubar These are used in the non-default themes, as the OS ones are hidden and replaced by them. They're created after the rest of the main UI, so come last in the 'children' list. --- EDMarketConnector.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index 13b4139f..ffdb8ab9 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -718,11 +718,15 @@ class AppWindow(object): theme.register(self.help_menu) # Alternate title bar and menu for dark theme - self.theme_menubar = tk.Frame(frame) + self.theme_menubar = tk.Frame(frame, name="alternate_menubar") self.theme_menubar.columnconfigure(2, weight=1) - theme_titlebar = tk.Label(self.theme_menubar, text=applongname, - image=self.theme_icon, cursor='fleur', - anchor=tk.W, compound=tk.LEFT) + theme_titlebar = tk.Label( + self.theme_menubar, + name="alternate_titlebar", + text=applongname, + image=self.theme_icon, cursor='fleur', + anchor=tk.W, compound=tk.LEFT + ) theme_titlebar.grid(columnspan=3, padx=2, sticky=tk.NSEW) self.drag_offset: Tuple[Optional[int], Optional[int]] = (None, None) theme_titlebar.bind('', self.drag_start) @@ -755,7 +759,7 @@ class AppWindow(object): tk.Frame(self.theme_menubar, highlightthickness=1).grid(columnspan=5, padx=self.PADX, sticky=tk.EW) theme.register(self.theme_minimize) # images aren't automatically registered theme.register(self.theme_close) - self.blank_menubar = tk.Frame(frame) + self.blank_menubar = tk.Frame(frame, name="blank_menubar") tk.Label(self.blank_menubar).grid() tk.Label(self.blank_menubar).grid() tk.Frame(self.blank_menubar, height=2).grid() From 991f69044cf9dbdaef3e63ee5328ebe9923b8ab0 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Sat, 31 Dec 2022 17:20:30 +0000 Subject: [PATCH 7/9] PLUGINS.md: Avoid an example implying plugins can set main status text The 'Error messages' section lays out how to get a message into the main UI status area, and it does *not* say to try setting it directly. --- PLUGINS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PLUGINS.md b/PLUGINS.md index eb5cf57c..cd0d47bc 100644 --- a/PLUGINS.md +++ b/PLUGINS.md @@ -1138,7 +1138,7 @@ _ = functools.partial(l10n.Translations.translate, context=__file__) Wrap each string that needs translating with the `_()` function, e.g.: ```python - status["text"] = _('Happy!') # Main window status + somewidget["text"] = _("Happy!") ``` If you display localized strings in EDMarketConnector's main window you should From 61ad75d52a44f3b26c4a93a61ea02e6900451ddd Mon Sep 17 00:00:00 2001 From: Athanasius Date: Mon, 2 Jan 2023 13:02:22 +0000 Subject: [PATCH 8/9] Configure per-plugin Frame the same as main Frame It was observed that some text in the EDMC-Canonn plugin was wrapping in this branch when the same text doesn't in `develop. So, tweaked the plugin_frame setup to match the main UI frame setup: * `tk.NSEW` not `tk.EW` - didn't fix the problem, but we should match this. * `.columnconfigure(1, weight=1)` - this actually fixed the problem. --- EDMarketConnector.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index ffdb8ab9..d901b448 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -578,8 +578,9 @@ class AppWindow(object): plugin_sep.grid(columnspan=2, sticky=tk.EW) ui_row = frame.grid_size()[1] plugin_frame.grid( - row=ui_row, columnspan=2, sticky=tk.EW + row=ui_row, columnspan=2, sticky=tk.NSEW ) + # plugin_frame.columnconfigure(1, weight=1) if isinstance(appitem, tuple) and len(appitem) == 2: ui_row = frame.grid_size()[1] appitem[0].grid(row=ui_row, column=0, sticky=tk.W) From 7bfeb5ec621a965a17164fb753934b5268715f84 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Mon, 2 Jan 2023 14:05:07 +0000 Subject: [PATCH 9/9] Actually uncomment `plugin_frame.columnconfigure(1, weight=1)` It had been commented to test that it made a difference. --- EDMarketConnector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index d901b448..94d7ec07 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -580,7 +580,7 @@ class AppWindow(object): plugin_frame.grid( row=ui_row, columnspan=2, sticky=tk.NSEW ) - # plugin_frame.columnconfigure(1, weight=1) + plugin_frame.columnconfigure(1, weight=1) if isinstance(appitem, tuple) and len(appitem) == 2: ui_row = frame.grid_size()[1] appitem[0].grid(row=ui_row, column=0, sticky=tk.W)