Kivy TabbedPanel switch_to work inconsistently - python

I am writing a code which starts the frontend, runs the backend and then loads the frontend. The frontend consists of TabbedPanel, and the currently displayed tab may be change by backend.
Here's the MRE:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelHeader
def button(instance):
instance.parent.parent.switch_to(instance.parent.parent.tab_2) # accessing TabbedPanel without messing with sending
# a variable
def backend(frontend):
# this class represents main backend function. In the result of its execution there might be a need to switch to
# another tab
frontend.switch_to(frontend.tab_2)
class MyTabbedPanel(TabbedPanel):
def __init__(self, **kwargs):
super().__init__()
self.tab_1 = TabbedPanelHeader()
self.tab_2 = TabbedPanelHeader()
self.tab_1.content = Button(text='Tab 1')
self.tab_1.content.bind(on_release=button)
self.tab_2.content = Label(text='Tab 2')
self.add_widget(self.tab_1)
self.add_widget(self.tab_2)
class Application(App):
def build(self):
frontend = MyTabbedPanel()
backend(frontend)
return frontend
Application().run()
The button, which I have added to compare, to switch from tab 1 to tab 2 works just fine, however, the auto swith when starting the app does not work.
What is the problem? Thank you in advance.

At the time that you're calling backend, there is no root widget returned by the build method, let alone a tab to switch to.
One way to solve this, is to schedule the call to the backend for after the build ends, using the Clock module.
def build(self):
frontend = MyTabbedPanel()
# backend(frontend)
from functools import partial
from kivy.clock import Clock
Clock.schedule_once(partial(backend, frontend))
return frontend
You also have to add an args argument to the backend method, because Clock sends a dt value:
def backend(frontend, *args):

Related

os.system() don't let updating image source in kivy python

I am trying to update the image source on a changeImageSource function it changes the source instantly but when I use time.sleep() method in that function, function executes but doesn't update the source of the image. updates after time.sleep() call completed.
from kivy.app import App
from kivy.uix.image import AsyncImage
from kivy.uix.button import Button
from kivy.uix.widget import Widget
import time
# creating the App class
class MyApp(App):
def build(self):
parent = Widget()
#creating and adding image to widget
self.img = AsyncImage(
source='http://kivy.org/logos/kivy-logo-black-64.png')
self.img.pos = (400,400)
#creating btn and adding press handler
self.change_img_btn = Button(text="Change Image ")
self.change_img_btn.bind(on_press = self.changeImageSource)
#adding widget to Widget instance
parent.add_widget(self.img)
parent.add_widget(self.change_img_btn)
return parent;
def changeImageSource(self,*args):
self.img.source = "https://cdn.sstatic.net/Sites/stackoverflow/Img/apple-touch-icon.png?v=c78bd457575a"
time.sleep(4)
# run the App
MyApp().run()
One: The "return parent;" should be "return parent"
Two: Why do you need time.sleep()?
You can also try flipping line 29 and 30.

Cannot disable buttons in Kivy (Python)

I have problem with disabling of buttons in kivy library. When I disable button, it simply not disable. It waits in some strange way.
Let me show you my code:
import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
import time
class MainApp(App):
def build(self):
self.l = FloatLayout()
b = Button(text="BUTTON", pos_hint={"top":0.8, "right": 0.8}, size_hint=(0.6, 0.6))
b.bind(on_press=self.press)
self.l.add_widget(b)
return self.l
def press(self, btn):
btn.disabled = True
time.sleep(3.0)
btn.disabled = False
app = MainApp()
app.run()
When I press button, I want to disable it for 3 sec. But instead of it program "freeze" (without disabling of button), and then after 3 secs do animation of press (button blinks with blue color). Of cource program must "freeze" because of time.sleep(3.0), but after disabling of button (Which must be gray, but it dont change color...)
How to solve it? If I put there instead time.sleep() something like for cycle (with about 10 milions of cycle) to imitate of "doing something" by program, it behaves in the same way...
So how I can solve it? How to disable button in kivy, then do something and after it is done enable button again?
Thanks!
EDIT: My problem isn't, that program freezes for 3 seconds. I understand that calling time.sleep() is blocking. What I don't understand is why button is not disabled before (and during) sleep...
The time.sleep is blocking the code. Instead you need to use Clock to enable the button after 3 seconds. Below is the corrected code to achieve your target:
import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.clock import Clock
from functools import partial
class MainApp(App):
def build(self):
self.l = FloatLayout()
b = Button(text="BUTTON", pos_hint={"top":0.8, "right": 0.8}, size_hint=(0.6, 0.6))
b.bind(on_press=self.press)
self.l.add_widget(b)
return self.l
def press(self, btn):
btn.disabled = True
Clock.schedule_once(partial(self.btn_enable, btn), 3)
def btn_enable(self, btn, *args):
btn.disabled = False
app = MainApp()
app.run()
TL; DR
The animation happens after the press function is called. This means that you freeze the program when doing time.sleep.
What to do about it?
Instead, you need to do something non-blocking, meaning that it runs in three seconds, but it doesn't cause the program to freeze. Something that would probably work is to utilize threads (something similar to the example, but dealing with sending variables across threads).
Example
Here is an example for your code that does not work, so you can understand the gist of it. Most likely, you are going have to deal with passing variables across threads:
import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
# import time
import threading
class MainApp(App):
def build(self):
self.l = FloatLayout()
b = Button(text="BUTTON", pos_hint={"top":0.8, "right": 0.8}, size_hint=(0.6, 0.6))
b.bind(on_press=self.press)
self.l.add_widget(b)
return self.l
def press(self, btn):
btn.disabled = True
# time.sleep(3.0)
threading.Timer(3.0, lambda: btn.disabled = False).start()
app = MainApp()
app.run()
This was inspired by this answer.

Add UIImagePickerController to a kivy-ios-app (Python)

I want to access my images/photo gallery on an iOS device from a kivy application.
Kivy has no native way implemented to do this, so I'm trying to solve it with kivy/pyobjus
where I can use the UIImagePickerController (from Apples UIKit).
from kivy.app import App
from kivy.lang import Builder
from pyobjus import autoclass, protocol
class TestApp(App):
ui = Builder.load_file("main.kv")
def build(self):
return self.ui
def imagePicker(self):
UIImagePickerController = autoclass('UIImagePickerController')
self.picker = UIImagePickerController.alloc().init()
self.picker.delegate = self
self.picker.sourceType = 0
#protocol('UIImagePickerControllerDelegate')
def imagePickerControllerDidFinish(self, image):
print("ABC")
The ui is just a button, which calls the imagePicker function.
I have three questions on this code:
How can I display the imagePicker?
When I'm using imagePicker.delegate = self, the App crashes because
[...] delegate, but there is no #protocol methods declared.
But I declared a protocol with "#protocol('UIImagePickerControllerDelegate')"
So why it won't use my declared protocol? (I tried also to add the "UIImagePickerControllerDelegate" in the protocols.py from pyobjus. This didn't solved the problem)
If the protocol will work, it is the correct way to use the "imagePickerControllerDidFinish(self, image)" method to access the image?

Python for kivy:Pass values between multiple screens

I am developing a kivy app in which ,there are two screens
1.LoginScreen
and 2.HomeScreen.
What required is -
A value 'xyz' which is computed in class LoginScreen, to be passed to the method 'insertdata' of class HomeScreen and want to display that value on a label.
For this I tried following code:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import Screen, ScreenManager
class loginScreen(Screen):
def __init__(self,**kwargs):
super(HomeScreen, self).__init__(**kwargs)
def auth(self):
xyz=1
self.manager.current="home"
obj=HomeScreen()
# 1. To Pass 'xyz' to method scrn2
HomeScreen.insertdata(obj)
class HomeScreen(Screen):
def __init__(self,**kwargs):
super(LoginScreen, self).__init__(**kwargs)
if (a==1):
# 2. To display label
self.scrn2()
def insertdata(self):
print "screen 2"
label=Label(text="good moring")
self.add_widget(label)
class ScreenApp(App):
pass
if __name__ == '__main__':
ScreenApp().run()
Here:
insertdata is called from method auth
1)the 1st way is proper , as it is passing 'xyz' and calling the method insertdata but it dosen't display label
2) in 1st way I have to create to create an object of HomeScreen ,to call insertdata, which in turn calls the ___init__ of Homescreen and init calls insertdata
insertdata called from __init__
1) It loads data before user authentication at loginscreen
insertdata gets total 3 calls, which is affecting app launch time.
Suggest me any simple and effective way for this.
Thanks in advance.
You can use Screen.manager() method to get an manager object from one screen, use it to retrieve another one with its ScreenManager.get_screen() method and then pass the value directly. For an working example check an answer of this question: Kivy - Slider Class value change on another screen

Kivy Sending text from spinner to another function

I've been trying to figure out how to get the value selected in a spinner into another function. I basically need a user to select some option and then press "save" prompting another function to write the data to a file (right now I just have it setup to print). When I run the form.finkle function it prints kivy.uix.button.Button object at 0x02C149D0
I'm sure its an easy fix, but I've been stuck on it for days.
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.popup import Popup
from kivy.uix.textinput import TextInput
from kivy.uix.spinner import Spinner
condspin = Spinner(text='Condition',values=('Good','Fair','Poor','Missing'))
typespin = Spinner(text='Type', values=('Metal','Wood','Pin','Missing'))
commlabel = Label(text='Comment')
commtext = TextInput(text="")
class Goose(App):
def build(self):
layout = GridLayout(cols=2,rows=6,padding=10,spacing=10)
layout.add_widget(Button(text='Hunter Parking'))
layout.add_widget(Button(text='Boat Launch'))
layout.add_widget(Button(text='ETRAP'))
layout.add_widget(Button(text='Monument',on_press=form.monform))
layout.add_widget(Button(text='Camp Site'))
layout.add_widget(Button(text='Sign'))
layout.add_widget(Button(text='Building'))
layout.add_widget(Button(text='Trail Head'))
layout.add_widget(Button(text='Dam'))
layout.add_widget(Button(text='Day Use'))
layout.add_widget(Button(text='Pavilion'))
layout.add_widget(Button(text='Misc Point'))
return layout
class form():
def finkle(condtest):
print condtest
def monform(self):
monbox = GridLayout(cols=2,rows=8,padding=20,spacing=20)
monpopup = Popup(title="Monument",content=monbox)
closebut = Button(text="Close")
closebut.bind(on_press=monpopup.dismiss)
savebut = Button(text="Save Point")
savebut.bind(on_press=form.finkle)
condtest = condspin.text
monbox.add_widget(condspin)
monbox.add_widget(typespin)
monbox.add_widget(commlabel)
monbox.add_widget(commtext)
monbox.add_widget(savebut)
monbox.add_widget(closebut)
monpopup.open()
Goose().run()
Since you have made the spinner global, you could just do print(condspin.text). More generally, you could pass the spinner as an argument, e.g.
from functools import partial
savebut.bind(on_press=partial(self.finkle, condspin))
and redefine the finkle method as
def finkle(self, spinner, button)
Note that I changed form.finkle to self.finkle and added both the self and spinner arguments. It's bad practice to call the method via the class like that.
There are some significant other bad style things in your code, and I would recommend some other changes. Mostly I would make use of kv language for basically everything, it will make the widget trees much clearer, more robust to changes later, and also make this binding very simple (you'd be able to refer to the spinner text via a kv id). Also, the form class is semi-unnecessary, you could replace it with a FormWidget that is the GridLayout you make in the monform function, adding all its children and behaviour in kv.

Categories