I am writing an app with two screens using ScreenManager. In one screen I have a text input, and a button that reads such text input. If a certain condition from the input is satisfied, the second screen is activated. From this screen, I want to be able to grab the content of the text input from the first screen.
I have made multiple attempts and looked at many similar questions (this one for example), but none of them really seemed to work.
Below is a minimal non-working version of my code:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
class RootWindow(Screen):
def __init__(self, **kwargs):
super(RootWindow, self).__init__(**kwargs)
box = BoxLayout()
self.add_widget(box)
self.searchInput = TextInput(multiline=False)
box.add_widget(self.searchInput)
self.searchButton = Button(text="Search")
self.searchButton.bind(on_press=self.searchRecipe)
box.add_widget(self.searchButton)
def searchRecipe(self, instance):
src = self.searchInput.text
if not src == "Go":
pass
else:
WMan.current = 'result'
class ResultWindow(Screen):
def __init__(self, **kwargs):
super(ResultWindow, self).__init__(**kwargs)
titleBox = BoxLayout()
self.add_widget(titleBox)
print(src)
WMan = ScreenManager()
WMan.add_widget(RootWindow(name='root'))
WMan.add_widget(ResultWindow(name='result'))
class RecipApp(App):
def build(self):
return WMan
if __name__ == "__main__":
RecipApp().run()
If you save a reference to titlebox like this:
class ResultWindow(Screen):
def __init__(self, **kwargs):
super(ResultWindow, self).__init__(**kwargs)
self.titleBox = BoxLayout()
self.add_widget(self.titleBox)
Then you can access the titlebox via the WMan, like this:
def searchRecipe(self, instance):
src = self.searchInput.text
if not src == "Go":
pass
else:
WMan.current = 'result'
WMan.current_screen.titlebox.add_widget(Label(text=src))
Related
I have a very simplistic case of a window with a single button. With a button release I want to pop up a modal view with some text on it. In every button release I create and open an instance of ModalView and it works:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.modalview import ModalView
from kivy.uix.label import Label
class AButton(Button):
def on_release(self, *largs):
view = ModalView(size_hint=(None, None), size=[200, 200])
view.add_widget(Label(text='I am a modal view'))
view.open()
class MyApp(App):
def build(self):
return AButton()
if __name__ == '__main__':
MyApp().run()
Now let's say I want to create a subclass of ModalView so I don't have to specify size_hint and size every time I pop up a modal view. This is the code after that change:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.modalview import ModalView
from kivy.uix.label import Label
from kivy.properties import ListProperty
class AButton(Button):
def on_release(self, *largs):
view = ModalView2()
view.add_widget(Label(text='I am a modal view'))
view.open()
class ModalView2(ModalView):
size_hint = ListProperty([None, None])
size = ListProperty([200, 200])
def __init__(self, **kwargs):
super(ModalView2, self).__init__(**kwargs)
class MyApp(App):
def build(self):
return AButton()
if __name__ == '__main__':
MyApp().run()
ModalView and Label position get totally messed up. I tried with anchor_x and anchor_y in ModalView2 in an attempt to fix the label position with no luck. What am I doing wrong?
The ModalView already has size_hint and size attributes, so you don't need to create new ones, just set the existing attributes to the values you want:
class ModalView2(ModalView):
def __init__(self, **kwargs):
super(ModalView2, self).__init__(**kwargs)
self.size_hint = (None, None)
self.size = (200, 200)
i'm using kivy for building an app screen with 3 screens.
my main screen ( class Base) is showing data from a SQL request.
i would like the user to be able to update these data using a button.
first this class Base is called in the screen manager which is itself called in my root class.
So my question is , how do i clear the data in my class Base and update it with the new data?
i tried to clear the data in my class screen manager.
the refresh function is called from the root class.
i have the below error:
'ScreenManager uses remove_widget only for removing Screens'
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from sql import runsql2
from kivy.core.window import Window
class Manager(ScreenManager):
def __init__(self):
super(Manager, self).__init__()
for i in range(2):
txt = 'Screen {}'.format(i)
lbl = Label(text=txt)
screen = Screen(name=txt)
screen.add_widget(lbl)
self.add_widget(screen)
base=Base('main')
self.add_widget(base)
def refresh(self):
self.clear_widgets(screens='main')
base=Base('main')
self.add_widget(base)
class Nav(GridLayout):
def __init__(self,sm=None):
super(Nav, self).__init__()
self.sm = sm
self.cols =3
self.size_hint = (1,0.1)
self.add_widget(Button(text="Clearing Screen", on_release=self.change))
self.add_widget(Button(text="Go screen 2", on_release=self.goscreen))
self.add_widget(Button(text="Quit", on_release=self.quit))
def change(self, btn):
#self.sm.current = btn.text
self.sm.current='main'
def quit(self,ins):
exit()
def goscreen(self,ins):
self.sm.current='Screen 1'
class Base(Screen):
def __init__(self,name):
super(Base, self).__init__()
self.lay=GridLayout()
self.name=name
self.bout=['[color=33ff99]Refresh[/color]','',"","","","","","","","",""]
self.data=runsql2()
self.lay.cols = 11
self.titre=['[color=ff9900]Market[/color]', '[color=ff9900]B/S[/color]', '[color=ff9900]Volume[/color]', '[color=ff9900]Contract[/color]',
'[color=ff9900]C/P[/color]', '[color=ff9900]Expiry[/color]', '[color=ff9900]Strike[/color]', '[color=ff9900]Price[/color]',
'[color=ff9900]Account[/color]', '[color=ff9900]Give up Member[/color]', '[color=ff9900]Allocation Account[/color]']
#self.lay.add_widget(Button(text='[color=33ff99]Refresh[/color]', size_hint=(1, 0.15), markup=True,on_release=self.do))
for i in range(11):
self.lay.add_widget(Label(text='', size_hint=(1, 0.15)))
for j in range(11):
self.lay.add_widget(Label(text=str(self.titre[j]),size_hint=(0.2,0.2),markup=True))
long = len(self.data)
for i in range(long):
for j in range(11):
self.lay.add_widget(Label(text=str(self.data[i][j])))
self.add_widget(self.lay)
class Root(BoxLayout):
def __init__(self):
super(Root, self).__init__()
self.orientation = "vertical"
#Window.clearcolor = (0.6, 0.6, 0.6,1)
sm = Manager()
self.add_widget(Nav(sm=sm))
self.add_widget(sm)
self.add_widget(Button(text='refresh',size_hint=(0.2,0.2),on_release=self.refresh))
Window.size = (1500, 900)
def refresh(self,ins):
sm=Manager()
sm.refresh()
class TestApp(App):
def build(App):
return Root()
if __name__ == '__main__':
TestApp().run()
Two problems with your code. First, in your refresh() method, the self.clear_widgets(screens='main') is incorrect. The screens arg must be a list of screens. So it should be
def refresh(self):
self.clear_widgets(screens=[self.get_screen('main')])
base=Base('main')
self.add_widget(base)
Since you are only removing one screen, you could use self.remove_widget(self.get_screen('main')) instead.
And, second, your refresh() method in the Root class is creating a new Manager class and calling the refresh() method of that new Manager rather than the one you have displayed. To correct this, you can save a reference to the original Manager, and use that reference in the refresh() method:
class Root(BoxLayout):
def __init__(self):
super(Root, self).__init__()
self.orientation = "vertical"
#Window.clearcolor = (0.6, 0.6, 0.6,1)
sm = Manager()
self.sm = sm # keep a reference for later use
self.add_widget(Nav(sm=sm))
self.add_widget(sm)
self.add_widget(Button(text='refresh',size_hint=(0.2,0.2),on_release=self.refresh))
Window.size = (1500, 900)
def refresh(self,ins):
self.sm.refresh()
I'm trying to disable a Kivy button. To do so, I've created a BooleanProperty and I've set the disabled button property as that BooleanProperty. When I launch, the button is created properly with the value I've assigned it (False). However, when I update the BooleanProperty, nothing happens to the button. When I click, I know that the property is properly binded and changed, because a print function shows me that it is changed.
What could be the issue? I am using Kivy 1.10.0. I've only kept the relevant lines of code - I don't think I missed anything, but I added comments with regards to what works.
class LoginScreen(GridLayout):
def __init__(self, **kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.selection = SelectionScreen()
self.add_widget(self.selection)
self.selection.bind(buttonStatus=self.disableButton)
def disableButton(self, instance, value):
self.selection.buttonStatus = value
print(self.selection.buttonStatus) # returns True when I press button
class SelectionScreen(GridLayout):
buttonStatus = BooleanProperty()
# button to disable
self.buttonStatus = False # putting True disables button at launch
btn = Button(text='BTN',disabled=self.buttonStatus)
btn.bind(on_press=self.btnFunction)
self.add_widget(btn)
def btnFunction(self, value=None):
self.buttonStatus = True # this updates property but interface not affected
btn = Button(text='BTN', disabled=self.buttonStatus) doesn't bind buttonStatus property with the button's disabled property. To do that, you can use bind together with the setter method:
self.bind(buttonStatus=btn.setter("disabled"))
Example based on your code:
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.properties import BooleanProperty
from kivy.uix.button import Button
class MainWindow(GridLayout):
buttonStatus = BooleanProperty()
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.cols = 1
self.buttonStatus = False
btn = Button(text='BTN', disabled=self.buttonStatus)
btn.bind(on_press=self.btnFunction)
self.bind(buttonStatus=btn.setter("disabled"))
self.add_widget(btn)
def btnFunction(self, *args):
self.buttonStatus = True
class TestApp(App):
def build(self):
return MainWindow()
if __name__ == '__main__':
TestApp().run()
I'm trying to bind the following function to a Button in Kivy.
def auth(self):
print(self.username)
if self.username == "Hendricko":
print("self.username == Hendricko")
popup = Popup(title="success",
content=Label(text="Howdy !"),
size=(100, 100),
size_hint=(0.3, 0.3),
auto_dismiss=False)
popup.open()
I've tried
class Foo():
def initUI(self):
self.add_widget(Button(text="Auth User and Password", on_press=self.auth))
but this doesn't work. What am I doing wrong?
here is my whole code
from kivy.uix.popup import Popup
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.boxlayout import BoxLayout
from kivy.uix.stacklayout import StackLayout
class LoginScreen(GridLayout):
def __init__(self, **kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.cols = 2
self.row = 2
self.add_widget(Label(text='User Name'))
self.username = TextInput(multiline=False)
self.add_widget(self.username)
self.add_widget(Label(text='password'))
self.password = TextInput(password=True, multiline=False)
self.add_widget(self.password)
self.hello = Button(text="hello", on_press=self.auth)
self.add_widget(self.hello)
def auth(self):
if self.username == "Hendricko":
popup = Popup(title="success",
content=Label(text="Howdy !"),
size=(100, 100),
size_hint=(0.3, 0.3),
auto_dismiss=False)
popup.open()
class MyApp(App):
def build(self):
return LoginScreen()
if __name__ == '__main__':
MyApp().run()
I don't think any of the answers are very clear. Neither explains that problem is that the callback given to on_press gets called with a parameter, the instance of button, so LoginScreen.auth must accept a parameter after the self:
def auth(self, button):
print('button pressed:', instance)
The problem is not that on_press must be given via Button.bind or that the callback must be a function, it can be a bound method, and the docs cited by other answer and by comments link to ButtonbBhavior which indicates that OP use of on_press in constructor was fine:
self.hello = Button(text="hello", on_press=self.auth)
would have worked if auth had been as described above.
If you read the Button documentation, the key seems to be to use the bind function:
def callback(instance):
print('The button <%s> is being pressed' % instance.text)
btn1 = Button(text='Hello world 1')
btn1.bind(on_press=callback)
Replace line
self.hello = Button(text="hello", on_press=lambda a:self.auth())
of your code and use this :
self.hello = Button(text="hello", on_press=lambda a:self.auth())
Also add below line in auth function to see if its called :)
print "auth called"
and There are many ways to perform a particular task .Above code will be to fix your code in minimum effort , However If you would like to do it in another way . Just use code below .
from kivy.uix.popup import Popup
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.boxlayout import BoxLayout
from kivy.uix.stacklayout import StackLayout
class LoginScreen(GridLayout):
def __init__(self, **kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.cols = 2
self.row = 2
self.add_widget(Label(text='User Name'))
self.username = TextInput(multiline=False)
self.add_widget(self.username)
self.add_widget(Label(text='password'))
self.password = TextInput(password=True, multiline=False)
self.add_widget(self.password)
self.hello = Button(text="hello")
self.hello.bind(on_press=self.auth)
self.add_widget(self.hello)
def auth(self,instance):
print "auth called"
if self.username == "Hendricko":
popup = Popup(title="success",
content=Label(text="Howdy !"),
size=(100, 100),
size_hint=(0.3, 0.3),
auto_dismiss=False)
popup.open()
class MyApp(App):
def build(self):
return LoginScreen()
if __name__ == '__main__':
MyApp().run()
I report an example of buttons created dynamically within the main class, and then triggered into a single listener:
class allMyApp(TabbedPanel):
def __init__(self, **kwargs):
super(allMyApp, self).__init__(**kwargs)
#[...]
for y in sorted(set(emissionYears)):
btn = Button(text = y,
size_hint_y = None,
height = '48dp',
on_release = lambda btn: self.ids.choseEmissionDate.select(btn.text))
btn.bind(on_press = self.sortByYear)
self.ids.choseEmissionDate.add_widget(btn)
def sortByYear(self, instance):
year = instance.text
print(year)
I want to make a game and I have chosen Kivy for my GUI, as I have written my backend in Python. I am currently using runTouchApp(appwindow) to run the application, where appwindow is a FloatLayout() object. The way I update my screen is that I run appwindow.clear_widgets() and then appwindow.add_widget(new_screen) where new_screen is the layout object which contains all other widgets.
It worked fine till now, but for some reason I can't understand the widget I add gets loaded properly (according to the cmd running in the background) but won't display till I change the screen size.
Here is a sample code:
import kivy
kivy.require('1.1.1')
from kivy.base import runTouchApp
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
appwindow = FloatLayout()
class MenuScreen():
def build(self):
bg = Image(source = 'Images/bg/bg.jpg',allow_stretch= True,keep_ratio= False)
box = BoxLayout(orientation = 'vertical')
menu_items = []
for i in range (0,4):
button = Button(text = 'Menu item no:'+str(i))
button.bind(state = checkMenuPress)
menu_items.append(button)
for button in menu_items:
box.add_widget(button)
floater = FloatLayout(size = bg.size)
floater.add_widget(bg)
floater.add_widget(box)
floater.size_hint = 1,1
return floater
class SecondScreen():
def build(self):
bg = Image(source = 'Images/bg/bg.jpg',allow_stretch= True,keep_ratio= False)
box = BoxLayout(orientation = 'vertical')
box.add_widget(bg)
return box
def checkMenuPress(button,*args):
if button.state == 'normal':
sc = SecondScreen().build()
appwindow.clear_widgets()
appwindow.add_widget(sc)
if __name__ == '__main__':
menuscreen = MenuScreen().build()
appwindow.add_widget(menuscreen)
runTouchApp(appwindow)
Okay, there are a few things wrong here. To create an app, you should subclass kivy.app.App and have it's build method return the "root" widget. Only this class needs build method: any others are redundant.
Once you have a root widget, it's just a matter of inserting and removing widgets. This is how I would approach it.
import kivy
kivy.require('1.1.1')
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
from kivy.app import App
class MenuScreen(App):
floater = None
def build(self):
bg = Image(source='Images/bg/bg.jpg',
allow_stretch= True,
keep_ratio= False)
box = BoxLayout(orientation='vertical')
menu_items = []
for i in range(0, 4):
button = Button(text='Menu item no:'+str(i))
button.bind(state=self.checkMenuPress)
menu_items.append(button)
for button in menu_items:
box.add_widget(button)
self.floater = FloatLayout(size=bg.size)
self.floater.add_widget(bg)
self.floater.add_widget(box)
self.floater.size_hint = 1, 1
return self.floater
def checkMenuPress(self, button,*args):
if button.state == 'normal':
self.floater.clear_widgets()
self.floater.add_widget(SecondScreen())
class SecondScreen(FloatLayout):
def __init__(self, **kwargs):
super(SecondScreen, self).__init__(**kwargs)
self.add_widget(Image(source='Images/bg/bg.jpg',
allow_stretch=True,
keep_ratio=False))
if __name__ == '__main__':
MenuScreen().run()
Okay, so here's a quick rework of the example using Screens. It's difficult without knowing what you are trying to do, so if this does not help, try writing summary of the behavior you are looking for.
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
class MenuScreen(Screen):
"""
The Screen containing the menu
"""
def __init__(self, sm, **kwargs):
super(MenuScreen, self).__init__(**kwargs)
self.sm = sm # Store a reference to the screen manager
bg = Image(source='Images/bg/bg.jpg',
allow_stretch= True,
keep_ratio= False)
box = BoxLayout(orientation='vertical')
menu_items = []
for i in range(0, 4):
button = Button(text='Menu item no:'+str(i))
button.bind(state=self.check_menu_press)
menu_items.append(button)
for button in menu_items:
box.add_widget(button)
floater = FloatLayout(size=bg.size)
floater.add_widget(bg)
floater.add_widget(box)
floater.size_hint = 1, 1
self.add_widget(floater)
def check_menu_press(self, button, *args):
if button.state == 'normal':
self.sm.current = "second"
class SecondScreen(Screen):
def __init__(self, sm, **kwargs):
super(SecondScreen, self).__init__(**kwargs)
self.add_widget(Image(source='Images/bg/bg.jpg',
allow_stretch=True,
keep_ratio=False))
self.sm = sm # Store a reference to the screen manager
def on_touch_down(self, touch):
self.sm.current = "menu"
class MainApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(MenuScreen(sm, name="menu"))
sm.add_widget(SecondScreen(sm, name="second"))
return sm
if __name__ == '__main__':
MainApp().run()
looks like i have found a source of the crashing problem. I had used multi-threading for a server client interaction. i had been using two extra threads, one for the server and another for the client. If i try to integrate the client thread with the GUI it raises another problem. even if the socket.recv() is after the ScreenManager.current = "newscreen" line, the app's screen does not change. it freezes in the way i left it. it resumes only after it gets a message from the server.