From d64cfab125dee15b35c6ca237e31021eb3abd76a Mon Sep 17 00:00:00 2001 From: Sassan Haradji Date: Tue, 20 Feb 2024 02:27:08 +0400 Subject: [PATCH] refactor: make `on_title` callback use weakref to avoid memory leaks refactor: use headless-kivy-pi 0.6.0 --- .github/workflows/integration_delivery.yml | 46 ++++++++++-------- CHANGELOG.md | 5 ++ poetry.lock | 56 +++++++++++----------- pyproject.toml | 15 ++---- ubo_gui/app/__init__.py | 27 ++++++----- ubo_gui/menu/__init__.py | 8 +++- ubo_gui/menu/transitions.py | 41 ++++++++++++---- 7 files changed, 116 insertions(+), 82 deletions(-) diff --git a/.github/workflows/integration_delivery.yml b/.github/workflows/integration_delivery.yml index 63cec4a..f84ee6c 100644 --- a/.github/workflows/integration_delivery.yml +++ b/.github/workflows/integration_delivery.yml @@ -16,6 +16,15 @@ jobs: - uses: actions/checkout@v4 name: Checkout + - name: Save Cached Poetry + id: cached-poetry + uses: actions/cache@v4 + with: + path: | + ~/.cache + ~/.local + key: poetry-${{ hashFiles('poetry.lock') }} + - uses: actions/setup-python@v5 name: Setup Python with: @@ -30,15 +39,6 @@ jobs: - name: Install dependencies run: poetry install --extras=dev --with dev - - name: Save Cached Poetry - id: cached-poetry - uses: actions/cache/save@v4 - with: - path: | - ~/.cache - ~/.local - key: poetry-0 - type-check: name: Type Check needs: @@ -61,7 +61,7 @@ jobs: path: | ~/.cache ~/.local - key: poetry-0 + key: poetry-${{ hashFiles('poetry.lock') }} - name: Create stub files run: poetry run pyright --createstub kivy @@ -91,7 +91,7 @@ jobs: path: | ~/.cache ~/.local - key: poetry-0 + key: poetry-${{ hashFiles('poetry.lock') }} - name: Lint run: poetry run poe lint @@ -102,7 +102,8 @@ jobs: - dependencies runs-on: ubuntu-latest outputs: - ubo_app_version: ${{ steps.extract_version.outputs.ubo_app_version }} + version: ${{ steps.extract_version.outputs.version }} + name: ${{ steps.extract_version.outputs.name }} steps: - uses: actions/checkout@v4 name: Checkout @@ -122,7 +123,7 @@ jobs: path: | ~/.cache ~/.local - key: poetry-0 + key: poetry-${{ hashFiles('poetry.lock') }} - name: Build run: poetry build @@ -130,7 +131,8 @@ jobs: - name: Extract Version id: extract_version run: | - echo "ubo_app_version=$(poetry run python scripts/print_version.py)" >> "$GITHUB_OUTPUT" + echo "version=$(poetry version --short)" >> "$GITHUB_OUTPUT" + echo "name=$(poetry version | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" - name: Upload wheel uses: actions/upload-artifact@v4 @@ -156,8 +158,8 @@ jobs: - build runs-on: ubuntu-latest environment: - name: PyPI - url: https://pypi.org/p/ubo-app + name: release + url: https://pypi.org/p/${{ needs.build.outputs.name }} permissions: id-token: write steps: @@ -176,6 +178,7 @@ jobs: with: packages-dir: dist verbose: true + skip-existing: true release: name: Release @@ -184,6 +187,9 @@ jobs: - lint - build - pypi-publish + environment: + name: release + url: https://pypi.org/p/${{ needs.build.outputs.name }} runs-on: ubuntu-latest permissions: contents: write @@ -202,12 +208,12 @@ jobs: path: artifacts - name: Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: files: artifacts/* - tag_name: ${{ needs.build.outputs.ubo_app_version }} + tag_name: v${{ needs.build.outputs.version }} body: | - Release of version ${{ needs.build.outputs.ubo_app_version }} - PyPI package: https://pypi.org/project/ubo-app/${{ needs.build.outputs.ubo_app_version }} + Release of version ${{ needs.build.outputs.version }} + PyPI package: https://pypi.org/project/${{ needs.build.outputs.name }}/${{ needs.build.outputs.version }} prerelease: false draft: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 6933666..44314f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## Version 0.9.6 + +- refactor: use headless-kivy-pi 0.6.0 +- refactor: make `on_title` callback use weakref to avoid memory leaks + ## Version 0.9.5 - chore: use git-lfs to track material font diff --git a/poetry.lock b/poetry.lock index 8ee420b..2ac553b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -323,13 +323,13 @@ files = [ [[package]] name = "headless-kivy-pi" -version = "0.5.12" +version = "0.6.0" description = "Headless renderer for Kivy framework on Raspberry Pi" optional = true python-versions = ">=3.11,<4.0" files = [ - {file = "headless_kivy_pi-0.5.12-py3-none-any.whl", hash = "sha256:e0547de5631611cc995f7431a202ab3ba6c40254b6983935aabc6e97769dfb8a"}, - {file = "headless_kivy_pi-0.5.12.tar.gz", hash = "sha256:29d182fe200f3fe00bbdda1425f36bea33c3c5e618aee31db24f92130a0d6f01"}, + {file = "headless_kivy_pi-0.6.0-py3-none-any.whl", hash = "sha256:d61a0f389adea918598fe3d2728df50892cc0d529c5a2fd435b2870326462c00"}, + {file = "headless_kivy_pi-0.6.0.tar.gz", hash = "sha256:2118d8f3449ae41b5f62b1ce6e94ea0f32ec8012c81fa637e066ac553d764d1d"}, ] [package.dependencies] @@ -555,7 +555,7 @@ files = [ name = "pastel" version = "0.2.1" description = "Bring colors to your terminal." -optional = true +optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, @@ -566,7 +566,7 @@ files = [ name = "poethepoet" version = "0.24.4" description = "A task runner that works well with poetry." -optional = true +optional = false python-versions = ">=3.8" files = [ {file = "poethepoet-0.24.4-py3-none-any.whl", hash = "sha256:fb4ea35d7f40fe2081ea917d2e4102e2310fda2cde78974050ca83896e229075"}, @@ -759,28 +759,28 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "ruff" -version = "0.2.1" +version = "0.2.2" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.2.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:dd81b911d28925e7e8b323e8d06951554655021df8dd4ac3045d7212ac4ba080"}, - {file = "ruff-0.2.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dc586724a95b7d980aa17f671e173df00f0a2eef23f8babbeee663229a938fec"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c92db7101ef5bfc18e96777ed7bc7c822d545fa5977e90a585accac43d22f18a"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:13471684694d41ae0f1e8e3a7497e14cd57ccb7dd72ae08d56a159d6c9c3e30e"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a11567e20ea39d1f51aebd778685582d4c56ccb082c1161ffc10f79bebe6df35"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:00a818e2db63659570403e44383ab03c529c2b9678ba4ba6c105af7854008105"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be60592f9d218b52f03384d1325efa9d3b41e4c4d55ea022cd548547cc42cd2b"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbd2288890b88e8aab4499e55148805b58ec711053588cc2f0196a44f6e3d855"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ef052283da7dec1987bba8d8733051c2325654641dfe5877a4022108098683"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7022d66366d6fded4ba3889f73cd791c2d5621b2ccf34befc752cb0df70f5fad"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0a725823cb2a3f08ee743a534cb6935727d9e47409e4ad72c10a3faf042ad5ba"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0034d5b6323e6e8fe91b2a1e55b02d92d0b582d2953a2b37a67a2d7dedbb7acc"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e5cb5526d69bb9143c2e4d2a115d08ffca3d8e0fddc84925a7b54931c96f5c02"}, - {file = "ruff-0.2.1-py3-none-win32.whl", hash = "sha256:6b95ac9ce49b4fb390634d46d6ece32ace3acdd52814671ccaf20b7f60adb232"}, - {file = "ruff-0.2.1-py3-none-win_amd64.whl", hash = "sha256:e3affdcbc2afb6f5bd0eb3130139ceedc5e3f28d206fe49f63073cb9e65988e0"}, - {file = "ruff-0.2.1-py3-none-win_arm64.whl", hash = "sha256:efababa8e12330aa94a53e90a81eb6e2d55f348bc2e71adbf17d9cad23c03ee6"}, - {file = "ruff-0.2.1.tar.gz", hash = "sha256:3b42b5d8677cd0c72b99fcaf068ffc62abb5a19e71b4a3b9cfa50658a0af02f1"}, + {file = "ruff-0.2.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0a9efb032855ffb3c21f6405751d5e147b0c6b631e3ca3f6b20f917572b97eb6"}, + {file = "ruff-0.2.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d450b7fbff85913f866a5384d8912710936e2b96da74541c82c1b458472ddb39"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecd46e3106850a5c26aee114e562c329f9a1fbe9e4821b008c4404f64ff9ce73"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e22676a5b875bd72acd3d11d5fa9075d3a5f53b877fe7b4793e4673499318ba"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1695700d1e25a99d28f7a1636d85bafcc5030bba9d0578c0781ba1790dbcf51c"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b0c232af3d0bd8f521806223723456ffebf8e323bd1e4e82b0befb20ba18388e"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f63d96494eeec2fc70d909393bcd76c69f35334cdbd9e20d089fb3f0640216ca"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a61ea0ff048e06de273b2e45bd72629f470f5da8f71daf09fe481278b175001"}, + {file = "ruff-0.2.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1439c8f407e4f356470e54cdecdca1bd5439a0673792dbe34a2b0a551a2fe3"}, + {file = "ruff-0.2.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:940de32dc8853eba0f67f7198b3e79bc6ba95c2edbfdfac2144c8235114d6726"}, + {file = "ruff-0.2.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0c126da55c38dd917621552ab430213bdb3273bb10ddb67bc4b761989210eb6e"}, + {file = "ruff-0.2.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3b65494f7e4bed2e74110dac1f0d17dc8e1f42faaa784e7c58a98e335ec83d7e"}, + {file = "ruff-0.2.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1ec49be4fe6ddac0503833f3ed8930528e26d1e60ad35c2446da372d16651ce9"}, + {file = "ruff-0.2.2-py3-none-win32.whl", hash = "sha256:d920499b576f6c68295bc04e7b17b6544d9d05f196bb3aac4358792ef6f34325"}, + {file = "ruff-0.2.2-py3-none-win_amd64.whl", hash = "sha256:cc9a91ae137d687f43a44c900e5d95e9617cb37d4c989e462980ba27039d239d"}, + {file = "ruff-0.2.2-py3-none-win_arm64.whl", hash = "sha256:c9d15fc41e6054bfc7200478720570078f0b41c9ae4f010bcc16bd6f4d1aacdd"}, + {file = "ruff-0.2.2.tar.gz", hash = "sha256:e62ed7f36b3068a30ba39193a14274cd706bc486fad521276458022f7bccb31d"}, ] [[package]] @@ -818,7 +818,7 @@ testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jar name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -optional = true +optional = false python-versions = ">=3.7" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, @@ -838,13 +838,13 @@ files = [ [[package]] name = "urllib3" -version = "2.2.0" +version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = true python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.0-py3-none-any.whl", hash = "sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224"}, - {file = "urllib3-2.2.0.tar.gz", hash = "sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20"}, + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [package.extras] @@ -860,4 +860,4 @@ dev = ["headless-kivy-pi", "headless-kivy-pi"] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "32ed6b0e01c04ee5d7510bbae6d398b733da4978f0f7df201395d2f0283e174c" +content-hash = "3baa684aa87590d4d65b7c925f73aaeee202133aad4c5a5e0e393da93da02ace" diff --git a/pyproject.toml b/pyproject.toml index b799936..03531b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "ubo-gui" -version = "0.9.5" +version = "0.9.6" description = "GUI sdk for Ubo Pod" authors = ["Sassan Haradji "] license = "Apache-2.0" @@ -8,19 +8,13 @@ readme = "README.md" packages = [{ include = "ubo_gui" }] include = ['ubo_gui/assets/fonts/*'] - -[[tool.poetry.source]] -name = "PyPI" -priority = "primary" - - [tool.poetry.dependencies] python = "^3.11" headless-kivy-pi = [ - { version = "^0.5.3", markers = "extra=='default'", extras = [ + { version = "^0.6.0", markers = "extra=='default'", extras = [ 'default', ] }, - { version = "^0.5.3", markers = "extra=='dev'", extras = [ + { version = "^0.6.0", markers = "extra=='dev'", extras = [ 'dev', ] }, ] @@ -30,8 +24,9 @@ python-immutable = "^1.0.2" optional = true [tool.poetry.group.dev.dependencies] +poethepoet = "^0.24.4" pyright = "^1.1.349" -ruff = "^0.2.1" +ruff = "^0.2.2" [tool.poetry.extras] dev = ['headless-kivy-pi'] diff --git a/ubo_gui/app/__init__.py b/ubo_gui/app/__init__.py index f55711c..aef6ab1 100644 --- a/ubo_gui/app/__init__.py +++ b/ubo_gui/app/__init__.py @@ -70,23 +70,26 @@ def central(self: UboApp) -> Widget | None: """The central section of the app.""" return None + def title_callback(self: UboApp, _: RootWidget, title: str) -> None: + """Update the header label when the title changes.""" + if not self: + return + header_layout: BoxLayout = self.root.ids.header_layout + if title is not None: + self.header_label.text = title + header_layout.height = dp(30) + else: + self.header_label.text = '' + header_layout.height = 0 + @cached_property def header(self: UboApp) -> Widget | None: """The header section of the app.""" - header_label = Label(text=self.root.title or '') - - def title_callback(_: RootWidget, title: str) -> None: - header_layout: BoxLayout = self.root.ids.header_layout - if title is not None: - header_label.text = title - header_layout.height = dp(30) - else: - header_label.text = '' - header_layout.height = 0 + self.header_label = Label(text=self.root.title or '') - self.root.bind(title=title_callback) + self.root.bind(title=self.title_callback) - return header_label + return self.header_label @cached_property def footer(self: UboApp) -> Widget | None: diff --git a/ubo_gui/menu/__init__.py b/ubo_gui/menu/__init__.py index d5282c4..8e93020 100644 --- a/ubo_gui/menu/__init__.py +++ b/ubo_gui/menu/__init__.py @@ -282,7 +282,9 @@ def go_back(self: MenuWidget) -> None: """Go back to the previous menu.""" if self.current_application and self.current_application.go_back(): return - HeadlessWidget.activate_high_fps_mode() + headless_widget = HeadlessWidget.get_instance(self) + if headless_widget: + headless_widget.activate_high_fps_mode() self.pop() def render_items(self: MenuWidget, *_: object) -> None: @@ -412,7 +414,9 @@ def set_current_screen(self: MenuWidget, screen: Screen) -> bool: def open_application(self: MenuWidget, application: PageWidget) -> None: """Open an application.""" - HeadlessWidget.activate_high_fps_mode() + headless_widget = HeadlessWidget.get_instance(self) + if headless_widget: + headless_widget.activate_high_fps_mode() self.push( application, transition=self._swap_transition, diff --git a/ubo_gui/menu/transitions.py b/ubo_gui/menu/transitions.py index b252338..968351c 100644 --- a/ubo_gui/menu/transitions.py +++ b/ubo_gui/menu/transitions.py @@ -6,7 +6,7 @@ from typing import Any from headless_kivy_pi import HeadlessWidget -from kivy.clock import Clock +from kivy.clock import Clock, mainthread from kivy.uix.screenmanager import ( NoTransition, Screen, @@ -15,6 +15,7 @@ SwapTransition, TransitionBase, ) +from kivy.uix.widget import Widget class TransitionsMixin: @@ -67,7 +68,10 @@ def _handle_transition_complete( ), ) else: - HeadlessWidget.activate_low_fps_mode() + if isinstance(self, Widget): + headless_widget = HeadlessWidget.get_instance(self) + if headless_widget: + headless_widget.activate_low_fps_mode() self._is_transition_in_progress = False def _setup_transition(self: TransitionsMixin, transition: TransitionBase) -> None: @@ -92,6 +96,22 @@ def _swap_transition(self: TransitionsMixin) -> SwapTransition: self._setup_transition(transition) return transition + def _perform_switch( + self: TransitionsMixin, + screen: Screen | None, + /, + *, + transition: TransitionBase, + duration: float | None = None, + direction: str | None = None, + ) -> None: + self.screen_manager.switch_to( + screen, + transition=transition, + **({'duration': duration} if duration else {}), + **({'direction': direction} if direction else {}), + ) + def _switch_to( self: TransitionsMixin, screen: Screen | None, @@ -104,15 +124,16 @@ def _switch_to( """Switch to a new screen.""" with self._transition_progress_lock: if not self._is_transition_in_progress: - HeadlessWidget.activate_high_fps_mode() + if isinstance(self, Widget): + headless_widget = HeadlessWidget.get_instance(self) + if headless_widget: + headless_widget.activate_high_fps_mode() self._is_transition_in_progress = transition is not self._no_transition - Clock.schedule_once( - lambda *_: self.screen_manager.switch_to( - screen, - transition=transition, - **({'duration': duration} if duration else {}), - **({'direction': direction} if direction else {}), - ), + mainthread(self._perform_switch)( + screen, + transition=transition, + duration=duration, + direction=direction, ) else: self.transition_queue = [