kivy Screen Manager and popup - python

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()

Related

Python Kivy Dyanmic Class not defined

I keep getting a Name Error reporting "name TripButton is not defined" although I have a button called TripButton defined in the same .kv file.
#:kivy 1.11.1
<TripButton#Button>:
size_hint: (0.15,0.15)
pos_hint: {'y':0.84}
text:"Test Text"
<MyPopup>:
size_hint: 0.5, 0.5
auto_dismiss: False
title: 'New Trip'
BoxLayout:
orientation: 'vertical'
TextInput:
id: trip_name
multiline: False
BoxLayout:
size_hint_y: None
height: 30
Button:
text: 'Submit'
on_release:
root.parent.parent.add_widget(TripButton(text=trip_name.text, size_hint=(1,0.1)))
root.dismiss()
Button:
text: 'Cancel'
on_release: root.dismiss()
<FirstScreen>:
name: 'first'
FloatLayout:
BoxLayout:
size_hint: (0.95, 0.95)
pos_hint: {'center_x':0.5, 'center_y':0.5}
orientation: "vertical"
canvas.before:
Color:
rgba: (1, 0, 0, 1) # Red color
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
TripButton:
Button:
text: 'Add Trip'
font_size: 12
size_hint: (0.1, 0.1)
pos_hint: {'right':1, 'bottom':1}
on_release: app.open_popup()
pos_hint: {'right':1, 'bottom':1}
on_release: app.open_popup()
I tried declaring the TripButton class above and below the MyPopup class and that didn't work. I compared my code with the kivy documentation for dynamic classes and the from what I can tell I'm matching the syntax. I tried importing the Button class in both the python file and the .kv file and that hasn't worked.
To clarify all of this is in the same .kv file in the same directory as the python file. the app runs until I click the submit button in MyPopup then the program crashes and I get the NameError. This is what I have in my python file.
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.popup import Popup
class FirstScreen(Screen):
pass
class MyPopup(Popup):
pass
class MyApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(FirstScreen(name='first'))
sm.current = 'first'
return sm
def open_popup(self):
p = MyPopup()
p.open()
def add_button(self):
pass
if __name__ == '__main__':
MyApp().run()
What am I doing wrong? What else I can try? Thanks for your time.
To access classes defined within kv file use kivy.factory.Factory
Add id attribute to your button's BoxLayout, so replace following part of kv file:
BoxLayout:
TripButton:
with
BoxLayout:
id: trip_buttons
TripButton:
then replace:
root.parent.parent.add_widget(TripButton(text=trip_name.text, size_hint=(1,0.1)))
with:
app.root.current_screen.ids.trip_buttons.add_widget(kivy.factory.Factory.TripButton(text=trip_name.text, size_hint=(1,0.1)))
You also duplicated following lines within last button definition on first screen. Replace:
pos_hint: {'right':1, 'bottom':1}
on_release: app.open_popup()
pos_hint: {'right':1, 'bottom':1}
on_release: app.open_popup()
with
pos_hint: {'right':1, 'bottom':1}
on_release: app.open_popup()

Kivy ScreenManager transitions not animating after pop up is clicked

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.

Multiple classes in one screen stops FloatLayout from working correctly

I am trying to incorporate ads into a Kivy app I made, and to do this, I want to have one class (with the ads) in a FloatLayout to be at the top of my screen, and the rest of the app to be below, in a separate class.
I was trying to get this to work with some test code (simple .py and .kv file that has multiple screens and classes and organizes accordingly). The code is supposed to have two float layouts: One has text, the other has a button that you press and it takes you to the next screen. However the issue I am having is that I can't position the button correctly, as it appears that the widget is shrunk in the bottom left corner. It is supposed to be next to the text box.
Here is my .kv file:
WindowManager:
Screen1:
Screen2:
<Screen1>:
name: "screen1"
FloatLayout:
Label:
pos_hint: {'top': 1, "center_x": 0.5}
size_hint: (0.2, 0.5)
font_size: 40
text: "TEXT AT TOP OF SCREEN"
FloatLayout:
TextInput:
pos_hint: {"x": 0.1, "y": 0.05}
size_hint: (0.3, 0.05)
multline:False
GoS:
FloatLayout:
Button:
text: "PRESS TO GO TO SCREEN 2"
pos_hint: {"right": 0.5, "center_y": 0.7}
on_press: widget.goscreen()
<Screen2>:
name: "screen2"
Label:
text: "YOU ARE ON SCREEN TWO"
and here is the .py file:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
class Screen1(Screen):
pass
class GoS(Widget):
def goscreen(self):
self.parent.current = "screen2"
class Screen2(Screen):
pass
class WindowManager(ScreenManager):
pass
kv = Builder.load_file("rec_view.kv")
class TestApp(App):
def build(self):
return kv
if __name__ == "__main__":
TestApp().run()
Why is this happening?
Another small point also is that my button doesn't work because I can't seem to call the correct class. If I use "root.goscreen()", it doesn't work as my root widget doesn't have this function. What should be the correct syntax here?
I recommend you to use BoxLayout to divide your GUI.
Your button don't work because you don't link it to a valid Widget.
The GoS widget must be define in KV or be imported.
Here is a proposition for your rec_view.kv file :
WindowManager:
Screen1:
Screen2:
<GoS>:
Button:
text: "PRESS TO GO TO SCREEN 2"
on_press: root.goscreen()
<Screen1>:
name: "screen1"
BoxLayout:
orientation: "vertical"
BoxLayout:
orientation: "horizontal"
size_hint_y: 0.5 # Proportion of screen height that the widget occupe
Label:
font_size: 40
text: "TEXT AT TOP OF SCREEN"
BoxLayout:
orientation: "horizontal"
size_hint_y: 0.3
TextInput:
multline:False
BoxLayout:
orientation: "horizontal"
size_hint_y: 0.2
GoS:
<Screen2>:
name: "screen2"
Label:
text: "YOU ARE ON SCREEN TWO"
The definition of goscreen is not correct, GoS is not a Screen, so his parent don't have current , use instead App.get_running_app().root.current = "screen2".
I don't know what you want to do and why you define the GoS class, but you can avoid it. By moving the goscreen definition into Screen1 class, and replace the 3rd BoxLayout of Screen1 with this:
BoxLayout:
orientation: "horizontal"
size_hint_y: 0.2
Button:
text: "PRESS TO GO TO SCREEN 2"
on_press: root.goscreen()

Kivy Selection on Focus

I'm trying to have kivy select the text of a TextInput widget on focus but when I try it seems to select it when it unfocuses and retains the selection. Any ideas how I can select it on focus and on unfocus deselect? I've attached my code below if someone wants to have a play around.
kv file:
<TextInput>:
size_hint: 0.9, 0.5
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
multiline: False
<Button>:
text: "Press Me"
size_hint: (0.1, 0.5)
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
<MainLayout>:
canvas.before:
Color:
rgba: 0.15, 0.15, 0.16, 1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
orientation: 'vertical'
padding: 10
BoxLayout:
padding: 10
TextInput:
text: "Directory"
Button:
text: "Browse"
on_press: root.browse_btn()
BoxLayout:
padding: 10
TextInput:
text: "Prefix"
on_focus: self.select_all()
TextInput:
text: "File"
on_focus: self.select_all()
TextInput:
text: "Suffix"
on_focus: self.select_all()
BoxLayout:
padding: 10
Button:
id: button_one
text: "Confirm"
on_press: root.confirm_btn()
Button:
text: "Cancel"
on_press: root.cancel_btn()
python file:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivy.core.window import Window
from kivy.config import Config
Config.set('graphics', 'resizable', 0)
class MainLayout(BoxLayout):
button_id = ObjectProperty(None)
def browse_btn(self):
print("Hey")
def confirm_btn(self):
print("Confirm")
def cancel_btn(self):
print("Cancel")
class BatchRenameApp(App):
def build(self):
self.title = "Batch File Rename"
Window.size = (750, 250)
return MainLayout()
if __name__ == '__main__':
app = BatchRenameApp()
app.run()
Well hidden in the TextInput documentation:
Selection is cancelled when TextInput is focused. If you need to show
selection when TextInput is focused, you should delay (use
Clock.schedule) the call to the functions for selecting text
(select_all, select_text).
So, in your kv, start by importing Clock:
#: import Clock kivy.clock.Clock
Then you can use it in a TextInput rule:
TextInput:
text: "Prefix"
on_focus: Clock.schedule_once(lambda dt: self.select_all()) if self.focus else None
The if self.focus makes sure the select_all only happens when the TextInput gains focus.

How to change a space when a button is pressed with kivy?

I am trying create a GUI by implementing the template of the ComicCreator GUI sample as a template for my own project. The code is easy to follow, but I would like to be able to reconfigure the drawingspace.kv, each time a button is pushed, say for example something like this:
Q: How could I configure the drawingspace.kv to have a different layout with different widgets for each button that is pressed?
A neat way to do this is to use screen.
Since I allready have an example of this app from you earlier question, it was easy to implement the screens, and rewrite the classes a bit.
When a button is pressed, you set the screenmanager's current to whatever the name you named the screen you want.
Then you just edit the layouts as you want inside of each screen, in the kv file, or python file.
I choose to make most of the layout stuff in kv language here. Because I find it easier to develop a layout the way I want it this way.
Later I could rewrite it to python if I want that.
So my python file looks like this now:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.clock import Clock
from kivy.uix.screenmanager import Screen,ScreenManager,NoTransition
from kivy.lang import Builder
import time
Builder.load_file("kv.kv")
class MyLayout(BoxLayout):
def __init__(self,**kwargs):
super(MyLayout,self).__init__(**kwargs)
self.orientation = "vertical"
self.padding = 10
class MainScreen(Screen):
pass
class RemoveScreen(Screen):
pass
class GroupScreen(Screen):
pass
class MyLogo(BoxLayout):
your_time = StringProperty()
def __init__(self,**kwargs):
super(MyLogo,self).__init__(**kwargs)
Clock.schedule_interval(self.set_time, 0.1)
def set_time(self,dt):
self.your_time = time.strftime("%m/%d/%Y %H:%M")
class MyApp(App):
def __init__(self,**kwargs):
super(MyApp,self).__init__(**kwargs)
self.sm = ScreenManager(transition=NoTransition())
self.sm.add_widget(MainScreen(name = "main"))
self.sm.add_widget(RemoveScreen(name = "remove"))
self.sm.add_widget(GroupScreen(name = "group"))
self.sm.current = "main"
def build(self):
return self.sm
if __name__ == "__main__":
MyApp().run()
And kv.kv file looks like this:
#:kivy 1.9.1
<MyButtons#BoxLayout>:
padding: 10,10,10,0
spacing: 10
size_hint: 1,0.3
orientation: "horizontal"
Button:
text: "Clear"
on_press: app.sm.current = "main"
Button:
text: "Remove"
on_press: app.sm.current = "remove"
Button:
text: "Group"
on_press: app.sm.current = "group"
Button:
text: "Color"
Button:
text: "Gestures"
<MyLogo>:
spacing: 10
padding: 10,10,10,0
orientation: "horizontal"
BoxLayout:
orientation: "vertical"
size_hint: 0.3,1
canvas:
Rectangle:
pos: self.pos
size: self.size
AsyncImage
source: 'http://lmsotfy.com/so.png'
Label:
size_hint: 1,0.3
text: root.your_time
color: [0,0,0,1]
Label:
size_hint: 1,0.3
text: "NYC, New York, USA"
color: [0,0,0,1]
<MainScreen>:
MyLayout:
MyLogo:
#Button:
# text: "main"
MyButtons:
#buttons
BoxLayout:
padding: 10,10,10,10
size_hint: 1,0.3
Button:
text: "Total figures: 1 Kivy Started"
<RemoveScreen>:
MyLayout:
MyLogo:
BoxLayout:
orientation: "horizontal"
Label:
font_size: "40sp"
text: "Remove"
Button:
font_size: "20sp"
text: "Remove this or something"
MyButtons:
#buttons
BoxLayout:
padding: 10,10,10,10
size_hint: 1,0.3
Button:
text: "Total figures: 1 Kivy Started"
<GroupScreen>:
MyLayout:
MyLogo:
BoxLayout:
orientation: "vertical"
Label:
font_size: "40sp"
text: "Group"
Button:
font_size: "20sp"
text: "Something groups stuff"
MyButtons:
#buttons
BoxLayout:
padding: 10,10,10,10
size_hint: 1,0.3
Button:
text: "Total figures: 1 Kivy Started"
The layout frame should be a screen manager, and each layout a screen. Screen transitions would be then triggered by pressing the buttons. You can also watch a tutorial here if you don't know how to do this, but the documentation should be enough.

Categories