Hi I'm making an app but there's an issue occurring. There are 2 buttons each on different screens. How do I center both buttons?
1st button is in MainWindow and 2nd button is in ImportWindow.
Please check my code.
Both buttons displaying at (0,0) position. I want to set the position in the center.
this is .py file
from kivy.app import App
from kivy.uix.image import Image
from kivy.uix.widget import Widget
from kivy.uix.screenmanager import ScreenManager, Screen
class MainWindow(Screen):
pass
class ImportWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
class MySplashScreenApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(MainWindow(name='main'))
sm.add_widget(ImportWindow(name='second'))
return sm
return MainWindow()
if __name__ == '__main__':
MySplashScreenApp().run()
and this is .kv file
WindowManager:
MainWindow:
ImportWindow:
<MainWindow>:
Button:
text: "Start"
font_size: 12
size: 75, 50
size_hint: None, None
pos_hint: 100, 100
on_release:
root.manager.current = "second"
root.manager.transition.direction = 'left'
<ImportWindow>:
Button:
text: "Import"
font_size: 12
size: 75, 50
size_hint: None, None
pos_hint: 100, 100
on_release:
root.manager.current = "main"
root.manager.transition.direction = 'right'
Your use of pos_hint is incorrect. As shown in the documentation, the pos_hint is a dictionary. in order to center a widget using pos_hint, you can use:
pos_hint: {'center_x':0.5, 'center_y':0.5}
Related
I am new to Kivy and struggling to change elements in MainWindow based on what happens in SecondWindow. The code below is how far I have got on my own. The clicking of the "A" button in SecondWindow fires an event in WindowManager and prints "A" in console. However, I would like to update id: labeltext in MainWindow based on that click. Can that be done directly somehow or do how do I pass information from WindowManager to MainWindow. If so, how? I am new to object oriented programming so these things cause a lot of head scratching to me. Thank you!
Python code:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.popup import Popup
from kivy.uix.textinput import TextInput
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
class MainWindow(Screen):
pass
class SecondWindow(Screen):
pass
class WindowManager(ScreenManager):
def keykey(self,whichkey):
print(whichkey)
kv = Builder.load_file("multiscreen.kv")
class MyMainApp(App):
def build(self):
return kv
if __name__ == '__main__':
MyMainApp().run()
Kivy:
WindowManager:
MainWindow:
SecondWindow:
<MainWindow>:
name: "main"
labeltext: labeltext
Label:
id: labeltext
text: "XXX"
size_hint: 0.2, 0.1
pos_hint: {"x":0.5, "y":0.8}
Button:
text: "Go Second"
size_hint: 0.1, 0.1
pos_hint: {"x":0.9, "y":0.9}
on_release:
app.root.current = "second"
root.manager.transition.direction = "left"
<SecondWindow>:
name: "second"
Button:
text: "A"
size_hint: 0.1, 0.1
pos_hint: {"x":0.5, "y":0.5}
on_release:
root.manager.keykey('A')
print("dfd")
Button:
text: "Go Main"
size_hint: 0.1, 0.1
pos_hint: {"x":0.0, "y":0.9}
on_release:
app.root.current = "main"
root.manager.transition.direction = "right"
In your kv, change the rule for the Button to include:
root.manager.get_screen('main').ids.labeltext.text = 'New Text'
I have this small issue where I'm trying to create a label in LabelScreen when a button in the MenuScreen is pressed. The closest I managed to do was making the Label appear in the same screen as the button.
The following code is just an simple example of what I would like to acheive. Any help is always appreciated!
.py
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.uix.button import Button,ButtonBehavior
from kivy.uix.image import Image
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.popup import Popup
from kivy.core.window import Window
from kivy.properties import ObjectProperty, NumericProperty
class MenuScreen(Screen):
def Item1(self,button):
App.get_running_app().cart += 5
label1 = Label(text="Item 1: $5", font_size=25)
self.add_widget(label1)
class LabelScreen(Screen):
pass
class WindowManager(ScreenManager):
pass
class ExampleApp(App):
cart = NumericProperty(0)
def build(self):
return WindowManager()
if __name__ == "__main__":
ExampleApp().run()
.kv
#:import SlideTransition kivy.uix.screenmanager.SlideTransition
<WindowManager>:
MenuScreen:
LabelScreen:
<MenuScreen>:
name: "menu"
FloatLayout:
Button:
text: "Add to cart"
size_hint: 0.5,0.05
pos_hint: {"x": 0.25, "y": 0.25}
on_release:
root.Item1(self)
Label:
text: "Cart: " + str(app.cart)
font_size: 25
pos_hint: {"x": 0.25, "y": 0.25}
Button:
text: "Label Screen"
size_hint: 0.5,0.05
pos_hint: {"x": 0.25, "y": 0.1}
on_release:
app.root.transition = SlideTransition(direction = "left")
app.root.current = "label"
<LabelScreen>:
name: "label"
FloatLayout:
Label:
text: "I want Item1 Label to appear here \n instead of in the MenuScreen"
font_size: 25
pos_hint: {"x": 0, "y": 0.25}
Button:
text: "Back"
size_hint: 0.5,0.05
pos_hint: {"x": 0.25, "y": 0.1}
on_release:
app.root.transition = SlideTransition(direction = "right")
app.root.current = "menu"
You have to reference the right screen window you want to add the wiget.
Since you are adding the widget whitin the python class first we have to get the app running, then we can navigate to the screen we want:
def Item1(self,button):
App.get_running_app().cart += 5
label1 = Label(text="Item 1: $5", font_size=25)
app_running = App.get_running_app()
root_screen = app_running.root
label_screen = root_scren.get_screen('label')
label_screen.add_widget(label1)
# Or simply:
# App.get_running_app().root.get_screen('label').add_widget(label1)
I am new to Kivy and I recently started to create my own application. But there's one problem I have that I can't figure out why it's doing it.
Basically when I open my app and press the button to go to the other screen it does the sliding transition succesfully. But when I open a pop up and close it and hit the same button again, it doesn't do any transition and both screens overlap each other. Why exactly is this the case?
The thing that almost fixed it is using self.manager.current = "second" in the python file but the sliding animation is still not there and it just cuts to the next screen. Or pehaps is there a way to make the sliding animation inside the python file?
I have provided a simple example code to demonstrate the issue. I didn't want to put all of what I've done since it would be too much code.
Any help or suggestions would be appriciated!
Here is the example code
import kivy
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.uix.label import Label
from kivy.uix.button import Button,ButtonBehavior
from kivy.uix.widget import Widget
from kivy.uix.screenmanager import Screen, ScreenManager, SlideTransition, CardTransition, FadeTransition
from kivy.uix.popup import Popup,PopupException
from kivy.lang import Builder
class FirstScreen(Screen):
def screen1btn(self):
popup = FloatLayout()
pop = Popup(title="Popup", content=popup, size_hint=(0.6, 0.6))
pop.open()
class SecondScreen(Screen):
pass
class WindowManager(ScreenManager):
pass
class ExampleApp(App):
def build(self):
return WindowManager()
if __name__ == "__main__":
ExampleApp().run()
.kv file
#:import SlideTransition kivy.uix.screenmanager.SlideTransition
<WindowManager>:
FirstScreen:
SecondScreen:
<FirstScreen>:
name: "first"
FloatLayout:
Label:
text: "Screen 1"
font_size: 20
pos_hint: {"x": 0.35,"y": 0.3}
Button:
text: "Popup"
size_hint: 0.3,0.1
pos_hint: {"x": 0.35,"y": 0.4}
on_release:
app.root.transition = SlideTransition(direction = "left")
app.root.current = root.screen1btn()
Button:
text: "Go to second screen"
size_hint: 0.5,0.1
pos_hint: {"x": 0.35,"y": 0.1}
on_release:
app.root.transition = SlideTransition(direction = "right")
app.root.current = "second"
<SecondScreen>:
name: "second"
FloatLayout:
Label:
text: "Screen 2"
font_size: 20
pos_hint: {"x": 0.35,"y": 0.3}
Button:
text: "Go back"
size_hint: 0.5, 0.1
pos_hint: {"x": 0.35,"y": 0.1}
on_release:
app.root.transition = SlideTransition(direction = "left")
app.root.current = "first"
The problem is your 'kv' code for the Popup Button:
Button:
text: "Popup"
size_hint: 0.3,0.1
pos_hint: {"x": 0.35,"y": 0.4}
on_release:
app.root.transition = SlideTransition(direction = "left")
app.root.current = root.screen1btn()
Not sure what that is tying to do, but I believe it should just be opening the Popup, like this:
Button:
text: "Popup"
size_hint: 0.3,0.1
pos_hint: {"x": 0.35,"y": 0.4}
on_release: root.screen1btn()
Note that the two lines referencing app.root have been removed. Now the Button just opens the Popup.
I have a app in kivy with a screen manager and a popup within it. The popup works until the point I put a button with the close function into the popup window. At this point i get the message:
PopupException: Popup can have only one widget as content
There is another post on this topic but it does not seem to work.
The python code
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.lang import Builder
from kivy.uix.popup import Popup
class CustomPopup(Popup):
pass
class MainScreen(Screen):
pass
class ContentScreen(Screen):
def open_popup(self):
the_popup = CustomPopup()
the_popup.open()
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("am.kv")
class AMApp(App):
def build(self):
return presentation
if __name__ == "__main__":
AMApp().run()
The kivy file is below. The issue seems to come in the button function when calling the custompop
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManagement:
transition: FadeTransition()
MainScreen:
ContentScreen:
<CustomPopup>:
size_hint: .5 , .5
auto_dismiss: False
title: "The Popup"
Button:
text: "Close"
on_press: root.dismiss()
<MainScreen>:
name: "Welcome"
Button:
text: "First Screen"
size_hint: 1, .5
font_size: 40
pos_hint: {'center_x': 0.5, 'center_y': 0.7}
on_release: app.root.current = "other"
Button:
text: 'Welcome Mr and Mrs Shaw'
size_hint: 1, .5
font_size: 25
pos_hint: {'center_x': 0.5, 'center_y': 0.3}
on_release: app.root.current = "other"
<ContentScreen>:
name: "other"
BoxLayout:
orientation: "vertical"
size_hint_x: .22
Button:
text: "open Popup"
on_press: root.open_popup()
The code posted above, runs fine on Linux Buster, Kivy 1.11.0-dev and 1.10.1, and Python 3.7.3rc1
Solution
Try adding a layout e.g. BoxLayout into CustomPopup to solve the PopupException.
Example
The following example illustrates a popup window with a message and a button.
Snippets
<CustomPopup>:
size_hint: .5 , .5
auto_dismiss: False
title: "The Popup"
BoxLayout:
orientation: 'vertical'
Label:
text: "Hello Kivy"
Button:
text: "Close"
on_press: root.dismiss()
Question:
How do you use an On-Press event to change screen for dynamically created buttons in python, and without using KV language?
Goal:
Be able to navigate to new screen from clicking on dynamically created button,
[without needing to create button in Kivy, and still getting to use Screenmanager in both Python and Kivy (not sure if you have to stick with either Python or Kivy throughout entire program?]
Things I've already tried:
Using button_share.bind(on_press = self.changer), then this:
def changer(self,*args):
ScreenManager()
screenmanager.current = 'MainScreen'
But I get the error ScreenManagerException: No Screen with name "MainScreen".
Suspicion:
I think this is because I'm creating a new instance of ScreenManager, instead of referencing the existing one. To combat this issue, I considered instantiating Screenmanager() in the App class, then referencing that instantiation in the my button def changer(self, *args) method, but that's useless if it's not the same ScreenManager I'm actually using for all my screens. And those are all defined in KV language. I wouldn't be able to switch them all over without a substantial amount of effort.
Using:
button_share.bind(on_press=partial(app.sm.setter('current'), (app.sm, "MainScreen")))`
But the error I get here is ValueError: ScreenManager.current accept only str
Below is a fully runnable example:
Note: In this example, I want to click 'Continue Editing' button, then click on 'Test 1', 'Test 2' or 'Test 3' button and have it take me to another screen.
Python Code:
from kivy.app import App
# kivy.require("1.10.0")
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.clock import Clock
from kivy.uix.widget import Widget
#from kivy.base import runTouchApp
from kivy.properties import StringProperty, ObjectProperty, NumericProperty
from functools import partial
class ScrollableLabelDataEntryInstructions(BoxLayout):
pass
class NewGarageScreen(Screen):
pass
class ContinueEditingScreen(Screen):
pass
class GarageNameBoxLayout(BoxLayout):
box_share2 = ObjectProperty()
sm = ScreenManager()
def __init__(self, **kwargs):
super(GarageNameBoxLayout, self).__init__(**kwargs)
self.orientation = "vertical"
Clock.schedule_interval(self.create_button, 5)
def create_button(self, *args):
self.box_share2.clear_widgets()
app = App.get_running_app()
#put GarageNameStartList data into app class, then pull from it in this class
top_button_share = 1.1
color = (.4, .4, .4, 1)
for i in range(len(app.GarageNameStartList)):
top_button_share -= .4
id_ = app.GarageNameStartList[i]
button_share = Button(background_normal='',
background_color = color,
id = id_,
pos_hint = {"x": 0, "top": top_button_share},
size_hint_y = None,
height = 60,
font_size = 30,
text = app.GarageNameStartList[i])
button_share.bind(on_press = self.changer)
#button_share.bind(on_press=partial(app.sm.setter('current'), (app.sm, "MainScreen")))
self.box_share2.add_widget(button_share)
def changer(self,*args):
ScreenManager()
#app = App.get_running_app()
screenmanager.current = 'MainScreen'
class BackHomeWidget(Widget):
pass
class MainScreen(Screen):
pass
class AnotherScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("example_on_press.kv")
class MainApp(App):
GarageNameStartList = ["Test1", "Test2", "Test3"]
def Update_GarageNameStartList(self, *args):
self.GarageNameStartList = ["Test1", "Test2", "Test3"]
def build(self):
return presentation
if __name__ == "__main__":
MainApp().run()
KV Code:
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManagement:
transition: FadeTransition()
MainScreen:
AnotherScreen:
NewGarageScreen:
ContinueEditingScreen:
<SmallNavButton#Button>:
font_size: 32
size: 125, 50
color: 0,1,0,1
<MedButton#Button>:
font_size: 30
size_hint: 0.25, 0.1
color: 0,1,0,1
<BackHomeWidget>:
SmallNavButton:
on_release: app.root.current = "main"
text: "Home"
pos: root.x, root.top - self.height
<MainScreen>:
name: "main"
FloatLayout:
MedButton:
on_release: app.root.current = "edit"
text: "Edit"
pos_hint: {"x":0.3728, "top": 0.4}
<AnotherScreen>:
name: "edit"
BackHomeWidget:
SmallNavButton:
on_release: app.root.current = "main"
text: "Back"
pos: root.x, root.top - (2.25*(self.height))
FloatLayout:
MedButton:
on_release: app.root.current = "continueediting"
text: "Continue Editing"
pos_hint: {"x":0.25, "top": 0.6}
MedButton:
on_release: app.root.current = "newgarage"
text: "Create New"
pos_hint: {"x":0.3728, "top": 0.4}
<NewGarageScreen>:
name: "newgarage"
BackHomeWidget:
SmallNavButton:
on_release: app.root.current = "edit"
text: "Back"
pos: root.x, root.top - (2.25*(self.height))
FloatLayout:
MedButton:
text: "1. Groundfloor"
pos_hint: {"x":0, "top": 0.6}
<GarageNameBoxLayout>:
box_share2: box_share2
ScrollView:
GridLayout:
id: box_share2
cols: 1
size_hint_y: None
size_hint_x: 0.5
spacing: 5
padding: 130
height: self.minimum_height
canvas:
Color:
rgb: 0, 0, 0
Rectangle:
pos: self.pos
size: self.size
<ContinueEditingScreen>:
name: "continueediting"
GarageNameBoxLayout:
BackHomeWidget:
SmallNavButton:
on_release: app.root.current = "edit"
text: "Back"
pos: root.x, root.top - (2.25*(self.height))
Your code can be improved in the following things:
You do not have to create box_share2 in the .py since you're creating it in the .kv
When you use sm = ScreenManager() you are creating another ScreenManager different from the original, that is not necessary.
It is not necessary to use range and len, make your code less readable, you just have to iterate.
If we look at the structure of the .kv we see that the presentation object is the ScreenManager so you can get it via app.root.
Using the above your code the solution is:
[...]
class GarageNameBoxLayout(BoxLayout):
def __init__(self, **kwargs):
super(GarageNameBoxLayout, self).__init__(**kwargs)
self.orientation = "vertical"
Clock.schedule_interval(self.create_button, 5)
def create_button(self, *args):
self.box_share2.clear_widgets()
app = App.get_running_app()
sm = app.root
#put GarageNameStartList data into app class, then pull from it in this class
top_button_share = 1.1
color = (.4, .4, .4, 1)
for text in app.GarageNameStartList:
top_button_share -= .4
id_ = text
button_share = Button(background_normal='',
background_color = color,
id = id_,
pos_hint = {"x": 0, "top": top_button_share},
size_hint_y = None,
height = 60,
font_size = 30,
text = text)
button_share.bind(on_press=lambda *args: setattr(sm, 'current', "main"))
self.box_share2.add_widget(button_share)
[...]