How to populate recycleview from input on another 'screen' - python

good day
what im trying to accomplish is to input text on one screen then have it create a button on another screen within a recycleview where if I keep adding buttons the recycleview keeps getting populated. I would assume that the button1 function would update the rvs.rv.data and that the recycleview would use the it update itself. could you point me in the right direction please?
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import Screen, ScreenManager
kv = """
<custombutton#BoxLayout>:
canvas.before:
Color:
rgba: 0.5, 0.5, 0.5, 1
Rectangle:
size: self.size
pos: self.pos
value: ''
Button:
text: root.value
<Root>:
RVScreen:
name: 'Rv'
InputScreen:
name: 'input_screen'
<InputScreen>:
name: "input_screen"
orientation: "vertical"
BoxLayout:
orientation: "vertical"
TextInput:
id: textinput
Button:
text: "hi"
on_press: print(app.rvs.rv.data)
on_press: app.rvs.button1('some_value')
Button:
text: 'rvscreen'
on_press: root.manager.current = 'Rv'
<RVScreen>:
name: 'Rv'
rv: rv
orientation: "vertical"
BoxLayout:
orientation: "vertical"
Button:
text: 'refresh'
on_press: root.rv.refresh_from_data()
on_press: print(app.rvs.rv.data)
Button:
text: "input page"
on_press: root.manager.current = 'input_screen'
RecycleView:
id: rv
viewclass: 'custombutton'
RecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
"""
Builder.load_string(kv)
class Root(ScreenManager):
pass
class RVScreen(Screen):
def __init__(self, **kwargs):
super(RVScreen, self).__init__(**kwargs)
def button1(self, value):
self.rv.data.insert(0, {'value': value or 'default value'})
class InputScreen(Screen):
pass
class TestApp(App):
rvs = RVScreen()
def build(self):
return Root()
if __name__ == '__main__':
TestApp().run()

The problem is that you are populating the data of the app.rvs, but that is created by the line:
rvs = RVScreen()
which creates a new RVScreen instance that is entirely unrelated to the RVScreen that is displayed in your GUI. In order to update the RVScreen in your GUI, you need to get a reference to that RVScreen.
I like to that that through a method in the App, just because it makes the code simpler. So here is a modified version of your code that does it my way:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
kv = """
<custombutton#BoxLayout>:
canvas.before:
Color:
rgba: 0.5, 0.5, 0.5, 1
Rectangle:
size: self.size
pos: self.pos
value: ''
Button:
text: root.value
<Root>:
RVScreen:
name: 'Rv'
InputScreen:
name: 'input_screen'
<InputScreen>:
name: "input_screen"
orientation: "vertical"
BoxLayout:
orientation: "vertical"
TextInput:
id: textinput
Button:
text: "hi"
on_press: app.add_data(textinput.text)
Button:
text: 'rvscreen'
on_press: root.manager.current = 'Rv'
<RVScreen>:
name: 'Rv'
rv: rv
orientation: "vertical"
BoxLayout:
orientation: "vertical"
Button:
text: 'refresh'
on_press: rv.refresh_from_data()
Button:
text: "input page"
on_press: root.manager.current = 'input_screen'
RecycleView:
id: rv
viewclass: 'custombutton'
RecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
"""
Builder.load_string(kv)
class Root(ScreenManager):
pass
class RVScreen(Screen):
pass
class InputScreen(Screen):
pass
class TestApp(App):
# rvs = RVScreen() # this code does nothing useful
def build(self):
return Root()
def add_data(self, value):
# get the `Screen` instance
rvs = self.root.get_screen('Rv')
# insert the new value into the data
rvs.ids.rv.data.insert(0, {'value': value or 'default value'})
if __name__ == '__main__':
TestApp().run()
I also made a small modification, so that the new value comes from the TextInput.

Related

Add widget dynamically from outside class (error)

I´m learning kivy, and it has passed 3 weeks since i face this problem without encounter a solution, so i hope any of u guys could help me, i would appreciate it.
I have a main file:
from app import MyProgramApp
if __name__ == "__main__":
winapp = MyProgramApp()
winapp.run()
from where i start my app. Then i have a directory called "app", inside there is the following "init.py" file.
from kivy.app import App
from kivy.utils import QueryDict, rgba
from kivy.core.window import Window
from .view import MainWindow
Window.minimum_width = 500
Window.minimum_height = 650
Window.maximize()
class MyProgramApp(App):
colors = QueryDict()
colors.primary = rgba('#2D9CDB')
colors.secondary = rgba('#16213E')
colors.succes = rgba('#1FC98E')
colors.warning = rgba('#F2C94C')
colors.danger = rgba('#E85757')
colors.grey_dark = rgba('#C4C4C4')
colors.grey_light = rgba('#F5F5F5')
colors.black = rgba('#A1A1A1')
colors.white = rgba('#FFFFFF')
def build(self):
return MainWindow()
Same folder app, i have the following "view.py".
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.behaviors import ToggleButtonBehavior
from kivy.properties import StringProperty
from kivy.uix.screenmanager import ScreenManager
class MainWindow(BoxLayout):
username = StringProperty("Usuario")
def __init__(self, **kw):
super().__init__(**kw)
def on_press_home(self):
self.ids.scrn_mngr.current = "scrn_home"
class ViewManager(ScreenManager):
def __init__(self, **kw):
super().__init__(**kw)
class NavTab(ToggleButtonBehavior, BoxLayout):
text = StringProperty('')
icon = StringProperty('')
def __init__(self, **kw):
super().__init__(**kw)
and finally for that folder i have "myprogram.kv"
#:kivy 2.1.0
#:import Home views.home.Home
<MainWindow>:
spacing: dp(8)
canvas.before:
Color:
rgba: app.colors.white
Rectangle:
pos: self.pos
size: self.size
# NAVIGATION BAR
BoxLayout:
id: nav_menu
size_hint_x: .2
orientation: "vertical"
size_hint_min_x: dp(100)
# LOGO
BoxLayout:
id: logo_nbox
size_hint_y: .1
size_hint_min_y: dp(70)
padding: dp(16)
AnchorLayout:
anchor_x: "right"
size_hint_x: None
width: dp(52)
halign: "left"
Label:
text: "COMPANY"
halign: "center"
BoxLayout:
orientation: "vertical"
Label:
text: "COMPANY"
halign: "center"
Label:
text: "Phrase"
halign: "center"
# OPTIONS
GridLayout:
id: tabs_box
cols: 1
spacing: dp(4)
size_hint_y: .5
canvas.before:
Color:
rgba: app.colors.grey_dark
Rectangle:
pos: self.pos
size: [self.size[0], dp(1)]
NavTab:
text: "Home"
state: "down"
on_press: root.on_press_home()
# BODY
BoxLayout:
size_hint_x: .8
spacing: dp(8)
orientation: "vertical"
padding: [dp(16), dp(8), dp(12), dp(8)]
canvas.before:
Color:
rgba: app.colors.grey_light
Rectangle:
pos: self.pos
size: self.size
# SCREENS
BoxLayout:
ViewManager:
id: scrn_mngr
<ViewManager>:
Screen:
name: "scrn_home"
Home:
id: home
<NavTab>:
background_normal: ""
background_down: ""
background_color: [0,0,0,0]
group: "tabs"
size_hint_y: None
height: dp(45)
spacing: dp(4)
canvas.before:
Color:
rgba: [0,0,0,0] if self.state == "normal" else rgba("#E1F1FF")
Rectangle:
pos: self.pos
size: self.size
Color:
rgba: [0,0,0,0] if self.state == "normal" else app.colors.primary
Rectangle:
pos: [self.pos[0]+self.size[0]-dp(1), self.pos[1]]
size: [dp(8), self.size[1]]
Label:
halign: "left"
valign: "middle"
text: root.text
color: app.colors.grey_dark if root.state == "normal" else app.colors.primary
Then i got another folder called "views" inside i have another folder called "home", inside home we encounter 3 files "init.py", "home.kv", "home.py". the first one "init.py" is the following.
from .home import Home
then we got "home.kv".
#:kivy 2.1.0
<Home>:
orientation: "vertical"
Label:
size_hint_y: .1
text: "Other"
Button:
size_hint_y: .1
text: "popup"
on_press: root.open_popup()
BoxLayout:
size_hint_y: .8
id: home_box
<SomePopup>:
title: "SOME TITLE"
title_align: "center"
title_color: app.colors.primary
size_hint: .25, .8
size_hint_min_x: dp(200)
pos_hint: {"x": .1, "top": .9}
BoxLayout:
orientation: "vertical"
padding: [dp(0), dp(12), dp(0), dp(12)]
Label:
text: "Some text"
Button:
text: "create buttons on box"
on_press: root.modify_home_box()
and finally and the problem that i´m facing is whit the following file "home.py"
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.factory import Factory
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.app import App
Builder.load_file('views/home/home.kv')
class Home(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def open_popup(self):
Factory.SomePopup().open()
class SomePopup(Popup):
def __init__(self, **kw):
super().__init__(**kw)
def modify_home_box(self):
my_app = App.get_running_app().root
my_box = my_app.ids.home.ids.home_box
custom_button = Button(
text = "something"
)
my_box.add_widget(custom_button)
That i´m trying to do is actually modify "home_box" with is store on ids Home dictionary, i also try with ObjectProperty (but that gives and Attribute Error, since i only could read propertys but doesnt modify it), instance of a new Home class doesnt work... searching for current app in App appear doesnt work since Home is store i think on screenManager...
I need add a button or some widget to "home_box" from outside class "SomePopup". I also drop here a repository on github with the whole code. github_repo
I don't know how to solve my issue, and i try with the resources available here on stack as well other net places... any help would be appreciate.
Just a very complicated path to the widget of interest. In your modify_home_box() method, try replacing:
my_app = App.get_running_app().root
my_box = my_app.ids.home.ids.home_box
with:
my_app = App.get_running_app().root
my_box = my_app.ids.scrn_mngr.ids.home.ids.home_box

Kivy Nested ScreenManager inside BoxLayout

Something seen with a different flavor each week, Here we go again with more ScreenManager shenanigans!
Screens won't change unless buttons are part of the screen itself, I wanted a universal navbar along the top and then a "display" under it. Both screens work, the buttons to switch between them don't.
(Bonus points if you can tell me how to make each screen its own KV file and still link with the screenmanager)
anyways: CODE
QCManager.py
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition
kivy.require('1.9.1')
class MOTD(Screen):
print("MOTD Screen!")
pass
class Search(Screen):
print("Search Screen!")
pass
class ScreenManagement(ScreenManager):
pass
class ClassAllScreen(BoxLayout):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.manager = ScreenManagement()
class ClassApp(App):
def build(self):
self.root = ClassAllScreen()
return self.root
if __name__ == '__main__':
Builder.load_file('./kvfiles/main.kv')
ClassApp().run()
main.kv
#: import NoTransition kivy.uix.screenmanager.NoTransition
<MOTD>:
name: 'motd'
BoxLayout:
orientation:'vertical'
padding:20
spacing:10
Label:
text:"The Cake Is a Lie"
<Search>:
name: 'search'
BoxLayout:
orientation:'vertical'
padding:20
spacing:10
GridLayout:
spacing:10
cols:2
Button:
text:'Left'
Button:
text:'Right'
Button:
text:'bottom'
<ScreenManagement>:
transition: NoTransition()
MOTD:
Search:
<ClassAllScreen>:
orientation:'vertical'
BoxLayout:
size_hint_y: None
height: 60
spacing: 5
padding: 5
canvas:
Color:
rgba: .1,.1,.1,1
Rectangle:
pos: self.pos
size: self.size
Button:
text:'Menu'
size_hint_x: None
width: 120
on_release: root.manager.current = 'motd'
Button:
text:'Search'
size_hint_x: None
width: 120
on_release: root.manager.current = 'search'
Button:
text:'Add to DB'
size_hint_x: None
width: 120
on_press: print("Button Working")
ScreenManagement:
You __init__() method in the ClassAllScreen class is creating an instance of ScreenManagement. That instance is saved as self.manager, but is not added to your GUI.
In your kv file, the line:
ScreenManagement:
is creating another, different instance of ScreenManagement. This instance of ScreenManagement is the one that is in your GUI.
So anything your do to self.manager in the ClassAllScreen will have no effect on the ScreenManagement that is in your GUI.
The fix is to make sure you are referencing the correct instance of ScreenManagement (and not bother to create any other instances). To do this you can add an ObjectProperty to ClassAllScreen in the kv file, like this:
<ClassAllScreen>:
manager: scr_manager # added ObjectProperty that references the scr_manager id
orientation:'vertical'
BoxLayout:
size_hint_y: None
height: 60
spacing: 5
padding: 5
canvas:
Color:
rgba: .1,.1,.1,1
Rectangle:
pos: self.pos
size: self.size
Button:
text:'Menu'
size_hint_x: None
width: 120
on_release: root.manager.current = 'motd'
Button:
text:'Search'
size_hint_x: None
width: 120
on_release: root.manager.current = 'search'
Button:
text:'Add to DB'
size_hint_x: None
width: 120
on_press: print("Button Working")
ScreenManagement:
id: scr_manager # new id to enable reference to this ScreenManagement instance
And the ClassAllScreen class can then be simplified to:
class ClassAllScreen(BoxLayout):
pass
Just to help out anyone trying to do the same. The fixed code is below
App.py
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition
kivy.require('1.9.1')
class MOTD(Screen):
print("MOTD Screen!")
pass
class Search(Screen):
print("Search Screen!")
pass
# class ScreenManagement(ScreenManager):
# pass # Removed, Instanced in kv
class ClassAllScreen(BoxLayout):
pass # Removed code, Done in kv
class ClassApp(App):
def build(self):
self.root = ClassAllScreen()
return self.root
if __name__ == '__main__':
Builder.load_file('./kvfiles/main.kv')
ClassApp().run()
main.kv
#: import NoTransition kivy.uix.screenmanager.NoTransition
<MOTD>:
name: 'motd'
BoxLayout:
orientation:'vertical'
padding:20
spacing:10
Label:
text:"The Cake Is a Lie"
<Search>:
name: 'search'
BoxLayout:
orientation:'vertical'
padding:20
spacing:10
GridLayout:
spacing:10
cols:2
Button:
text:'Left'
Button:
text:'Right'
Button:
text:'bottom'
<ClassAllScreen>:
manager:scr_manager
orientation:'vertical'
BoxLayout:
size_hint_y: None
height: 60
spacing: 5
padding: 5
canvas:
Color:
rgba: .1,.1,.1,1
Rectangle:
pos: self.pos
size: self.size
Button:
text:'Menu'
size_hint_x: None
width: 120
on_release: root.manager.current = 'motd'
Button:
text:'Search'
size_hint_x: None
width: 120
on_release: root.manager.current = 'search'
Button:
text:'Add to DB'
size_hint_x: None
width: 120
on_press: print("Button Working")
ScreenManager:
transition: NoTransition()
id: scr_manager
MOTD:
Search:

Pass input value to dialog and send to screen

I have the following code where I would like to be able to click on the navigation drawer, select Status under Users, input the name and then be sent to a given screen with the current_user now populated.
main.py
from kivy.uix.behaviors import CoverBehavior
from kivy import utils
from kivy.properties import ObjectProperty, ListProperty, NumericProperty, StringProperty, BooleanProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivymd.theming import ThemableBehavior
from kivymd.uix.button import MDFlatButton
from kivymd.uix.dialog import MDDialog
from kivymd.uix.list import MDList
#from models import ServerInfo
from navigation_drawer import navigation_helper
Window.fullscreen = 'auto'
class ItemWidget(BoxLayout):
sid = NumericProperty()
name = StringProperty()
work = NumericProperty()
is_disabled = BooleanProperty()
description = StringProperty()
has_issue = BooleanProperty()
class MainWidget(FloatLayout):
recycleView = ObjectProperty()
items = ListProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.items = [
#ServerInfo(1, "server1", 95, "desc", is_disabled=False, has_issue=False),
]
# Put the servers with issues on top, otherwise sort by name
self.items = sorted((s for s in self.items), key=lambda x: (not(x.has_issue),x.name))
# Load the server information when the recycleView is called
def on_recycleView(self, widget, parent):
self.recycleView.data = [
m.get_dictionary() for m in self.items
]
class Content(BoxLayout):
pass
class DemoApp(MDApp):
class ContentNavigationDrawer(BoxLayout):
pass
class DrawerList(ThemableBehavior, MDList):
pass
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.dialog = None
# set main interface
self.manager = ScreenManager()
self.current_user = ""
# return main interface
#return self.manager
def build(self):
screen = Builder.load_string(navigation_helper)
return screen
def on_start(self):
pass
def dialog_close(self, *args):
self.dialog.dismiss(force=True)
def set_current_user(self,user,sm):
self.current_user = user
# Send over to given screen
# ???? = sm
def show_user_input_dialog(self,user,screen):
#print(sm.current)
if not self.dialog:
self.dialog = MDDialog(
title="Input Username:",
type="custom",
content_cls=Content(),
buttons=[
MDFlatButton(
text="CANCEL",
theme_text_color="Custom",
text_color=self.theme_cls.primary_color,
on_release=self.dialog_close
),
MDFlatButton(
text="OK",
theme_text_color="Custom",
text_color=self.theme_cls.primary_color,
#on_release=self.set_current_user(user,screen)
on_release=self.dialog_close
),
],
)
# Send to user management page with current user now populated
# Fails as sm is a str instead of ScreenManager()
#sm.current = 'user_management'
self.dialog.open()
DemoApp().run()
navigation_drawer.py
navigation_helper = """
Screen:
MDNavigationLayout:
ScreenManager:
id: screenManager
Screen:
name: "monitor"
BoxLayout:
orientation: 'vertical'
MDToolbar:
title: 'Administration'
left_action_items: [["menu", lambda x: nav_drawer.set_state('toggle')]]
elevation:5
MainWidget:
Screen:
name: "user_management"
BoxLayout:
orientation: 'vertical'
MDToolbar:
title: 'Administration'
left_action_items: [["menu", lambda x: nav_drawer.set_state('toggle')]]
elevation:5
UserManagement:
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
orientation: 'vertical'
padding: "2dp"
spacing: "2dp"
ScrollView:
DrawerList:
id: md_list
MDList:
TwoLineIconListItem:
text: "Monitor"
secondary_text: "Monitor servers"
on_release: screenManager.current = 'monitor'
IconLeftWidget:
icon: "monitor"
MDLabel:
padding: dp(10), dp(0)
text: "Users"
bold: True
font_size: dp(22)
size_hint_y: None
OneLineIconListItem:
text: "Status"
on_release: app.show_user_input_dialog('abc999','user_management')
<Content>
orientation: "vertical"
spacing: "12dp"
size_hint_y: None
height: "60dp"
MDTextField:
id: username_input
hint_text: "Username"
<UserManagement#BoxLayout>:
Label:
text: "Users"
<MainWidget>:
id: mainWidgetId2
recycleView: recycleView
CoverImage:
source: 'images/monitor.png'
# Darken the photo
canvas:
Color:
rgba: 0, 0, 0, .6
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
padding: dp(20)
spacing: dp(10)
BoxLayout:
orientation: "horizontal"
BoxLayout:
canvas.before:
Color:
rgba: 1,1,1,.1
# Use a float layout to round the corners
RoundedRectangle:
pos: self.pos
size: self.size
RecycleView:
id: recycleView
viewclass: 'ItemWidget'
RecycleGridLayout:
cols: 1
default_size: self.parent.width / 5, dp(60)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
width: self.minimum_width
spacing: dp(155), dp(0)
BoxLayout:
orientation: "vertical"
spacing: dp(10)
BoxLayout:
spacing: dp(10)
BoxLayout:
canvas.before:
Color:
rgba: 1,1,1,.1
# Use a float layout to round the corners
RoundedRectangle:
pos: self.pos
size: self.size
Label:
text: "APPS"
BoxLayout:
canvas.before:
Color:
rgba: 1,1,1,.1
# Use a float layout to round the corners
RoundedRectangle:
pos: self.pos
size: self.size
Label:
text: "OTHER"
BoxLayout:
canvas.before:
Color:
rgba: 1,1,1,.1
# Use a float layout to round the corners
RoundedRectangle:
pos: self.pos
size: self.size
Label:
text: "MOUNTS"
BoxLayout:
canvas.before:
Color:
rgba: 1,1,1,.1
# Use a float layout to round the corners
RoundedRectangle:
pos: self.pos
size: self.size
Label:
text: "RECENT ACTIVITY"
<CoverImage#CoverBehavior+Image>:
reference_size: self.texture_size
<ItemWidget>:
BoxLayout:
#size_hint_max_x: dp(360)
size_hint_min_x: dp(150)
orientation: "horizontal"
BoxLayout:
orientation: "horizontal"
Button:
# Use the on_press to print troubleshooting info, otherwise comment out
on_press: print(self.background_color)
padding: dp(10), dp(10)
text_size: self.size
font_size: dp(22) if root.has_issue else dp(18)
background_color: .5,1,1,.6 #utils.get_random_color(alpha=.6)
halign: "left"
valign: "top"
size: 1,1
# Show last 4 of server name
text: str(root.name).upper()[-4:]
bold: True
color: (1,0,0) if root.has_issue else (1,1,1)
Button:
background_color: .5,1,1,.6
text_size: self.size
valign: "center"
halign: "center"
text: str(root.work)
padding: dp(8), dp(8)
bold: True if root.work > 90 else False
color: (1,0,0) if root.work > 90 else (1,1,1) # Red if greater than 90
"""
Questions:
How do I get the value from this so that I can pass the value instead of the hardcoded username of abc999
MDTextField:
id: username_input
How do I send to the given screen (user_management here) from
def set_current_user(self,user,sm):
self.current_user = user
# Send over to given screen
# ???? = sm
You can toggle commenting on the two on_release below to see the issue
MDFlatButton(
text="OK",
theme_text_color="Custom",
text_color=self.theme_cls.primary_color,
#on_release=self.set_current_user(user,screen)
on_release=self.dialog_close
You can do what you want by modifying set_current_user() to:
def set_current_user(self, button):
self.current_user = self.dialog.content_cls.ids.username_input.text
self.dialog_close()
self.root.ids.users.add_widget(Label(text=self.current_user))
sm = self.root.ids.screenManager
sm.current = 'user_management'
The above code requires the addition of an id to the UserManagerment in the kv:
UserManagement:
id: users
This just adds a new Label with the name of the user.

Kivy Python: buttons and classes for multiple screens

I have a problem with my Kivy Python Code. I have 2 screens: 1st is to navigate to the second screen and on the 2nd screen there is a button to add text to a scrollview...navigating is working but it does not add any text to the scrollview...I think I need some help here! AttributeError: 'super' object has no attribute 'getattr'
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.clock import Clock, mainthread
from kivy.core.window import Window
from kivy.uix.scrollview import ScrollView
from kivy.effects.scroll import ScrollEffect
from kivy.lang import Builder
Builder.load_string("""
<MenuScreen>:
name: 'mainmenu'
BoxLayout:
spacing: 1
orientation: "vertical"
Label:
text: "MAIN MENU"
Button:
text: 'Go to Screen 2'
on_release:
root.manager.current = 'screen2'
root.manager.transition.direction = "left"
Button:
text: 'Quit'
on_release: root.manager.current = app.exit_software()
<Screen2>:
name: 'screen2'
BoxLayout:
spacing: 1
orientation: "vertical"
ScrollView:
id: scroll_view
always_overscroll: False
BoxLayout:
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
Label:
id: label
text: "You can add some Text here by pressing the button"
size_hint: None, None
size: self.texture_size
Button:
text: 'Add text!'
size_hint_y: 0.1
on_release: app.add_text()
Button:
text: 'Back to main menu'
size_hint_y: 0.1
on_release:
root.manager.current = 'mainmenu'
root.manager.transition.direction = "right"
""")
# Declare both screens
class MenuScreen(Screen):
pass
class Screen2(Screen):
pass
class AddTextApp(App):
def __init__(self,**kwargs):
super().__init__(**kwargs)
def build(self):
# Create the screen manager
sm = ScreenManager()
sm.add_widget(MenuScreen(name='mainmenu'))
sm.add_widget(Screen2(name='screen2'))
return sm
def add_text(self):
self.root.ids.label.text += f"Some new Text\n"
self.root.ids.scroll_view.scroll_y = 0
def exit_software(self):
App.get_running_app().stop()
if __name__ == '__main__':
AddTextApp().run()
Thank you very much in advance!
The error occurred because self.root.ids gets access to widgets located in the root widget of the main class. To access the secondary screen elements, you need to add it to the main class (in your case, in ScreenManager) and set its id. Also, you have a lot of imported excess, so that it is clearly visible, I advise you to use Pycharm or something like that.
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
kv = """
<MenuScreen>:
name: 'mainmenu'
BoxLayout:
spacing: 1
orientation: "vertical"
Label:
text: "MAIN MENU"
Button:
text: 'Go to Screen 2'
on_release:
root.manager.current = 'screen2'
root.manager.transition.direction = "left"
Button:
text: 'Quit'
on_release: root.manager.current = app.exit_software()
<Screen2>:
name: 'screen2'
BoxLayout:
spacing: 1
orientation: "vertical"
ScrollView:
id: scroll_view
always_overscroll: False
BoxLayout:
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
Label:
id: label
text: "You can add some Text here by pressing the button"
size_hint: None, None
size: self.texture_size
Button:
text: 'Add text!'
size_hint_y: 0.1
on_release: app.add_text()
Button:
text: 'Back to main menu'
size_hint_y: 0.1
on_release:
root.manager.current = 'mainmenu'
root.manager.transition.direction = "right"
ScreenManager:
MenuScreen:
id: menu_scr
Screen2:
id: scr_2
"""
class MenuScreen(Screen):
pass
class Screen2(Screen):
pass
class AddTextApp(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def build(self):
return Builder.load_string(kv)
def add_text(self):
self.root.ids.scr_2.ids.label.text += f"Some new Text\n"
self.root.ids.scr_2.ids.scroll_view.scroll_y = 0
#staticmethod
def exit_software():
App.get_running_app().stop()
if __name__ == '__main__':
AddTextApp().run()

Kivy: changing image source on screen

After much headbanging, I've created an app that opens the FileChooser and picks an image. A function will then transform the image and change it on the screen.
< FirstScreen > is just something to start with, relatively unimportant.
< SecondScreen > is the file chooser. The path may need editing. < ThirdScreen > shoudl show the image along with buttons that will eventually lead elsewhere.
I suspect the problem is that the line
sm.current = "_third_screen_"
is creating a new instance separate to everything else I've passed items too. How do I show the image I selected?
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty, StringProperty
Builder.load_string("""
<MyScreenManager>:
FirstScreen:
SecondScreen:
ThirdScreen:
<FirstScreen>:
name: '_first_screen_'
BoxLayout:
orientation: "horizontal"
Label:
id: first_screen_label
text: "Hi, I'm the home page"
BoxLayout:
orientation: "vertical"
Button:
text: "Okay!"
on_press: root.manager.current = '_second_screen_'
Button:
text: "Cancel!"
on_press: app.stop()
<SecondScreen>:
name: '_second_screen_'
id: file_chooser
BoxLayout:
id: file_chooser_box_layout
orientation: "horizontal"
Button
text: "Open"
on_press:
root.callback_image_and_other_stuff(file_chooser_list_view.selection)
FileChooserListView:
id: file_chooser_list_view
<ThirdScreen>:
name: '_third_screen_'
BoxLayout:
orientation: "vertical"
id: third_screen
Label:
id: main_title
text: "Upload"
size_hint: (1, 0.1)
Image:
id: main_image
source: root.img
size_hint: (1, 0.75)
BoxLayout:
orientation: "horizontal"
padding: 10
size_hint: (1, 0.15)
Button:
text: "Okay"
size_hint: (0.5, 1)
on_press: image_viewer.image_accepted_by_user(filechooser.selection)
Button:
text: "Cancel"
size_hint: (0.5, 1)
on_press: root.manager.current = '_first_screen_'
""")
class FirstScreen(Screen):
pass
class SecondScreen(Screen):
def callback_image_and_other_stuff(self, new_image_address):
# do other stuff here also, then pass new_image_address along
new_image_address = new_image_address[0].replace("\\", "/")
third_screen = ThirdScreen()
third_screen.callback_image(new_image_address)
class ThirdScreen(Screen):
img = ObjectProperty(None)
def __init__(self, **kwargs):
super(Screen, self).__init__(**kwargs)
def callback_image(self, new_image_address):
sm.current = "_third_screen_"
self.img = new_image_address
self.ids.main_image.source = self.img
print(self.img)
# Create the screen manager
sm = ScreenManager() # Problem?
sm.add_widget(FirstScreen(name='_first_screen_'))
sm.add_widget(SecondScreen(name='_second_screen_'))
sm.add_widget(ThirdScreen(name='_third_screen_'))
class MyApp(App):
def build(self):
return sm
if __name__ == '__main__':
MyApp().run()
Every time you use some_obj = SomClass() you are creating a new object, in your case that is what you are doing, you are creating a new ThirdScreen different from the one shown by that you do not observe the image, the solution is that you have to access to the initial object using the ScreenManager and name screen.
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty, StringProperty
Builder.load_string("""
<FirstScreen>:
BoxLayout:
orientation: "horizontal"
Label:
id: first_screen_label
text: "Hi, I'm the home page"
BoxLayout:
orientation: "vertical"
Button:
text: "Okay!"
on_press: root.manager.current = '_second_screen_'
Button:
text: "Cancel!"
on_press: app.stop()
<SecondScreen>:
id: file_chooser
BoxLayout:
id: file_chooser_box_layout
orientation: "horizontal"
Button
text: "Open"
on_press:
root.callback_image_and_other_stuff(file_chooser_list_view.selection)
FileChooserListView:
id: file_chooser_list_view
<ThirdScreen>:
BoxLayout:
orientation: "vertical"
id: third_screen
Label:
id: main_title
text: "Upload"
size_hint: (1, 0.1)
Image:
id: main_image
source: root.img
size_hint: (1, 0.75)
BoxLayout:
orientation: "horizontal"
padding: 10
size_hint: (1, 0.15)
Button:
text: "Okay"
size_hint: (0.5, 1)
on_press: image_viewer.image_accepted_by_user(filechooser.selection)
Button:
text: "Cancel"
size_hint: (0.5, 1)
on_press: root.manager.current = '_first_screen_'
""")
class FirstScreen(Screen):
pass
class SecondScreen(Screen):
def callback_image_and_other_stuff(self, new_image_address):
if new_image_address:
third_screen = self.manager.get_screen("_third_screen_")
# do other stuff here also, then pass new_image_address along
new_image_address = new_image_address[0].replace("\\", "/")
third_screen.callback_image(new_image_address)
class ThirdScreen(Screen):
img = ObjectProperty(None)
def __init__(self, **kwargs):
super(Screen, self).__init__(**kwargs)
def callback_image(self, new_image_address):
sm.current = "_third_screen_"
self.img = new_image_address
self.ids.main_image.source = self.img
print(self.img)
# Create the screen manager
sm = ScreenManager() # Problem?
sm.add_widget(FirstScreen(name='_first_screen_'))
sm.add_widget(SecondScreen(name='_second_screen_'))
sm.add_widget(ThirdScreen(name='_third_screen_'))
class MyApp(App):
def build(self):
return sm
if __name__ == '__main__':
MyApp().run()

Categories

Resources