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.
Related
I want to catch a value from my first screen into my thirdscreen.
In the first, I write my name in an input field.
I go to the next window.
And I try to show my name in this last window.
So I share the code with you and I hope I will find an issue.
Python code :
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.properties import ObjectProperty
from kivy.uix.screenmanager import ScreenManager, Screen
#define ou different screens
class FirstWindow(Screen):
def envoyer(self):
name = self.ids.nom_input.text
self.ids.my_name.text = name
class SecondWindow(Screen):
pass
class ThirdWindow(Screen):
#PROBLEM HERE
def on_press(self):
self.ids.recup_infos.text = self.root.get_screen('FirstWindow').ids.my_name.text
class WindowManager(ScreenManager):
pass
class MonWidget(Widget):
pass
kv = Builder.load_file('new_window.kv')
class AwesomeApp(App):
def build(self):
Window.clearcolor = (0,0,0,0)
return kv
if __name__ == '__main__':
AwesomeApp().run()
My KV CODE :
WindowManager:
FirstWindow:
SecondWindow:
ThirdWindow:
<FirstWindow>:
name: "romain"
BoxLayout:
orientation: "vertical"
size: root.width, root.height
Label:
id: my_name
text: "Entrez votre nom"
font_size: 32
TextInput:
id: nom_input
multiline: False
size_hint: (1, .5)
Button:
text: "Next screen"
font_size: 32
on_press: root.envoyer()
on_release:
app.root.current = "Mickael"
root.manager.transition.direction = "left"
<SecondWindow>:
name: "Mickael"
BoxLayout:
orientation: "vertical"
size: root.width, root.height
Label:
text: "Entre votre ville"
font_size: 32
TextInput:
id: ville_input
multiline: False
size_hint: (1, .5)
Button:
text: "VĂ©rifier les infos"
font_size: 32
on_release:
app.root.current = "foncier"
root.manager.transition.direction = "left"
Button:
text: "go back first screen"
font_size: 32
on_release:
app.root.current = "romain"
root.manager.transition.direction = "right"
<ThirdWindow>:
name: "foncier"
BoxLayout:
orientation: "vertical"
size: root.width, root.height
Label:
text: "Verifier : "
font_size: 32
Label:
id: recup_infos
text: ""
font_size: 32
color: 'white'
Button:
text: "On press"
font_size: 32
#Problem HERE
on_press: root.on_press()
Button:
text: "Précedent"
font_size: 32
on_release:
app.root.current = "Mickael"
root.manager.transition.direction = "right"
Could you help me ?
Thank you
Romain
In your on_press method:
def on_press(self):
self.ids.recup_infos.text = self.root.get_screen('FirstWindow').ids.my_name.text
self.root.get_screen('FirstWindow').ids.my_name.text isn't the correct way to get access to widgets outside of the class that you are in right now, or in this situation, screen. The correct way is to use the App.get_running_app() method:
self.ids.recup_infos.text = App.get_running_app().root.ids.First.ids.my_name.text
But before doing that, you have to give ids to the screens of your app, so that the First argument of the method demonstrated above actually makes sense:
WindowManager:
FirstWindow:
id: First
# "First" is the id of the FirstWindow class
# which can also explain why there was a "First" arg
# inside "App.get_running_app().root.ids.First.ids.my_name.text"
SecondWindow:
id: Second
ThirdWindow:
id: Third
Still confused to why this works? Let's divide the attributes of App.get_running_app().root.ids.First.ids.my_name.text into 3 parts:
App.get_running_app(): this method returns the location of your running App class, in this case AwesomeApp. This also acts as self if you were to get the variable inside the App object itself
.root.ids.First: if you read the Kivy documentation, or just simply watched Kivy course videos online, carefully, you should know that self.root.ids inside the App object returns a list of ids of the widgets inside your root widget. In this case, App.get_running_app().root.ids is doing the same thing here, and your screens are passed in the ScreenManager root widget, hence make First an available attribute in App.get_running_app().root.ids
.ids.my_name.text: same as above, App.get_running_app().root.ids.First acts the same as self if you were to run it in your FirstWindow class, which gives you the opportunity to get access to variables outside of your working classes/screens
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()
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()
I've been at this for hours now, trying every solution I could find on here and trying random things....
I'm trying to build a layout consisting of 3 buttons at the top, then a scroll-able GridLayout or BoxLayout. I just cant figure out whats wrong... I've read on one response "bind the layout's size to adapt itself:" but I'm using screenmanagement and cant figure out how to do that with my code setup
<HomeScreen>:
BoxLayout:
orientation: "vertical"
BoxLayout:
size_hint: 1,.1
orientation: "horizontal"
Button:
text:"1"
Button:
text:"2"
Button:
text:"3"
ScrollView:
GridLayout:
orientation: "vertical"
size_hint_y: None
row_default_height: 60
cols:1
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Your code is correct, you just need to specify the height of the GridLayout. You can use height: self.minimum_height.
Reproducible example:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
kv_text = '''
<MyScreenManager>:
HomeScreen:
<HomeScreen>:
BoxLayout:
orientation: "vertical"
BoxLayout:
size_hint: 1,.1
orientation: "horizontal"
Button:
text:"1"
Button:
text:"2"
Button:
text:"3"
ScrollView:
GridLayout:
orientation: "vertical"
size_hint_y: None
height: self.minimum_height #<<<<<<<<<<<<<<<<<<<<
row_default_height: 60
cols:1
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
'''
class MyScreenManager(ScreenManager):
pass
class HomeScreen(Screen):
pass
class MyApp(App):
def build(self):
return HomeScreen()
def main():
Builder.load_string(kv_text)
app = MyApp()
app.run()
if __name__ == '__main__':
main()
Output:
I tried like #Ishinomori to recreate the app of #FJSevilla in pure Python3.7.
After a few changes I have done it!
# -*- coding: utf-8 -*-
# Kivy
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.button import Button
from kivy.core.window import Window
class HomeScreen(BoxLayout):
def __init__(self, **kwargs):
# Initiate Box Layout and change orientation to vertical
super().__init__(**kwargs)
self.orientation = "vertical"
# Top Bar with Buttons "1", "2" & "3"
self.top_bar = BoxLayout(orientation="horizontal", size_hint=(1, .1))
self.top_bar.add_widget(Button(text="1"))
self.top_bar.add_widget(Button(text="2"))
self.top_bar.add_widget(Button(text="3"))
# Create the Gridlayout for the Scroll View and add height bounding
self.contend_scroll_view = GridLayout(size_hint_y=None, row_default_height=60, cols=1)
self.contend_scroll_view.bind(minimum_height=self.contend_scroll_view.setter('height'))
# 30 Dummy Buttons (real data here!)
for _ in range(30):
self.contend_scroll_view.add_widget(Button())
# Add the contend to the Scroll View
self.scroll_view = ScrollView()
self.scroll_view.add_widget(self.contend_scroll_view)
# Add the two Widgets to Home Screen
self.add_widget(self.top_bar)
self.add_widget(self.scroll_view)
class MyApp(App):
def build(self):
return HomeScreen()
if __name__ == '__main__':
# Only runs if file is executed directly, but not if importet
MyApp().run()
I want to create a simple app in Kivy, but I want to split the main display in two sections. The top part will have some buttons and it will stay fixed. However, in the bottom part I want to switch between different screens. Is this possible?
Here is a simplified version:
<home screen>:
BoxLayout:
Button:
Screen:
(Switch between Screen 1, and 2)
<screen 1>
<screen 2>
I would really appreciate some help. Thank you!
Ok, here is my non working example.
.py File:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
class HomeScreen(Screen):
pass
class Screen1(Screen):
pass
class Screen2(Screen):
pass
class SplitScreenApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(HomeScreen(name='home_screen'))
sm.add_widget(Screen1(name='screen_one'))
sm.add_widget(Screen2(name='screen_two'))
return sm
if __name__ == "__main__":
SplitScreenApp().run()
.kv File
<HomeScreen>:
BoxLayout:
orientation: 'vertical'
rows: 3
ScreenManager:
size_hint_y: 0.8
Button:
size_hint_y: 0.1
on_press:
root.manager.transition.direction = 'left'
root.manager.current = 'screen_one'
Button:
size_hint_y: 0.1
on_press:
root.manager.transition.direction = 'left'
root.manager.current = 'screen_two'
<Screen1>:
BoxLayout:
orientation: 'vertical'
rows: 1
Button:
text: "Screen 1"
<Screen2>:
BoxLayout:
orientation: 'vertical'
rows: 1
Button:
text: "Screen 2"
So I expect to see "Screen 1" show first on top of the two Buttons. Then, I can press one of the two in "HomeScreen" and it will switch between "Screen 1" and "Screen 2" on press.
Certainly, just replace Screen with ScreenManager in your example and add whatever Screens you like to it.
If you have a specific problem, post an example with real code demonstrating what you think should work and how it fails.
You lack a reference to the screenmanager object, and your homescreen should be just another screen under the screenmanager, with the buttons on a separate Layout.
Have you checked out the examples which come with kivy? The 'showcase' one does pretty much exactly what you want.
I ask this a while back - and based on the feedback I received here I got the following code working for me. Perhaps, this will help someone else.
Python File:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
class HomeScreen(BoxLayout):
pass
class Screen1(Screen):
pass
class SplitScreenApp(App):
def build(self):
return HomeScreen()
if __name__ == "__main__":
SplitScreenApp().run()
Kivy Code:
<HomeScreen>:
name: 'ScreenManager'
BoxLayout:
orientation: 'vertical'
rows: 3
ScreenManager:
id: sm
size_hint_y: 0.8
Screen1:
Screen:
name: "screen_two"
BoxLayout:
orientation: 'vertical'
rows: 1
Button:
text: "Screen 2"
on_release: sm.current = 'screen_one'
Button:
text: "Screen 1"
size_hint_y: 0.1
on_press:
sm.transition.direction = 'left'
sm.current = 'screen_one'
Button:
text: "Screen 2"
size_hint_y: 0.1
on_press:
sm.transition.direction = 'left'
sm.current = 'screen_two'
<Screen1>:
name: "screen_one"
BoxLayout:
orientation: 'vertical'
rows: 1
Button:
text: "Screen 1 Out"
on_release: root.parent.current = 'screen_two'
Thanks again to everybody that is willing to help here.