pythonnative 0.2.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pythonnative-0.2.0 → pythonnative-0.4.0}/LICENSE +1 -1
- pythonnative-0.4.0/PKG-INFO +143 -0
- pythonnative-0.4.0/README.md +80 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/pyproject.toml +28 -1
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/__init__.py +1 -1
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/button.py +14 -10
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/cli/pn.py +11 -13
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/date_picker.py +8 -4
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/image_view.py +5 -3
- pythonnative-0.4.0/src/pythonnative/label.py +133 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/list_view.py +7 -4
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/material_activity_indicator_view.py +3 -1
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/material_button.py +8 -4
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/material_date_picker.py +5 -3
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/material_progress_view.py +8 -4
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/material_search_bar.py +8 -4
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/material_switch.py +8 -4
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/material_time_picker.py +8 -4
- pythonnative-0.4.0/src/pythonnative/page.py +396 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/picker_view.py +8 -4
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/progress_view.py +5 -3
- pythonnative-0.4.0/src/pythonnative/scroll_view.py +101 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/search_bar.py +8 -4
- pythonnative-0.4.0/src/pythonnative/stack_view.py +199 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/switch.py +5 -3
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/build.gradle +5 -2
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +10 -7
- pythonnative-0.4.0/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/Navigator.kt +26 -0
- pythonnative-0.4.0/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PageFragment.kt +111 -0
- pythonnative-0.4.0/src/pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml +10 -0
- pythonnative-0.4.0/src/pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +22 -0
- pythonnative-0.4.0/src/pythonnative/templates/android_template/build.gradle +7 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +1 -1
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +7 -4
- pythonnative-0.4.0/src/pythonnative/templates/ios_template/ios_template/ViewController.swift +218 -0
- pythonnative-0.4.0/src/pythonnative/text_field.py +132 -0
- pythonnative-0.4.0/src/pythonnative/text_view.py +135 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/time_picker.py +8 -4
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/utils.py +25 -1
- pythonnative-0.4.0/src/pythonnative/view.py +173 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/web_view.py +5 -3
- pythonnative-0.4.0/src/pythonnative.egg-info/PKG-INFO +143 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative.egg-info/SOURCES.txt +3 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/tests/test_cli.py +30 -5
- pythonnative-0.4.0/tests/test_smoke.py +2 -0
- pythonnative-0.2.0/PKG-INFO +0 -137
- pythonnative-0.2.0/README.md +0 -74
- pythonnative-0.2.0/src/pythonnative/label.py +0 -66
- pythonnative-0.2.0/src/pythonnative/page.py +0 -209
- pythonnative-0.2.0/src/pythonnative/scroll_view.py +0 -63
- pythonnative-0.2.0/src/pythonnative/stack_view.py +0 -60
- pythonnative-0.2.0/src/pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml +0 -18
- pythonnative-0.2.0/src/pythonnative/templates/android_template/build.gradle +0 -7
- pythonnative-0.2.0/src/pythonnative/templates/ios_template/ios_template/ViewController.swift +0 -118
- pythonnative-0.2.0/src/pythonnative/text_field.py +0 -67
- pythonnative-0.2.0/src/pythonnative/text_view.py +0 -70
- pythonnative-0.2.0/src/pythonnative/view.py +0 -25
- pythonnative-0.2.0/src/pythonnative.egg-info/PKG-INFO +0 -137
- pythonnative-0.2.0/tests/test_smoke.py +0 -2
- {pythonnative-0.2.0 → pythonnative-0.4.0}/setup.cfg +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/activity_indicator_view.py +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/cli/__init__.py +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/collection_view.py +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/material_bottom_navigation_view.py +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/material_toolbar.py +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/proguard-rules.pro +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/values/colors.xml +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/values/strings.xml +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/values/themes.xml +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/values-night/themes.xml +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/backup_rules.xml +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/data_extraction_rules.xml +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/app/src/test/java/com/pythonnative/android_template/ExampleUnitTest.kt +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.jar +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/gradle.properties +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/gradlew +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/gradlew.bat +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/android_template/settings.gradle +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/ios_template/ios_template/AppDelegate.swift +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/ios_template/ios_template/Base.lproj/LaunchScreen.storyboard +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/ios_template/ios_template/Base.lproj/Main.storyboard +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/ios_template/ios_template/Info.plist +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative.egg-info/dependency_links.txt +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative.egg-info/entry_points.txt +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative.egg-info/requires.txt +0 -0
- {pythonnative-0.2.0 → pythonnative-0.4.0}/src/pythonnative.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pythonnative
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Cross-platform native UI toolkit for Android and iOS
|
|
5
|
+
Author: Owen Carey
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Owen Carey
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in
|
|
18
|
+
all copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
26
|
+
THE SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/pythonnative/pythonnative
|
|
29
|
+
Project-URL: Repository, https://github.com/pythonnative/pythonnative
|
|
30
|
+
Project-URL: Issues, https://github.com/pythonnative/pythonnative/issues
|
|
31
|
+
Project-URL: Documentation, https://docs.pythonnative.com/
|
|
32
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
33
|
+
Classifier: Intended Audience :: Developers
|
|
34
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
35
|
+
Classifier: Programming Language :: Python :: 3
|
|
36
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
41
|
+
Classifier: Topic :: Software Development :: User Interfaces
|
|
42
|
+
Requires-Python: >=3.9
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
License-File: LICENSE
|
|
45
|
+
Requires-Dist: requests>=2.31.0
|
|
46
|
+
Provides-Extra: ios
|
|
47
|
+
Requires-Dist: rubicon-objc<0.5.0,>=0.4.6; extra == "ios"
|
|
48
|
+
Provides-Extra: docs
|
|
49
|
+
Requires-Dist: mkdocs>=1.5; extra == "docs"
|
|
50
|
+
Requires-Dist: mkdocs-material[imaging]>=9.5; extra == "docs"
|
|
51
|
+
Requires-Dist: mkdocstrings[python]>=0.24; extra == "docs"
|
|
52
|
+
Provides-Extra: dev
|
|
53
|
+
Requires-Dist: black>=24.0; extra == "dev"
|
|
54
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
55
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
56
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
57
|
+
Provides-Extra: ci
|
|
58
|
+
Requires-Dist: black>=24.0; extra == "ci"
|
|
59
|
+
Requires-Dist: ruff>=0.5; extra == "ci"
|
|
60
|
+
Requires-Dist: mypy>=1.10; extra == "ci"
|
|
61
|
+
Requires-Dist: pytest>=8.0; extra == "ci"
|
|
62
|
+
Dynamic: license-file
|
|
63
|
+
|
|
64
|
+
<p align="center">
|
|
65
|
+
<img src="docs/assets/banner.jpg" alt="PythonNative" width="800" />
|
|
66
|
+
</p>
|
|
67
|
+
|
|
68
|
+
<p align="center">
|
|
69
|
+
<em>Build native Android and iOS apps in Python.</em>
|
|
70
|
+
</p>
|
|
71
|
+
|
|
72
|
+
<p align="center">
|
|
73
|
+
<a href="https://github.com/pythonnative/pythonnative/actions/workflows/ci.yml"><img src="https://github.com/pythonnative/pythonnative/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
|
|
74
|
+
<a href="https://github.com/pythonnative/pythonnative/actions/workflows/release.yml"><img src="https://github.com/pythonnative/pythonnative/actions/workflows/release.yml/badge.svg" alt="Release" /></a>
|
|
75
|
+
<a href="https://pypi.org/project/pythonnative/"><img src="https://img.shields.io/pypi/v/pythonnative" alt="PyPI Version" /></a>
|
|
76
|
+
<a href="https://pypi.org/project/pythonnative/"><img src="https://img.shields.io/pypi/pyversions/pythonnative" alt="Python Versions" /></a>
|
|
77
|
+
<a href="LICENSE"><img src="https://img.shields.io/pypi/l/pythonnative" alt="License: MIT" /></a>
|
|
78
|
+
<a href="https://docs.pythonnative.com/"><img src="https://img.shields.io/website?url=https%3A%2F%2Fdocs.pythonnative.com&label=docs" alt="Docs" /></a>
|
|
79
|
+
</p>
|
|
80
|
+
|
|
81
|
+
<p align="center">
|
|
82
|
+
<a href="https://docs.pythonnative.com/">Documentation</a> ·
|
|
83
|
+
<a href="https://docs.pythonnative.com/getting-started/">Getting Started</a> ·
|
|
84
|
+
<a href="https://docs.pythonnative.com/examples/">Examples</a> ·
|
|
85
|
+
<a href="CONTRIBUTING.md">Contributing</a>
|
|
86
|
+
</p>
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Overview
|
|
91
|
+
|
|
92
|
+
PythonNative is a cross-platform toolkit for building native Android and iOS apps in Python. It provides a Pythonic API for native UI components, lifecycle events, and device capabilities, powered by Chaquopy on Android and rubicon-objc on iOS. Write your app once in Python and run it on both platforms with genuinely native interfaces.
|
|
93
|
+
|
|
94
|
+
## Features
|
|
95
|
+
|
|
96
|
+
- **Cross-platform native UI:** Build Android and iOS apps from a single Python codebase with truly native rendering.
|
|
97
|
+
- **Direct native bindings:** Python calls platform APIs directly through Chaquopy and rubicon-objc, with no JavaScript bridge.
|
|
98
|
+
- **Unified component API:** Components like `Page`, `StackView`, `Label`, `Button`, and `WebView` share a consistent interface across platforms.
|
|
99
|
+
- **CLI scaffolding:** `pn init` creates a ready-to-run project structure; `pn run android` and `pn run ios` build and launch your app.
|
|
100
|
+
- **Page lifecycle:** Hooks for `on_create`, `on_start`, `on_resume`, `on_pause`, `on_stop`, and `on_destroy`, with state save and restore.
|
|
101
|
+
- **Navigation:** Push and pop screens with argument passing for multi-page apps.
|
|
102
|
+
- **Rich component set:** Core views (Label, Button, TextField, ImageView, WebView, Switch, DatePicker, and more) plus Material Design variants.
|
|
103
|
+
- **Bundled templates:** Android Gradle and iOS Xcode templates are included, so scaffolding requires no network access.
|
|
104
|
+
|
|
105
|
+
## Quick Start
|
|
106
|
+
|
|
107
|
+
### Installation
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
pip install pythonnative
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Usage
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
import pythonnative as pn
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class MainPage(pn.Page):
|
|
120
|
+
def __init__(self, native_instance):
|
|
121
|
+
super().__init__(native_instance)
|
|
122
|
+
|
|
123
|
+
def on_create(self):
|
|
124
|
+
super().on_create()
|
|
125
|
+
stack = pn.StackView()
|
|
126
|
+
stack.add_view(pn.Label("Hello from PythonNative!"))
|
|
127
|
+
button = pn.Button("Tap me")
|
|
128
|
+
button.set_on_click(lambda: print("Button tapped"))
|
|
129
|
+
stack.add_view(button)
|
|
130
|
+
self.set_root_view(stack)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Documentation
|
|
134
|
+
|
|
135
|
+
Visit [docs.pythonnative.com](https://docs.pythonnative.com/) for the full documentation, including getting started guides, platform-specific instructions for Android and iOS, API reference, and working examples.
|
|
136
|
+
|
|
137
|
+
## Contributing
|
|
138
|
+
|
|
139
|
+
Contributions are welcome. Please see [CONTRIBUTING.md](CONTRIBUTING.md) for setup instructions, coding standards, and guidelines for submitting pull requests.
|
|
140
|
+
|
|
141
|
+
## License
|
|
142
|
+
|
|
143
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/assets/banner.jpg" alt="PythonNative" width="800" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<em>Build native Android and iOS apps in Python.</em>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://github.com/pythonnative/pythonnative/actions/workflows/ci.yml"><img src="https://github.com/pythonnative/pythonnative/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
|
|
11
|
+
<a href="https://github.com/pythonnative/pythonnative/actions/workflows/release.yml"><img src="https://github.com/pythonnative/pythonnative/actions/workflows/release.yml/badge.svg" alt="Release" /></a>
|
|
12
|
+
<a href="https://pypi.org/project/pythonnative/"><img src="https://img.shields.io/pypi/v/pythonnative" alt="PyPI Version" /></a>
|
|
13
|
+
<a href="https://pypi.org/project/pythonnative/"><img src="https://img.shields.io/pypi/pyversions/pythonnative" alt="Python Versions" /></a>
|
|
14
|
+
<a href="LICENSE"><img src="https://img.shields.io/pypi/l/pythonnative" alt="License: MIT" /></a>
|
|
15
|
+
<a href="https://docs.pythonnative.com/"><img src="https://img.shields.io/website?url=https%3A%2F%2Fdocs.pythonnative.com&label=docs" alt="Docs" /></a>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<a href="https://docs.pythonnative.com/">Documentation</a> ·
|
|
20
|
+
<a href="https://docs.pythonnative.com/getting-started/">Getting Started</a> ·
|
|
21
|
+
<a href="https://docs.pythonnative.com/examples/">Examples</a> ·
|
|
22
|
+
<a href="CONTRIBUTING.md">Contributing</a>
|
|
23
|
+
</p>
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Overview
|
|
28
|
+
|
|
29
|
+
PythonNative is a cross-platform toolkit for building native Android and iOS apps in Python. It provides a Pythonic API for native UI components, lifecycle events, and device capabilities, powered by Chaquopy on Android and rubicon-objc on iOS. Write your app once in Python and run it on both platforms with genuinely native interfaces.
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- **Cross-platform native UI:** Build Android and iOS apps from a single Python codebase with truly native rendering.
|
|
34
|
+
- **Direct native bindings:** Python calls platform APIs directly through Chaquopy and rubicon-objc, with no JavaScript bridge.
|
|
35
|
+
- **Unified component API:** Components like `Page`, `StackView`, `Label`, `Button`, and `WebView` share a consistent interface across platforms.
|
|
36
|
+
- **CLI scaffolding:** `pn init` creates a ready-to-run project structure; `pn run android` and `pn run ios` build and launch your app.
|
|
37
|
+
- **Page lifecycle:** Hooks for `on_create`, `on_start`, `on_resume`, `on_pause`, `on_stop`, and `on_destroy`, with state save and restore.
|
|
38
|
+
- **Navigation:** Push and pop screens with argument passing for multi-page apps.
|
|
39
|
+
- **Rich component set:** Core views (Label, Button, TextField, ImageView, WebView, Switch, DatePicker, and more) plus Material Design variants.
|
|
40
|
+
- **Bundled templates:** Android Gradle and iOS Xcode templates are included, so scaffolding requires no network access.
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
### Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install pythonnative
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Usage
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
import pythonnative as pn
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class MainPage(pn.Page):
|
|
57
|
+
def __init__(self, native_instance):
|
|
58
|
+
super().__init__(native_instance)
|
|
59
|
+
|
|
60
|
+
def on_create(self):
|
|
61
|
+
super().on_create()
|
|
62
|
+
stack = pn.StackView()
|
|
63
|
+
stack.add_view(pn.Label("Hello from PythonNative!"))
|
|
64
|
+
button = pn.Button("Tap me")
|
|
65
|
+
button.set_on_click(lambda: print("Button tapped"))
|
|
66
|
+
stack.add_view(button)
|
|
67
|
+
self.set_root_view(stack)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Documentation
|
|
71
|
+
|
|
72
|
+
Visit [docs.pythonnative.com](https://docs.pythonnative.com/) for the full documentation, including getting started guides, platform-specific instructions for Android and iOS, API reference, and working examples.
|
|
73
|
+
|
|
74
|
+
## Contributing
|
|
75
|
+
|
|
76
|
+
Contributions are welcome. Please see [CONTRIBUTING.md](CONTRIBUTING.md) for setup instructions, coding standards, and guidelines for submitting pull requests.
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
[MIT](LICENSE)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pythonnative"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "Cross-platform native UI toolkit for Android and iOS"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Owen Carey" }
|
|
@@ -88,3 +88,30 @@ ignore = []
|
|
|
88
88
|
[tool.black]
|
|
89
89
|
line-length = 120
|
|
90
90
|
target-version = ['py39']
|
|
91
|
+
|
|
92
|
+
# ── Semantic Release ────────────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
[tool.semantic_release]
|
|
95
|
+
version_toml = ["pyproject.toml:project.version"]
|
|
96
|
+
version_variables = ["src/pythonnative/__init__.py:__version__"]
|
|
97
|
+
commit_message = "chore(release): v{version}"
|
|
98
|
+
tag_format = "v{version}"
|
|
99
|
+
major_on_zero = false
|
|
100
|
+
|
|
101
|
+
[tool.semantic_release.branches.main]
|
|
102
|
+
match = "main"
|
|
103
|
+
prerelease = false
|
|
104
|
+
|
|
105
|
+
[tool.semantic_release.changelog]
|
|
106
|
+
changelog_file = "CHANGELOG.md"
|
|
107
|
+
exclude_commit_patterns = [
|
|
108
|
+
"^chore\\(release\\):",
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
[tool.semantic_release.commit_parser_options]
|
|
112
|
+
allowed_tags = [
|
|
113
|
+
"build", "chore", "ci", "docs", "feat", "fix",
|
|
114
|
+
"perf", "refactor", "revert", "style", "test",
|
|
115
|
+
]
|
|
116
|
+
minor_tags = ["feat"]
|
|
117
|
+
patch_tags = ["fix", "perf"]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
-
from typing import Callable, Optional
|
|
2
|
+
from typing import Any, Callable, Optional
|
|
3
3
|
|
|
4
4
|
from .utils import IS_ANDROID, get_android_context
|
|
5
5
|
from .view import ViewBase
|
|
@@ -15,7 +15,7 @@ class ButtonBase(ABC):
|
|
|
15
15
|
super().__init__()
|
|
16
16
|
|
|
17
17
|
@abstractmethod
|
|
18
|
-
def set_title(self, title: str) ->
|
|
18
|
+
def set_title(self, title: str) -> "ButtonBase":
|
|
19
19
|
pass
|
|
20
20
|
|
|
21
21
|
@abstractmethod
|
|
@@ -23,7 +23,7 @@ class ButtonBase(ABC):
|
|
|
23
23
|
pass
|
|
24
24
|
|
|
25
25
|
@abstractmethod
|
|
26
|
-
def set_on_click(self, callback: Callable[[], None]) ->
|
|
26
|
+
def set_on_click(self, callback: Callable[[], None]) -> "ButtonBase":
|
|
27
27
|
pass
|
|
28
28
|
|
|
29
29
|
|
|
@@ -43,23 +43,25 @@ if IS_ANDROID:
|
|
|
43
43
|
self.native_instance = self.native_class(context)
|
|
44
44
|
self.set_title(title)
|
|
45
45
|
|
|
46
|
-
def set_title(self, title: str) ->
|
|
46
|
+
def set_title(self, title: str) -> "Button":
|
|
47
47
|
self.native_instance.setText(title)
|
|
48
|
+
return self
|
|
48
49
|
|
|
49
50
|
def get_title(self) -> str:
|
|
50
51
|
return self.native_instance.getText().toString()
|
|
51
52
|
|
|
52
|
-
def set_on_click(self, callback: Callable[[], None]) ->
|
|
53
|
+
def set_on_click(self, callback: Callable[[], None]) -> "Button":
|
|
53
54
|
class OnClickListener(dynamic_proxy(jclass("android.view.View").OnClickListener)):
|
|
54
|
-
def __init__(self, callback):
|
|
55
|
+
def __init__(self, callback: Callable[[], None]) -> None:
|
|
55
56
|
super().__init__()
|
|
56
57
|
self.callback = callback
|
|
57
58
|
|
|
58
|
-
def onClick(self, view):
|
|
59
|
+
def onClick(self, view: Any) -> None:
|
|
59
60
|
self.callback()
|
|
60
61
|
|
|
61
62
|
listener = OnClickListener(callback)
|
|
62
63
|
self.native_instance.setOnClickListener(listener)
|
|
64
|
+
return self
|
|
63
65
|
|
|
64
66
|
else:
|
|
65
67
|
# ========================================
|
|
@@ -77,7 +79,7 @@ else:
|
|
|
77
79
|
_callback: Optional[Callable[[], None]] = None
|
|
78
80
|
|
|
79
81
|
@objc_method
|
|
80
|
-
def onTap_(self, sender) -> None:
|
|
82
|
+
def onTap_(self, sender: object) -> None:
|
|
81
83
|
try:
|
|
82
84
|
callback = self._callback
|
|
83
85
|
if callback is not None:
|
|
@@ -93,13 +95,14 @@ else:
|
|
|
93
95
|
self.native_instance = self.native_class.alloc().init()
|
|
94
96
|
self.set_title(title)
|
|
95
97
|
|
|
96
|
-
def set_title(self, title: str) ->
|
|
98
|
+
def set_title(self, title: str) -> "Button":
|
|
97
99
|
self.native_instance.setTitle_forState_(title, 0)
|
|
100
|
+
return self
|
|
98
101
|
|
|
99
102
|
def get_title(self) -> str:
|
|
100
103
|
return self.native_instance.titleForState_(0)
|
|
101
104
|
|
|
102
|
-
def set_on_click(self, callback: Callable[[], None]) ->
|
|
105
|
+
def set_on_click(self, callback: Callable[[], None]) -> "Button":
|
|
103
106
|
# Create a handler object with an Objective-C method `onTap:` and attach the Python callback
|
|
104
107
|
handler = _PNButtonHandler.new()
|
|
105
108
|
# Keep strong references to the handler and callback
|
|
@@ -107,3 +110,4 @@ else:
|
|
|
107
110
|
handler._callback = callback
|
|
108
111
|
# UIControlEventTouchUpInside = 1 << 6
|
|
109
112
|
self.native_instance.addTarget_action_forControlEvents_(handler, SEL("onTap:"), 1 << 6)
|
|
113
|
+
return self
|
|
@@ -41,7 +41,7 @@ def init_project(args: argparse.Namespace) -> None:
|
|
|
41
41
|
|
|
42
42
|
os.makedirs(app_dir, exist_ok=True)
|
|
43
43
|
|
|
44
|
-
# Minimal hello world app scaffold
|
|
44
|
+
# Minimal hello world app scaffold (no bootstrap function; host instantiates Page directly)
|
|
45
45
|
main_page_py = os.path.join(app_dir, "main_page.py")
|
|
46
46
|
if not os.path.exists(main_page_py) or args.force:
|
|
47
47
|
with open(main_page_py, "w", encoding="utf-8") as f:
|
|
@@ -55,19 +55,17 @@ class MainPage(pn.Page):
|
|
|
55
55
|
|
|
56
56
|
def on_create(self):
|
|
57
57
|
super().on_create()
|
|
58
|
-
stack =
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
stack = (
|
|
59
|
+
pn.StackView()
|
|
60
|
+
.set_axis("vertical")
|
|
61
|
+
.set_spacing(12)
|
|
62
|
+
.set_alignment("fill")
|
|
63
|
+
.set_padding(all=16)
|
|
64
|
+
)
|
|
65
|
+
stack.add_view(pn.Label("Hello from PythonNative!").set_text_size(18))
|
|
66
|
+
button = pn.Button("Tap me").set_on_click(lambda: print("Button clicked"))
|
|
62
67
|
stack.add_view(button)
|
|
63
|
-
self.set_root_view(stack)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def bootstrap(native_instance):
|
|
67
|
-
'''Entry point called by the host app (Android Activity or iOS ViewController).'''
|
|
68
|
-
page = MainPage(native_instance)
|
|
69
|
-
page.on_create()
|
|
70
|
-
return page
|
|
68
|
+
self.set_root_view(stack.wrap_in_scroll())
|
|
71
69
|
"""
|
|
72
70
|
)
|
|
73
71
|
|
|
@@ -14,7 +14,7 @@ class DatePickerBase(ABC):
|
|
|
14
14
|
super().__init__()
|
|
15
15
|
|
|
16
16
|
@abstractmethod
|
|
17
|
-
def set_date(self, year: int, month: int, day: int) ->
|
|
17
|
+
def set_date(self, year: int, month: int, day: int) -> "DatePickerBase":
|
|
18
18
|
pass
|
|
19
19
|
|
|
20
20
|
@abstractmethod
|
|
@@ -28,17 +28,20 @@ if IS_ANDROID:
|
|
|
28
28
|
# https://developer.android.com/reference/android/widget/DatePicker
|
|
29
29
|
# ========================================
|
|
30
30
|
|
|
31
|
+
from typing import Any
|
|
32
|
+
|
|
31
33
|
from java import jclass
|
|
32
34
|
|
|
33
35
|
class DatePicker(DatePickerBase, ViewBase):
|
|
34
|
-
def __init__(self, context, year: int = 0, month: int = 0, day: int = 0) -> None:
|
|
36
|
+
def __init__(self, context: Any, year: int = 0, month: int = 0, day: int = 0) -> None:
|
|
35
37
|
super().__init__()
|
|
36
38
|
self.native_class = jclass("android.widget.DatePicker")
|
|
37
39
|
self.native_instance = self.native_class(context)
|
|
38
40
|
self.set_date(year, month, day)
|
|
39
41
|
|
|
40
|
-
def set_date(self, year: int, month: int, day: int) ->
|
|
42
|
+
def set_date(self, year: int, month: int, day: int) -> "DatePicker":
|
|
41
43
|
self.native_instance.updateDate(year, month, day)
|
|
44
|
+
return self
|
|
42
45
|
|
|
43
46
|
def get_date(self) -> tuple:
|
|
44
47
|
year = self.native_instance.getYear()
|
|
@@ -63,9 +66,10 @@ else:
|
|
|
63
66
|
self.native_instance = self.native_class.alloc().init()
|
|
64
67
|
self.set_date(year, month, day)
|
|
65
68
|
|
|
66
|
-
def set_date(self, year: int, month: int, day: int) ->
|
|
69
|
+
def set_date(self, year: int, month: int, day: int) -> "DatePicker":
|
|
67
70
|
date = datetime(year, month, day)
|
|
68
71
|
self.native_instance.setDate_(date)
|
|
72
|
+
return self
|
|
69
73
|
|
|
70
74
|
def get_date(self) -> tuple:
|
|
71
75
|
date = self.native_instance.date()
|
|
@@ -14,7 +14,7 @@ class ImageViewBase(ABC):
|
|
|
14
14
|
super().__init__()
|
|
15
15
|
|
|
16
16
|
@abstractmethod
|
|
17
|
-
def set_image(self, image: str) ->
|
|
17
|
+
def set_image(self, image: str) -> "ImageViewBase":
|
|
18
18
|
pass
|
|
19
19
|
|
|
20
20
|
@abstractmethod
|
|
@@ -40,9 +40,10 @@ if IS_ANDROID:
|
|
|
40
40
|
if image:
|
|
41
41
|
self.set_image(image)
|
|
42
42
|
|
|
43
|
-
def set_image(self, image: str) ->
|
|
43
|
+
def set_image(self, image: str) -> "ImageView":
|
|
44
44
|
bitmap = BitmapFactory.decodeFile(image)
|
|
45
45
|
self.native_instance.setImageBitmap(bitmap)
|
|
46
|
+
return self
|
|
46
47
|
|
|
47
48
|
def get_image(self) -> str:
|
|
48
49
|
# Please note that this is a simplistic representation, getting image from ImageView
|
|
@@ -66,10 +67,11 @@ else:
|
|
|
66
67
|
if image:
|
|
67
68
|
self.set_image(image)
|
|
68
69
|
|
|
69
|
-
def set_image(self, image: str) ->
|
|
70
|
+
def set_image(self, image: str) -> "ImageView":
|
|
70
71
|
ns_str = NSString.alloc().initWithUTF8String_(image)
|
|
71
72
|
ui_image = UIImage.imageNamed_(ns_str)
|
|
72
73
|
self.native_instance.setImage_(ui_image)
|
|
74
|
+
return self
|
|
73
75
|
|
|
74
76
|
def get_image(self) -> str:
|
|
75
77
|
# Similar to Android, getting the image from UIImageView isn't straightforward.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from .utils import IS_ANDROID, get_android_context
|
|
5
|
+
from .view import ViewBase
|
|
6
|
+
|
|
7
|
+
# ========================================
|
|
8
|
+
# Base class
|
|
9
|
+
# ========================================
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class LabelBase(ABC):
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def __init__(self) -> None:
|
|
15
|
+
super().__init__()
|
|
16
|
+
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def set_text(self, text: str) -> "LabelBase":
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
@abstractmethod
|
|
22
|
+
def get_text(self) -> str:
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
@abstractmethod
|
|
26
|
+
def set_text_color(self, color: Any) -> "LabelBase":
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
@abstractmethod
|
|
30
|
+
def set_text_size(self, size: float) -> "LabelBase":
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if IS_ANDROID:
|
|
35
|
+
# ========================================
|
|
36
|
+
# Android class
|
|
37
|
+
# https://developer.android.com/reference/android/widget/TextView
|
|
38
|
+
# ========================================
|
|
39
|
+
|
|
40
|
+
from java import jclass
|
|
41
|
+
|
|
42
|
+
class Label(LabelBase, ViewBase):
|
|
43
|
+
def __init__(self, text: str = "") -> None:
|
|
44
|
+
super().__init__()
|
|
45
|
+
self.native_class = jclass("android.widget.TextView")
|
|
46
|
+
context = get_android_context()
|
|
47
|
+
self.native_instance = self.native_class(context)
|
|
48
|
+
self.set_text(text)
|
|
49
|
+
|
|
50
|
+
def set_text(self, text: str) -> "Label":
|
|
51
|
+
self.native_instance.setText(text)
|
|
52
|
+
return self
|
|
53
|
+
|
|
54
|
+
def get_text(self) -> str:
|
|
55
|
+
return self.native_instance.getText().toString()
|
|
56
|
+
|
|
57
|
+
def set_text_color(self, color: Any) -> "Label":
|
|
58
|
+
# Accept int ARGB or hex string
|
|
59
|
+
if isinstance(color, str):
|
|
60
|
+
c = color.strip()
|
|
61
|
+
if c.startswith("#"):
|
|
62
|
+
c = c[1:]
|
|
63
|
+
if len(c) == 6:
|
|
64
|
+
c = "FF" + c
|
|
65
|
+
color_int = int(c, 16)
|
|
66
|
+
else:
|
|
67
|
+
color_int = int(color)
|
|
68
|
+
try:
|
|
69
|
+
self.native_instance.setTextColor(color_int)
|
|
70
|
+
except Exception:
|
|
71
|
+
pass
|
|
72
|
+
return self
|
|
73
|
+
|
|
74
|
+
def set_text_size(self, size_sp: float) -> "Label":
|
|
75
|
+
try:
|
|
76
|
+
self.native_instance.setTextSize(float(size_sp))
|
|
77
|
+
except Exception:
|
|
78
|
+
pass
|
|
79
|
+
return self
|
|
80
|
+
|
|
81
|
+
else:
|
|
82
|
+
# ========================================
|
|
83
|
+
# iOS class
|
|
84
|
+
# https://developer.apple.com/documentation/uikit/uilabel
|
|
85
|
+
# ========================================
|
|
86
|
+
|
|
87
|
+
from rubicon.objc import ObjCClass
|
|
88
|
+
|
|
89
|
+
class Label(LabelBase, ViewBase):
|
|
90
|
+
def __init__(self, text: str = "") -> None:
|
|
91
|
+
super().__init__()
|
|
92
|
+
self.native_class = ObjCClass("UILabel")
|
|
93
|
+
self.native_instance = self.native_class.alloc().init()
|
|
94
|
+
self.set_text(text)
|
|
95
|
+
|
|
96
|
+
def set_text(self, text: str) -> "Label":
|
|
97
|
+
self.native_instance.setText_(text)
|
|
98
|
+
return self
|
|
99
|
+
|
|
100
|
+
def get_text(self) -> str:
|
|
101
|
+
return self.native_instance.text()
|
|
102
|
+
|
|
103
|
+
def set_text_color(self, color: Any) -> "Label":
|
|
104
|
+
# Accept int ARGB or hex string
|
|
105
|
+
if isinstance(color, str):
|
|
106
|
+
c = color.strip()
|
|
107
|
+
if c.startswith("#"):
|
|
108
|
+
c = c[1:]
|
|
109
|
+
if len(c) == 6:
|
|
110
|
+
c = "FF" + c
|
|
111
|
+
color_int = int(c, 16)
|
|
112
|
+
else:
|
|
113
|
+
color_int = int(color)
|
|
114
|
+
try:
|
|
115
|
+
UIColor = ObjCClass("UIColor")
|
|
116
|
+
a = ((color_int >> 24) & 0xFF) / 255.0
|
|
117
|
+
r = ((color_int >> 16) & 0xFF) / 255.0
|
|
118
|
+
g = ((color_int >> 8) & 0xFF) / 255.0
|
|
119
|
+
b = (color_int & 0xFF) / 255.0
|
|
120
|
+
color_obj = UIColor.colorWithRed_green_blue_alpha_(r, g, b, a)
|
|
121
|
+
self.native_instance.setTextColor_(color_obj)
|
|
122
|
+
except Exception:
|
|
123
|
+
pass
|
|
124
|
+
return self
|
|
125
|
+
|
|
126
|
+
def set_text_size(self, size: float) -> "Label":
|
|
127
|
+
try:
|
|
128
|
+
UIFont = ObjCClass("UIFont")
|
|
129
|
+
font = UIFont.systemFontOfSize_(float(size))
|
|
130
|
+
self.native_instance.setFont_(font)
|
|
131
|
+
except Exception:
|
|
132
|
+
pass
|
|
133
|
+
return self
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Any
|
|
2
3
|
|
|
3
4
|
from .utils import IS_ANDROID
|
|
4
5
|
from .view import ViewBase
|
|
@@ -14,7 +15,7 @@ class ListViewBase(ABC):
|
|
|
14
15
|
super().__init__()
|
|
15
16
|
|
|
16
17
|
@abstractmethod
|
|
17
|
-
def set_data(self, data: list) ->
|
|
18
|
+
def set_data(self, data: list) -> "ListViewBase":
|
|
18
19
|
pass
|
|
19
20
|
|
|
20
21
|
@abstractmethod
|
|
@@ -31,18 +32,19 @@ if IS_ANDROID:
|
|
|
31
32
|
from java import jclass
|
|
32
33
|
|
|
33
34
|
class ListView(ListViewBase, ViewBase):
|
|
34
|
-
def __init__(self, context, data: list = []) -> None:
|
|
35
|
+
def __init__(self, context: Any, data: list = []) -> None:
|
|
35
36
|
super().__init__()
|
|
36
37
|
self.context = context
|
|
37
38
|
self.native_class = jclass("android.widget.ListView")
|
|
38
39
|
self.native_instance = self.native_class(context)
|
|
39
40
|
self.set_data(data)
|
|
40
41
|
|
|
41
|
-
def set_data(self, data: list) ->
|
|
42
|
+
def set_data(self, data: list) -> "ListView":
|
|
42
43
|
adapter = jclass("android.widget.ArrayAdapter")(
|
|
43
44
|
self.context, jclass("android.R$layout").simple_list_item_1, data
|
|
44
45
|
)
|
|
45
46
|
self.native_instance.setAdapter(adapter)
|
|
47
|
+
return self
|
|
46
48
|
|
|
47
49
|
def get_data(self) -> list:
|
|
48
50
|
adapter = self.native_instance.getAdapter()
|
|
@@ -63,9 +65,10 @@ else:
|
|
|
63
65
|
self.native_instance = self.native_class.alloc().init()
|
|
64
66
|
self.set_data(data)
|
|
65
67
|
|
|
66
|
-
def set_data(self, data: list) ->
|
|
68
|
+
def set_data(self, data: list) -> "ListView":
|
|
67
69
|
# Note: This is a simplified representation. Normally, you would need to create a UITableViewDataSource.
|
|
68
70
|
self.native_instance.reloadData()
|
|
71
|
+
return self
|
|
69
72
|
|
|
70
73
|
def get_data(self) -> list:
|
|
71
74
|
# Note: This is a simplified representation.
|