From 9537e251a95c2cfac1884f8f9cf0e6e497f4de9e Mon Sep 17 00:00:00 2001 From: alwaysintreble Date: Tue, 15 Apr 2025 02:09:33 -0500 Subject: [PATCH 1/9] implement tabs as NavigationBar --- data/client.kv | 16 ++++-- kvui.py | 129 ++++++++++++++++++++----------------------------- 2 files changed, 66 insertions(+), 79 deletions(-) diff --git a/data/client.kv b/data/client.kv index 562986cd17a4..baea8fea1f78 100644 --- a/data/client.kv +++ b/data/client.kv @@ -24,9 +24,19 @@ : ripple_color: app.theme_cls.primaryColor ripple_duration_in_fast: 0.2 -: - ripple_color: app.theme_cls.primaryColor - ripple_duration_in_fast: 0.2 + + set_bars_color: True +: + on_press: app.screens.switch_screens(self) + active: False + label: label + + MDNavigationItemLabel: + id: label + text: root.text + theme_text_color: "Custom" + text_color_active: app.theme_cls.primaryColor + text_color_normal: 1, 1, 1, 1 : adaptive_height: True theme_font_size: "Custom" diff --git a/kvui.py b/kvui.py index 9a8b7109fa66..4bbf9d264dcf 100644 --- a/kvui.py +++ b/kvui.py @@ -43,6 +43,7 @@ from kivy.base import ExceptionHandler, ExceptionManager from kivy.clock import Clock from kivy.factory import Factory +from kivy.graphics import Color, Line from kivy.properties import BooleanProperty, ObjectProperty, NumericProperty, StringProperty from kivy.metrics import dp, sp from kivy.uix.widget import Widget @@ -60,7 +61,10 @@ from kivymd.uix.gridlayout import MDGridLayout from kivymd.uix.floatlayout import MDFloatLayout from kivymd.uix.boxlayout import MDBoxLayout -from kivymd.uix.tab.tab import MDTabsSecondary, MDTabsItem, MDTabsItemText, MDTabsCarousel +from kivymd.uix.navigationbar import MDNavigationBar, MDNavigationItem +from kivymd.uix.screen import MDScreen +from kivymd.uix.screenmanager import MDScreenManager + from kivymd.uix.menu import MDDropdownMenu from kivymd.uix.menu.menu import MDDropdownTextItem from kivymd.uix.dropdownitem import MDDropDownItem, MDDropDownItemText @@ -722,59 +726,30 @@ def __init__(self, title, text, error=False, **kwargs): self.height += max(0, label.height - 18) -class ClientTabs(MDTabsSecondary): - carousel: MDTabsCarousel - lock_swiping = True - - def __init__(self, *args, **kwargs): - self.carousel = MDTabsCarousel(lock_swiping=True, anim_move_duration=0.2) - super().__init__(*args, MDDivider(size_hint_y=None, height=dp(1)), self.carousel, **kwargs) - self.size_hint_y = 1 - - def _check_panel_height(self, *args): - self.ids.tab_scroll.height = dp(38) - - def update_indicator( - self, x: float = 0.0, w: float = 0.0, instance: MDTabsItem = None - ) -> None: - def update_indicator(*args): - indicator_pos = (0, 0) - indicator_size = (0, 0) - - item_text_object = self._get_tab_item_text_icon_object() - - if item_text_object: - indicator_pos = ( - instance.x + dp(12), - self.indicator.pos[1] - if not self._tabs_carousel - else self._tabs_carousel.height, - ) - indicator_size = ( - instance.width - dp(24), - self.indicator_height, - ) +class MDNavigationItemBase(MDNavigationItem): + text = StringProperty(None) - Animation( - pos=indicator_pos, - size=indicator_size, - d=0 if not self.indicator_anim else self.indicator_duration, - t=self.indicator_transition, - ).start(self.indicator) - - if not instance: - self.indicator.pos = (x, self.indicator.pos[1]) - self.indicator.size = (w, self.indicator_height) +class MDNavigationItemUnderline(Widget): + def __init__(self, **kwargs): + super().__init__(size_hint=(None, None), height=2, **kwargs) + with self.canvas.before: + Color(0, 0, 0, 0) + self.line = Line(width=2) + +class MDScreenManagerBase(MDScreenManager): + underline_bar: MDNavigationItemUnderline + def switch_screens(self, new_tab: MDNavigationItemBase) -> None: + name = new_tab.text + if self.screen_names.index(name) > self.screen_names.index(self.current_screen.name): + self.transition.direction = "left" else: - Clock.schedule_once(update_indicator) - - def remove_tab(self, tab, content=None): - if content is None: - content = tab.content - self.ids.container.remove_widget(tab) - self.carousel.remove_widget(content) - self.on_size(self, self.size) + self.transition.direction = "right" + self.current = name + self.update_underline(new_tab) + def update_underline(self, tab: MDNavigationItemBase) -> None: + self.underline_bar.line.points = [tab.x, tab.y, tab.right, tab.y] + self.underline_bar.canvas.before.children[0].rgba = MDApp.get_running_app().theme_cls.primaryColor class CommandButton(MDButton, MDTooltip): def __init__(self, *args, manager: "GameManager", **kwargs): @@ -801,6 +776,9 @@ class GameManager(ThemedApp): main_area_container: MDGridLayout """ subclasses can add more columns beside the tabs """ + tabs: MDNavigationBar + screens: MDScreenManagerBase + def __init__(self, ctx: context_type): self.title = self.base_title self.ctx = ctx @@ -830,7 +808,7 @@ def intercept_say(text): @property def tab_count(self): if hasattr(self, "tabs"): - return max(1, len(self.tabs.tab_list)) + return max(1, len(self.tabs.children)) return 1 def on_start(self): @@ -870,30 +848,29 @@ def connect_bar_validate(sender): self.grid.add_widget(self.progressbar) # middle part - self.tabs = ClientTabs(pos_hint={"center_x": 0.5, "center_y": 0.5}) - self.tabs.add_widget(MDTabsItem(MDTabsItemText(text="All" if len(self.logging_pairs) > 1 else "Archipelago"))) - self.log_panels["All"] = self.tabs.default_tab_content = UILog(*(logging.getLogger(logger_name) - for logger_name, name in - self.logging_pairs)) - self.tabs.carousel.add_widget(self.tabs.default_tab_content) + self.screens = MDScreenManagerBase(pos_hint={"center_x": 0.5}) + self.tabs = MDNavigationBar(orientation="horizontal", size_hint_y=None, height=dp(40), set_bars_color=True) + log_panel = self.log_panels["All"] = self.add_client_tab( + "All" if len(self.logging_pairs) > 1 else "Archipelago", + UILog(*(logging.getLogger(logger_name) for logger_name, name in self.logging_pairs)), + ) + log_panel.active = True + self.screens.underline_bar = MDNavigationItemUnderline() for logger_name, display_name in self.logging_pairs: bridge_logger = logging.getLogger(logger_name) self.log_panels[display_name] = UILog(bridge_logger) if len(self.logging_pairs) > 1: - panel = MDTabsItem(MDTabsItemText(text=display_name)) - panel.content = self.log_panels[display_name] - # show Archipelago tab if other logging is present - self.tabs.carousel.add_widget(panel.content) - self.tabs.add_widget(panel) + self.add_client_tab(display_name, self.log_panels[display_name]) - hint_panel = self.add_client_tab("Hints", HintLayout()) self.hint_log = HintLog(self.json_to_kivy_parser) + hint_panel = self.add_client_tab("Hints", HintLayout(self.hint_log)) self.log_panels["Hints"] = hint_panel.content - hint_panel.content.add_widget(self.hint_log) - self.main_area_container = MDGridLayout(size_hint_y=1, rows=1) + self.main_area_container = MDGridLayout(size_hint_y=1, cols=1) self.main_area_container.add_widget(self.tabs) + self.main_area_container.add_widget(self.screens.underline_bar) + self.main_area_container.add_widget(self.screens) self.grid.add_widget(self.main_area_container) @@ -933,22 +910,22 @@ def connect_bar_validate(sender): def add_client_tab(self, title: str, content: Widget, index: int = -1) -> Widget: """Adds a new tab to the client window with a given title, and provides a given Widget as its content. Returns the new tab widget, with the provided content being placed on the tab as content.""" - new_tab = MDTabsItem(MDTabsItemText(text=title)) + if self.tabs.children: + self.tabs.add_widget(MDDivider(orientation="vertical")) + new_tab = MDNavigationItemBase(text=title) new_tab.content = content - if -1 < index <= len(self.tabs.carousel.slides): - new_tab.bind(on_release=self.tabs.set_active_item) - new_tab._tabs = self.tabs - self.tabs.ids.container.add_widget(new_tab, index=index) - self.tabs.carousel.add_widget(new_tab.content, index=len(self.tabs.carousel.slides) - index) + new_screen = MDScreen(name=title) + new_screen.add_widget(content) + if -1 < index <= len(self.tabs.children): + index = len(self.tabs.children) - index + self.tabs.add_widget(new_tab, index=index) + self.screens.add_widget(new_screen, index=index) else: self.tabs.add_widget(new_tab) - self.tabs.carousel.add_widget(new_tab.content) + self.screens.add_widget(new_screen) return new_tab def update_texts(self, dt): - for slide in self.tabs.carousel.slides: - if hasattr(slide, "fix_heights"): - slide.fix_heights() # TODO: remove this when Kivy fixes this upstream if self.ctx.server: self.title = self.base_title + " " + Utils.__version__ + \ f" | Connected to: {self.ctx.server_address} " \ From 2d067d0303e409902744d9a7ac68952ce112a690 Mon Sep 17 00:00:00 2001 From: alwaysintreble Date: Thu, 17 Apr 2025 17:58:05 -0500 Subject: [PATCH 2/9] update the underline bar with the screen manager --- kvui.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/kvui.py b/kvui.py index 4bbf9d264dcf..72db32124988 100644 --- a/kvui.py +++ b/kvui.py @@ -738,16 +738,27 @@ def __init__(self, **kwargs): class MDScreenManagerBase(MDScreenManager): underline_bar: MDNavigationItemUnderline + current_tab: MDNavigationItemBase + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.bind(size=self.update_underline, pos=self.update_underline) + def switch_screens(self, new_tab: MDNavigationItemBase) -> None: + name = new_tab.text if self.screen_names.index(name) > self.screen_names.index(self.current_screen.name): self.transition.direction = "left" else: self.transition.direction = "right" self.current = name - self.update_underline(new_tab) + self.current_tab = new_tab + self.update_underline() - def update_underline(self, tab: MDNavigationItemBase) -> None: + def update_underline(self, *args) -> None: + tab = self.current_tab + if self.underline_bar is None or tab is None: + return self.underline_bar.line.points = [tab.x, tab.y, tab.right, tab.y] self.underline_bar.canvas.before.children[0].rgba = MDApp.get_running_app().theme_cls.primaryColor @@ -850,11 +861,11 @@ def connect_bar_validate(sender): # middle part self.screens = MDScreenManagerBase(pos_hint={"center_x": 0.5}) self.tabs = MDNavigationBar(orientation="horizontal", size_hint_y=None, height=dp(40), set_bars_color=True) - log_panel = self.log_panels["All"] = self.add_client_tab( + self.screens.current_tab = self.log_panels["All"] = self.add_client_tab( "All" if len(self.logging_pairs) > 1 else "Archipelago", UILog(*(logging.getLogger(logger_name) for logger_name, name in self.logging_pairs)), ) - log_panel.active = True + self.log_panels["All"].active = True self.screens.underline_bar = MDNavigationItemUnderline() for logger_name, display_name in self.logging_pairs: From 3a5e62ac4a3395d9d080efb9d014f83895dfcf32 Mon Sep 17 00:00:00 2001 From: alwaysintreble Date: Fri, 18 Apr 2025 17:11:07 -0500 Subject: [PATCH 3/9] remove some unneeded kv --- data/client.kv | 5 ----- 1 file changed, 5 deletions(-) diff --git a/data/client.kv b/data/client.kv index baea8fea1f78..b8ce0455f5ba 100644 --- a/data/client.kv +++ b/data/client.kv @@ -24,15 +24,10 @@ : ripple_color: app.theme_cls.primaryColor ripple_duration_in_fast: 0.2 - - set_bars_color: True : on_press: app.screens.switch_screens(self) - active: False - label: label MDNavigationItemLabel: - id: label text: root.text theme_text_color: "Custom" text_color_active: app.theme_cls.primaryColor From 35b300bb0ba30c387e3043ddc496ecbf535a283d Mon Sep 17 00:00:00 2001 From: alwaysintreble Date: Thu, 1 May 2025 17:13:23 -0500 Subject: [PATCH 4/9] remove the underline in favor of a full tab highlight --- data/client.kv | 8 +++++++- kvui.py | 22 ---------------------- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/data/client.kv b/data/client.kv index b8ce0455f5ba..e23802454dff 100644 --- a/data/client.kv +++ b/data/client.kv @@ -30,8 +30,14 @@ MDNavigationItemLabel: text: root.text theme_text_color: "Custom" - text_color_active: app.theme_cls.primaryColor + text_color_active: self.theme_cls.primaryColor text_color_normal: 1, 1, 1, 1 + # indicator is on icon only for some reason + canvas.before: + Color: + rgba: self.theme_cls.secondaryContainerColor if root.active else self.theme_cls.transparentColor + Rectangle: + size: root.size : adaptive_height: True theme_font_size: "Custom" diff --git a/kvui.py b/kvui.py index 72db32124988..0d671c4c3d00 100644 --- a/kvui.py +++ b/kvui.py @@ -729,20 +729,9 @@ def __init__(self, title, text, error=False, **kwargs): class MDNavigationItemBase(MDNavigationItem): text = StringProperty(None) -class MDNavigationItemUnderline(Widget): - def __init__(self, **kwargs): - super().__init__(size_hint=(None, None), height=2, **kwargs) - with self.canvas.before: - Color(0, 0, 0, 0) - self.line = Line(width=2) - class MDScreenManagerBase(MDScreenManager): - underline_bar: MDNavigationItemUnderline current_tab: MDNavigationItemBase - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.bind(size=self.update_underline, pos=self.update_underline) def switch_screens(self, new_tab: MDNavigationItemBase) -> None: @@ -753,14 +742,6 @@ def switch_screens(self, new_tab: MDNavigationItemBase) -> None: self.transition.direction = "right" self.current = name self.current_tab = new_tab - self.update_underline() - - def update_underline(self, *args) -> None: - tab = self.current_tab - if self.underline_bar is None or tab is None: - return - self.underline_bar.line.points = [tab.x, tab.y, tab.right, tab.y] - self.underline_bar.canvas.before.children[0].rgba = MDApp.get_running_app().theme_cls.primaryColor class CommandButton(MDButton, MDTooltip): def __init__(self, *args, manager: "GameManager", **kwargs): @@ -866,7 +847,6 @@ def connect_bar_validate(sender): UILog(*(logging.getLogger(logger_name) for logger_name, name in self.logging_pairs)), ) self.log_panels["All"].active = True - self.screens.underline_bar = MDNavigationItemUnderline() for logger_name, display_name in self.logging_pairs: bridge_logger = logging.getLogger(logger_name) @@ -880,7 +860,6 @@ def connect_bar_validate(sender): self.main_area_container = MDGridLayout(size_hint_y=1, cols=1) self.main_area_container.add_widget(self.tabs) - self.main_area_container.add_widget(self.screens.underline_bar) self.main_area_container.add_widget(self.screens) self.grid.add_widget(self.main_area_container) @@ -928,7 +907,6 @@ def add_client_tab(self, title: str, content: Widget, index: int = -1) -> Widget new_screen = MDScreen(name=title) new_screen.add_widget(content) if -1 < index <= len(self.tabs.children): - index = len(self.tabs.children) - index self.tabs.add_widget(new_tab, index=index) self.screens.add_widget(new_screen, index=index) else: From 619f1146c055647ceeac17535f335c145c477da7 Mon Sep 17 00:00:00 2001 From: Silvris <58583688+Silvris@users.noreply.github.com> Date: Thu, 8 May 2025 21:36:54 -0500 Subject: [PATCH 5/9] fix insert transitions --- kvui.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/kvui.py b/kvui.py index 0d671c4c3d00..7ef87bf240b9 100644 --- a/kvui.py +++ b/kvui.py @@ -731,12 +731,25 @@ class MDNavigationItemBase(MDNavigationItem): class MDScreenManagerBase(MDScreenManager): current_tab: MDNavigationItemBase + local_screen_names: list[str] + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.bind(size=self.update_underline, pos=self.update_underline) + self.local_screen_names = [] + + def add_widget(self, widget, *args, **kwargs): + super().add_widget(widget, *args, **kwargs) + index = kwargs.get("index", -1) + if index > -1: + self.local_screen_names.insert(index, widget.name) + else: + self.local_screen_names.append(widget.name) def switch_screens(self, new_tab: MDNavigationItemBase) -> None: name = new_tab.text - if self.screen_names.index(name) > self.screen_names.index(self.current_screen.name): + if self.local_screen_names.index(name) > self.local_screen_names.index(self.current_screen.name): self.transition.direction = "left" else: self.transition.direction = "right" @@ -842,11 +855,12 @@ def connect_bar_validate(sender): # middle part self.screens = MDScreenManagerBase(pos_hint={"center_x": 0.5}) self.tabs = MDNavigationBar(orientation="horizontal", size_hint_y=None, height=dp(40), set_bars_color=True) - self.screens.current_tab = self.log_panels["All"] = self.add_client_tab( + self.screens.current_tab = self.add_client_tab( "All" if len(self.logging_pairs) > 1 else "Archipelago", UILog(*(logging.getLogger(logger_name) for logger_name, name in self.logging_pairs)), ) - self.log_panels["All"].active = True + self.log_panels["All"] = self.screens.current_tab.content + self.screens.current_tab.active = True for logger_name, display_name in self.logging_pairs: bridge_logger = logging.getLogger(logger_name) @@ -907,7 +921,8 @@ def add_client_tab(self, title: str, content: Widget, index: int = -1) -> Widget new_screen = MDScreen(name=title) new_screen.add_widget(content) if -1 < index <= len(self.tabs.children): - self.tabs.add_widget(new_tab, index=index) + remapped_index = len(self.tabs.children) - index + self.tabs.add_widget(new_tab, index=remapped_index) self.screens.add_widget(new_screen, index=index) else: self.tabs.add_widget(new_tab) From 2198fd38d8d60c52c44bd4fb46380ceb83c5f8f2 Mon Sep 17 00:00:00 2001 From: alwaysintreble Date: Thu, 8 May 2025 22:34:36 -0500 Subject: [PATCH 6/9] use on_release instead of on_press --- data/client.kv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/client.kv b/data/client.kv index e23802454dff..48b81bb3d436 100644 --- a/data/client.kv +++ b/data/client.kv @@ -25,7 +25,7 @@ ripple_color: app.theme_cls.primaryColor ripple_duration_in_fast: 0.2 : - on_press: app.screens.switch_screens(self) + on_release: app.screens.switch_screens(self) MDNavigationItemLabel: text: root.text From 00b101d6eb8a21f7be773a476f38677f15b37155 Mon Sep 17 00:00:00 2001 From: alwaysintreble Date: Thu, 8 May 2025 22:35:05 -0500 Subject: [PATCH 7/9] minor cleanup --- kvui.py | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/kvui.py b/kvui.py index 7ef87bf240b9..3662d792b707 100644 --- a/kvui.py +++ b/kvui.py @@ -729,25 +729,28 @@ def __init__(self, title, text, error=False, **kwargs): class MDNavigationItemBase(MDNavigationItem): text = StringProperty(None) + class MDScreenManagerBase(MDScreenManager): current_tab: MDNavigationItemBase local_screen_names: list[str] def __init__(self, **kwargs): super().__init__(**kwargs) - self.bind(size=self.update_underline, pos=self.update_underline) self.local_screen_names = [] - def add_widget(self, widget, *args, **kwargs): + def add_widget(self, widget: Widget, *args, **kwargs) -> None: super().add_widget(widget, *args, **kwargs) - index = kwargs.get("index", -1) - if index > -1: - self.local_screen_names.insert(index, widget.name) + if "index" in kwargs: + self.local_screen_names.insert(kwargs["index"], widget.name) else: self.local_screen_names.append(widget.name) def switch_screens(self, new_tab: MDNavigationItemBase) -> None: + """ + Called whenever the user clicks a tab to switch to a different screen. + :param new_tab: The new screen to switch to's tab. + """ name = new_tab.text if self.local_screen_names.index(name) > self.local_screen_names.index(self.current_screen.name): self.transition.direction = "left" @@ -756,6 +759,7 @@ def switch_screens(self, new_tab: MDNavigationItemBase) -> None: self.current = name self.current_tab = new_tab + class CommandButton(MDButton, MDTooltip): def __init__(self, *args, manager: "GameManager", **kwargs): super().__init__(*args, **kwargs) @@ -911,9 +915,18 @@ def connect_bar_validate(sender): return self.container - def add_client_tab(self, title: str, content: Widget, index: int = -1) -> Widget: - """Adds a new tab to the client window with a given title, and provides a given Widget as its content. - Returns the new tab widget, with the provided content being placed on the tab as content.""" + def add_client_tab(self, title: str, content: Widget, index: int = -1) -> MDNavigationItemBase: + """ + Adds a new tab to the client window with a given title, and provides a given Widget as its content. + Returns the new tab widget, with the provided content being placed on the tab as content. + + :param title: The title of the tab. + :param content: The Widget to be added as content for this tab's new MDScreen. Will also be added to the + returned tab as tab.content. + :param index: The index to insert the tab at. Defaults to -1, meaning the tab will be appended to the end. + + :return: The new tab. + """ if self.tabs.children: self.tabs.add_widget(MDDivider(orientation="vertical")) new_tab = MDNavigationItemBase(text=title) From 263d1340e9da13f57c7d66018318c92ff2239e77 Mon Sep 17 00:00:00 2001 From: alwaysintreble Date: Thu, 8 May 2025 22:40:35 -0500 Subject: [PATCH 8/9] add remove_client_tab and add a caller to the NavigationBar for back compat --- kvui.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/kvui.py b/kvui.py index 3662d792b707..9b723ae6eb28 100644 --- a/kvui.py +++ b/kvui.py @@ -859,6 +859,8 @@ def connect_bar_validate(sender): # middle part self.screens = MDScreenManagerBase(pos_hint={"center_x": 0.5}) self.tabs = MDNavigationBar(orientation="horizontal", size_hint_y=None, height=dp(40), set_bars_color=True) + # bind the method to the bar for back compatibility + self.tabs.remove_tab = self.remove_client_tab self.screens.current_tab = self.add_client_tab( "All" if len(self.logging_pairs) > 1 else "Archipelago", UILog(*(logging.getLogger(logger_name) for logger_name, name in self.logging_pairs)), @@ -942,6 +944,31 @@ def add_client_tab(self, title: str, content: Widget, index: int = -1) -> MDNavi self.screens.add_widget(new_screen) return new_tab + def remove_client_tab(self, tab: MDNavigationItemBase) -> None: + """ + Called to remove a tab and its screen. + + :param tab: The tab to remove. + """ + tab_index = self.tabs.children.index(tab) + # if the tab is currently active we need to swap before removing it + if tab == self.screens.current_tab: + if not tab_index: + # account for the divider + swap_index = tab_index + 2 + else: + swap_index = tab_index - 2 + self.tabs.children[swap_index].on_release() + # self.screens.switch_screens(self.tabs.children[swap_index]) + # get the divider to the left if we can + if not tab_index: + divider_index = tab_index + 1 + else: + divider_index = tab_index - 1 + self.tabs.remove_widget(self.tabs.children[divider_index]) + self.tabs.remove_widget(tab) + self.screens.remove_widget(self.screens.get_screen(tab.text)) + def update_texts(self, dt): if self.ctx.server: self.title = self.base_title + " " + Utils.__version__ + \ From b0aeeca7da7691fbdf2b2efbeb62b81dbb48099c Mon Sep 17 00:00:00 2001 From: alwaysintreble Date: Thu, 8 May 2025 22:45:22 -0500 Subject: [PATCH 9/9] unused imports --- kvui.py | 1 - 1 file changed, 1 deletion(-) diff --git a/kvui.py b/kvui.py index 9b723ae6eb28..6fb0e465ef1b 100644 --- a/kvui.py +++ b/kvui.py @@ -43,7 +43,6 @@ from kivy.base import ExceptionHandler, ExceptionManager from kivy.clock import Clock from kivy.factory import Factory -from kivy.graphics import Color, Line from kivy.properties import BooleanProperty, ObjectProperty, NumericProperty, StringProperty from kivy.metrics import dp, sp from kivy.uix.widget import Widget