Crash runnng in android apps created kivymd and colab buildozer - android

I have done successfully to convert a very simple python scripts into android apps using colab buildozer.
I uses only kivymd.app
However when I added kivymd.uix.pickers, it crashes when I run the app in my android phone.
Please tell me what was wrong and simple solution.
I have editted the buildozer.spec line 40 to: requirements = python3,kivy,kivymd
Below are the py and kv file.
#main.py
from kivymd.app import MDApp
from kivymd.uix.pickers import MDDatePicker
class MainApp(MDApp):
def build(self):
self.theme_cls.primary_palette = "DeepPurple"
def show_date_picker(self):
date_dialog = MDDatePicker()
date_dialog.open()
if __name__ == '__main__':
app = MainApp()
app.run()
MDFloatLayout:
MDLabel:
text: "My Tasks"
pos_hint: {'x': .5,'y': .45}
MDRectangleFlatButton:
text: 'Date Picker'
pos_hint: {'x': .5, 'y':.5}
on_release: app.show_date_picker()

Related

Kivy - How to keep widgets that are added later when the app restarts

I was wondering if there is any way to keep widgets that are added later during the application use. Whenever the app restarts, the build() function is called, and the widgets that have been added during the app usage disappear since they were not added in build() function.
I want to keep these widgets in the restart in the same way the app keeps them in Pause mode.
Thank you!
Here is a simple example of using an ini file with your Kivy App:
import os
import ast
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
kv = '''
BoxLayout:
orientation: 'vertical'
BoxLayout:
orientation: 'horizontal'
size_hint_y: 0.15
Button:
text: 'Add Widget'
on_release: app.do_add()
Button:
text: 'Some Button'
Button:
text: 'Another Button'
BoxLayout:
size_hint_y: 0.85
orientation: 'vertical'
id: box
'''
class TestApp(App):
def build(self):
self.count = 0 # counter used in Label text
return Builder.load_string(kv)
def build_config(self, config):
# Make sure that the config has at least default entries that we need
config.setdefaults('app', {
'labels': '[]',
})
def get_application_config(self):
# This defines the path and name where the ini file is located
return str(os.path.join(os.path.expanduser('~'), 'TestApp.ini'))
def on_start(self):
# the ini file is read automatically, here we initiate doing something with it
self.load_my_config()
def on_stop(self):
# save the config when the app exits
self.save_config()
def do_add(self):
self.count += 1
self.root.ids.box.add_widget(Label(text='Label ' + str(self.count)))
def save_config(self):
# this writes the data we want to save to the config
labels = []
for wid in self.root.ids.box.children:
labels.append(wid.text)
# set the data in the config
self.config.set('app', 'labels', str(labels))
# write the config file
self.config.write()
def load_my_config(self):
# extract our saved data from the config (it has already been read)
labels_str = self.config.get('app', 'labels')
# us the extracted data to build the Labels
labels = ast.literal_eval(labels_str)
labels.reverse()
self.count = len(labels)
for lab in labels:
self.root.ids.box.add_widget(Label(text=lab))
if __name__ == '__main__':
TestApp().run()

Easy kivy app work on windows but not on Android

I'm trying to study Kivy, so I've done a simple gui that work on windows but when I run it on Android with Kivy Launcher I'm getting this error trought ADB LOGCAT :
This is a simple kv code :
#:kivy 1.0.9
<ROT>:
FloatLayout:
AnchorLayout:
anchor_x:"center"
anchor_y: "top"
Button:
size_hint : 1,1
text : "prova"
on_press: root.press()
and this is the main.py :
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
class ROT(Widget):
def press(self):
print "ciao"
pass
class PongApp(App):
def build(self):
self.root = Builder.load_file('questionario.kv')
return ROT()
if __name__ == '__main__':
PongApp().run()
I can't understand why it doesn't want to work

App works well on Windows in Python2 & Python3, but receiving Error : failed to import android, and Button unresponsive

This is an app written in python 2.7 using kivy and numpy modules. I have installed buildozer by following steps in this link : https://github.com/kivy/buildozer , i did not do sudo pip install https://github.com/kivy/buildozer/archive/master.zip. After deploying the app on android buildozer android deploy run logcat i saw some errors on the log :
10-10 17:44:49.497 19176 19207 I python : [ERROR ] [Image ] Error reading file .\logo_example1.png
10-10 17:44:49.498 19176 19207 I python : [WARNING] [Base ] Unknown <android> provider
10-10 17:44:49.498 19176 19207 I python : [INFO ] [Base ] Start application main loop
10-10 17:44:49.503 19176 19207 I python : [INFO ] [GL ] NPOT texture support is available
10-10 17:44:49.504 19176 19207 I python : 0
10-10 17:44:49.504 19176 19207 I python : coloring
10-10 17:44:49.505 19176 19207 I python : [ERROR ] [Base ] Failed to import "android" module. Could not remove android presplash.
Problem : The Image widget does not show, and the touch for the button does not seem to receive any event/response. May I have feedbacks on this, thanks.
Current partially solved : The Image widget now shows, I change the source address from .\\logo_example1.png to logo_example1.png.
Existing problem : the Button widget is still unresponsive to touch on android, but works fine on Windows.
Code to test Start and Back buttons :
This is the main.kv :
#: import Main main
<CtmButton#Button>:
font_size:30
size:100, 50
<BackButton#Button>:
font_size:30
size:100, 50
text: "Back"
pos: Main.Wsize[0]+100, Main.Wsize[1]+100
<mainWidget>:
id: main_widget
<homeWidget>:
id: home_widget
padding : 200
Image:
id:logo
source: 'logo_example1.png'
center: 300, 450
CtmButton:
id: start_button
text: "Start"
center:300, 200
on_release: home_widget.startbutton_function()
## I also have tried replacing this with on_touch_down
## to see whether it will solve the problem
<puzzleWidget>:
id: puzzle_widget
BackButton:
id: back_button_1
center: 100, 37.5
on_release: puzzle_widget.backbutton_function()
This is the main.py:
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.core.window import Window
from kivy.uix.image import Image
from kivy.core.image import ImageData
from kivy.clock import Clock
from kivy.graphics import Rectangle
import time
import math
import itertools
import numpy
Window.clearcolor = (0.2, 0.25, 0.2, 0.5); col_dx = 0.005; y = list(Window.clearcolor); xstart = 0;
Window.size = (600, 700); Wsize = Window.size;
class mainWidget(Widget):
def __init__(self):
Widget.__init__(self);
home = homeWidget(); self.add_widget(home);
class homeWidget(Widget):
def __init__(self):
Widget.__init__(self);
self.bg_animate();
def printingsomething(self, dt):
print(dt);
def bg_animate(self):
global bg_anim
self.bg_anim = Clock.schedule_interval(self.printingsomething,1/40);
self.bg_anim();
def startbutton_function(self):
print('start pressed');
self.bg_anim.cancel();
mainw.clear_widgets();
mainw.add_widget(puzzleWidget());
class puzzleWidget(Widget):
def __init__(self):
Widget.__init__(self);
self.Bar1 = Image(size = [600, 75], color = list(Window.clearcolor)); self.Bar1.color[-1] = 0.8; self.Bar1.pos = [0, 700 - self.Bar1.size[1]];
self.Bar2 = Image(size = [600, 75], color = list(Window.clearcolor)); self.Bar2.color[-1] = 0.8; self.Bar2.pos = [0, 0];
#### ADD WIDGET(S):
self.add_widget(self.Bar1); self.add_widget(self.Bar2);
self.remove_widget(self.ids.back_button_1); self.add_widget(self.ids.back_button_1);
def backbutton_function(self):
print('back pressed');
self.clear_widgets();
mainw.clear_widgets();
mainw.add_widget(homeWidget());
################## main window for the app #########################
class mainApp(App):
def build(self):
global mainw
mainw = mainWidget();
return mainw
####################################################################
mainApp = mainApp()
mainApp.run();
Failed to import "android" module.
This msg has nothing to do with image. It's won't affect your project if you don't use android module explicitly. All you need to do to avoid this line in logs is to add android to requirements inside buildozer.spec.
Error reading file .\logo_example1.png
This error indicates that image can't be found. I don't know what's problem with .\\logo_example1.png, but it's better just to use absolute path's and forget about problems.
import os
root_dir = os.path.dirname(os.path.abspath(__file__))
img_rel = 'logo_example1.png'
img_abs = os.path.join(root_dir, img_rel)
print(img_abs)
Upd:
Problem with pressing is related to this line - Window.size = (600, 700). Remove it and everything will work.
Looks like changing Window.size somehow breaks kivy touch point detection. If you want to change window size on Windows, use Config or wrap Window.size changing with platform check.

Python Kivy - App that open url in a native web browser

I try to make a simple app that open a web page inside Kivy after clicking a button placed on a "Screen One".
I used this topic (Python - Showing a web browser/iframe right into the app) as reference but I didn't understand how to use the code provided by Michael...
So I tried this... and when I launch the apk (build with Buildozer) it didn't work :')
import kivy
kivy.require('1.9.2')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
# MICHAEL'S CODE
from kivy.utils import platform
from kivy.uix.widget import Widget
from kivy.clock import Clock
from jnius import autoclass
from android.runnable import run_on_ui_thread
WebView = autoclass('android.webkit.WebView')
WebViewClient = autoclass('android.webkit.WebViewClient')
activity = autoclass('org.renpy.android.PythonActivity').mActivity
class Wv(Widget):
def __init__(self, **kwargs):
super(Wv, self).__init__(**kwargs)
Clock.schedule_once(self.create_webview, 0)
#run_on_ui_thread
def create_webview(self, *args):
webview = WebView(activity)
webview.getSettings().setJavaScriptEnabled(True)
wvc = WebViewClient();
webview.setWebViewClient(wvc);
activity.setContentView(webview)
webview.loadUrl('http://www.google.com/')
# END OF MICHAEL'S CODE
Builder.load_string('''
<ScreenOne>:
BoxLayout:
Label:
text: "SCREEN 1"
Button:
text: "GO GO GO TO GOOGLE !"
on_press: root.open_browser()
<ScreenTwo>:
BoxLayout:
Label:
text: "SCREEN 2"
Button:
text: "GO GO GO TO SCREEN 1"
on_press:
root.manager.transition.direction = "right"
root.manager.transition.duration = 1
root.manager.current = "screen_one"
''')
class ScreenOne(Screen):
def open_browser(self):
return Wv()
class ScreenTwo(Screen):
pass
screen_manager = ScreenManager()
screen_manager.add_widget(ScreenOne(name="screen_one"))
screen_manager.add_widget(ScreenTwo(name="screen_two"))
class BrowserApp(App):
def build(self):
return screen_manager
app = BrowserApp()
app.run()
The app don't crash but just close when I start it.
What I'm doing wrong ? I'm sure that I don't use it the right way...
Log from adb logcat:
06-13 12:54:47.559 7429 7510 I python : ImportError: No module named android
06-13 12:54:47.579 7429 7510 I python : Python for android ended.
From the log you posted in the comments I extracted the two important lines:
06-13 12:54:47.559 7429 7510 I python : ImportError: No module named android
06-13 12:54:47.579 7429 7510 I python : Python for android ended.
This basically means that the copied code:
from android.runnable import run_on_ui_thread
won't work because it doesn't detect the android module. The module has a separate recipe, therefore you will need to add it to the requirements, so that it compiles the Cython code and adds it to your app, otherwise the import will always fail.
Basically you always want to search for 3-4 keywords when looking into such a messy logcat → "python", "Traceback", "Python for android", "kivy". There's a filter in buildozer for that if you use it:
android.logcat_filters = *:S python:D

Kivy compilation error "NoneType" object has no attribute "name"

##### main.py #####
from kivy.app import App
from kivy.app import Widget
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import NumericProperty, ObjectProperty, StringProperty, ListProperty, BooleanProperty
class MainScreen(Screen):
def __init__(self,**kwargs):
super(MainScreen,self).__init__(**kwargs) # implements the features of a GridLayout (the base class of MainScreen)
def save(self):
'''Saves the data from the input to a text file.
It is bound to the save button'''
status = self.ids.workout1.ids.status.active
workout = self.ids.workout1.ids.workoutInput.text
day = self.ids.workout1.ids.dayInput.text
time = self.ids.workout1.ids.timeInput.text
with open('workoutData.txt','a') as f:
f.write('%s, %s, %s, %s\n' %(status, workout, day, time))
return None
def totalTime(self):
'''This function doesn't do anything right now! Add code to compute the total exercise time, and document it in this string'''
return None
class WorkoutLayout(BoxLayout):
pass
class SecondScreen(Screen):
pass
class AppScreenManager(ScreenManager):
name = StringProperty('Num Num') ### <----line 37
def __init__(self,**kwargs):
super(AppScreenManager,self).__init__(**kwargs)
self.name = 'Num Num' ### <----line 40
class WorkoutApp(App):
def build(self):
sm = AppScreenManager()
print (sm.name)
# sm.name="123"
sm.add_widget(MainScreen(name='main'))
sm.add_widget(SecondScreen(name='secondary'))
return sm
if __name__=='__main__':
WorkoutApp().run()
# NOTE: running is diff on diff platforms
# python main.py
# kivy main.py
##### part of workout.kv #####
<MainScreen>:
BoxLayout:
orientation: 'vertical'
BoxLayout:
orientation: 'horizontal'
Label:
text: 'Workout'
size_hint_x: 2/3.
Label:
text: 'Day'
size_hint_x: 1/6.
Label:
text: 'Time'
size_hint_x: 1/6.
WorkoutLayout:
id: workout1
WorkoutLayout:
id: workout2
WorkoutLayout:
id: workout3
WorkoutLayout:
id: workout4
WorkoutLayout:
id: workout5
WorkoutLayout:
id: workout6
WorkoutLayout:
id: workout7
BoxLayout:
orientation: 'horizontal'
Button:
text: 'Save'
on_press: root.save()
Button:
text: 'Next screen'
on_press: root.manager.current = 'secondary'
<SecondScreen>:
BoxLayout:
orientation: 'vertical'
Label:
size_hint_y: 3/4.
text: root.manager.name ### <----line 45
# text: 'Hello second screen!' ### <----line 46
Button:
size_hint: (1/4.,1/4.)
text: 'Back to main screen'
on_press: root.manager.current = 'main'
<WorkoutLayout>:
size_hint_x: 1
orientation: 'horizontal'
CheckBox:
id: status
size_hint_x: 1/8.
TextInput:
id: workoutInput
size_hint_x: 5/8.
TextInput:
id: dayInput
size_hint_x: 1/8.
TextInput:
id: timeInput
size_hint_x: 1/8.
TextInput:
multiline: False
These are modified a bit from some sample codes online. When I compile, it says "NoneType" object has no attribute "name" , an error in line 45 in my kv file. I tried commenting in my main.py either line 37 or 40, but still got the same error.
If I comment in workout.kv line 45 and uncomment line 46, the code can be complied and line 46 in main.py can print out "NumNum", which means sm does have the attribute name.
Looks like a timing issue. The logic in the kv file is executed during construction of the SecondScreen object, at which point in time its "manager" property has not yet been populated. Since it hasn't been populated, it has a value of "None," and a NoneType doesn't have a name property.
Now, why does the manager property have a value of None? Well, according to Kivy's documentation, this property is "set when the screen is added to a manager." Which you're doing just after initialization.
So when you print the value out in main.py, the manager property has already been populated, but when you do it in the .kv file, which is invoked during initialization, you've got a NoneType in your property.
What can you do about this?
Well, one approach would be to change the offending line in the .kv to:
text: root.manager.name if root.manager else ""
This will cope with the manager property being None during initialization, and when the manager property is updated later, the text property should update automatically. Perhaps there is a more elegant way to do this, though.

Categories

Resources