screen update in screen manager - python

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

Related

Referencing Layout from a different class kivy

EDIT: so this is a very simple version of what it is like in my app but i think you get the point. Basically i want to destroy the Buttons created in the for loop with the Button on the destroywidgets screen.
.kv:
MainWindow:
<MainWindow>
FloatLayout:
size_hint: 1, .1
Button:
text:"next screen"
size_hint:.1,1
pos_hint:{"x": 0, "y": 0}
on_release: app.root.current = "destroywidgets"
Button:
text:"laodwidgets"
on_release: root.create_widgets()
size_hint:.1, 1
pos_hint:{"x": .5, "y": 0}
.py:
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.scrollview import ScrollView
class MainWindow(Screen):
name = "mainwindow"
def __init__(self, **kwargs):
super(MainWindow, self).__init__()
self.scrl_view_1 = ScrollView(
size_hint_y=.85,
pos_hint={"x": 0, "y": .15},
do_scroll_x=False,
do_scroll_y=True,
size_hint_x=1
)
self.scrl_child_1 = GridLayout(
size_hint_x=1,
size_hint_y=None,
cols=2,
height=1000,
row_default_height=150,
row_force_default=True
)
self.add_widget(self.scrl_view_1)
self.scrl_view_1.add_widget(self.scrl_child_1)
def create_widgets(self):
print("creating widgets")
for i in range(0, 6):
btn = Button(
text=str(i)
)
self.scrl_child_1.add_widget(btn)
print("added")
class DestroyWidgets(Screen):
name = "destroywidgets"
def __init__(self, **kwargs):
super(DestroyWidgets, self).__init__()
btn_destroy_widgets = Button(
text="Destroy children of Mainwindow",
#some on release function to clear all children from scrl_child_1 in Mainwindow
)
self.add_widget(btn_destroy_widgets)
class ShoppingList(App):
def build(self):
self.sm = ScreenManager()
self.sm.add_widget(MainWindow(name="mainwindow"))
self.sm.add_widget(DestroyWidgets(name="destroywidgets"))
return self.sm
if __name__ == "__main__":
main_app = ShoppingList()
main_app.run()
So I have to reference a GridLayout which is inside of a ScrollView created in class A in class B. Since you cannot give Layouts an ID in python code and reference it with self.ids i can't figure out how to do it. I tried suggestions from another post with the weakref.ref method as example but i couldn't get it to work. The whole point is that i have to destroy all children of Layouts from other classes in a function somehow.
Heres just a little snippet of my code which i think will be enough. If you need more just write me. Thanks for all the help in advance!
class SelfMadePlans(Screen):
name = "selfmadeplans"
def __init__(self, **kwargs):
super(SelfMadePlans, self).__init__()
self.scrl_view_2 = ScrollView(
size_hint_y=.85,
pos_hint={"x": 0, "y": 0},
do_scroll_x=False,
do_scroll_y=True,
size_hint_x=1
)
self.scrl_child_2 = GridLayout(
size_hint_x=1,
size_hint_y=None,
cols=3,
height=20000,
row_default_height=150,
row_force_default=True,
)
self.add_widget(self.scrl_view_2)
self.scrl_view_2.add_widget(self.scrl_child_2)
and then something in another class like:
class B:
def destroy_children(self):
MDApp.get_running_app().sm.get_screen("selfmadeplans").ids.scrl_child_2.children.clear()
First of all you must pass var. no. of kwargs in __init__ in order to use and get all the default functionalities.
...
def __init__(self, **kwargs):
super(MainWindow, self).__init__(**kwargs)
...
etc.
Next to access certain screen from ScreenManager you can use method get_screen as follows,
...
btn_destroy_widgets = Button(
text="Destroy children of Mainwindow",
#some on release function to clear all children from scrl_child_1 in Mainwindow
)
btn_destroy_widgets.bind(on_release = self.destroy_widgets_on_main)
self.add_widget(btn_destroy_widgets)
def destroy_widgets_on_main(self, *args):
main_window = self.manager.get_screen("mainwindow")
main_window.scrl_child_1.clear_widgets()

1.why output is blank?

I am am doing python kivy scroll view program there is no error but it gives me blank output
in main python file:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.stacklayout import StackLayout
from kivy.metrics import dp
class stacklayoutexample(StackLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
for i in range(0,101):
size = dp(100)
b=Button(text=str(i), size_hint=(None,None), size=(size,size))
# self.orientation = "rl-bt"
# self.spacing = ("20dp","20dp")
self.add_widget(b)
class main(App):
pass
main().run()
in main.kv file:
scrollviewexample:
<scrollviewexample#ScrollView>:
stacklayoutex:
size_hint:1,None
<stacklayoutexample>:
When you use kv, you must capitalize all your classes. Also, is stacklayoutex intended to be stacklayoutexample? If so, here is a modified version of your code that I think will work:
main.kv:
Scrollviewexample:
<Scrollviewexample#ScrollView>:
Stacklayoutexample:
size_hint:1,None
<Stacklayoutexample>:
And the python code:
class Stacklayoutexample(StackLayout): # just capitalized
def __init__(self, **kwargs):
super().__init__(**kwargs)
for i in range(0, 101):
size = dp(100)
b = Button(text=str(i), size_hint=(None, None), size=(size, size))
# self.orientation = "rl-bt"
# self.spacing = ("20dp","20dp")
self.add_widget(b)

Problem with Screen Manager in Kivy Python

What I want to do is to switch the screen when the "join" button is pressed.
This is what I have written.
import kivy
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.screenmanager import ScreenManager, Screen
class ConnectingPage(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 2
self.add_widget(Label(text = "Usename:"))
self.username = TextInput(multiline=False)
self.add_widget(self.username)
self.add_widget(Label(text = "Password:"))
self.password = TextInput(multiline=False,password = True)
self.add_widget(self.password)
self.joinbutton = Button(text="Join")
self.joinbutton.bind(on_press = self.click_join_button)
self.add_widget(Label())
self.add_widget(self.joinbutton)
def click_join_button(self, instance):
username = self.username.text
password = self.password.text
info = "you can enter"
MyApp.screen_manager.current = "Info"
# Simple information/error page
class InfoPage(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 1
self.message = Label(text = "welcome",halign="center", valign="middle", font_size=30)
self.add_widget(self.message)
class MyApp(App):
def build(self):
self.screen_manager = ScreenManager()
self.connecting_page = ConnectingPage()
screen = Screen(name='Connect')
screen.add_widget(self.connecting_page)
# Info page
self.info_page = InfoPage()
screen = Screen(name='Info')
screen.add_widget(self.info_page)
return ConnectingPage()
MyApp().run()
Everything works fine in the starting but when the button "join" is pressed, this is the error:
AttributeError: type object 'MyApp' has no attribute 'self'
Please suggest what to do next.
Your build() method returns ConnectingPage(), so your App is not actually using a ScreenManager. All the code in that build() method (except that return) has no effect. Here is a modified version of your MyApp class that actually uses ScreenManager:
class MyApp(App):
screen_manager = ScreenManager() # this make screen_manager a class vaiable
def build(self):
# self.screen_manager = ScreenManager()
self.connecting_page = ConnectingPage()
screen = Screen(name='Connect')
screen.add_widget(self.connecting_page)
self.screen_manager.add_widget(screen) # add screen to ScreenManager
# Info page
self.info_page = InfoPage()
screen = Screen(name='Info')
screen.add_widget(self.info_page)
self.screen_manager.add_widget(screen) # add screen to ScreenManager
# return ConnectingPage()
return self.screen_manager
This allows your code:
MyApp.screen_manager.current = "Info"
to work, but it is an odd construction.
A more typical construction would be something like this:
class MyApp(App):
def build(self):
screen_manager = ScreenManager()
connecting_page = ConnectingPage()
screen = Screen(name='Connect')
screen.add_widget(connecting_page)
screen_manager.add_widget(screen) # add screen to ScreenManager
# Info page
info_page = InfoPage()
screen = Screen(name='Info')
screen.add_widget(info_page)
screen_manager.add_widget(screen) # add screen to ScreenManager
# return ConnectingPage()
return screen_manager # return the ScreenManager
But this requires a change in the code that changes the Screen:
App.get_running_app().root.current = "Info"

Kivy, ScreenManager: how to access a variable defined in another screen?

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

widget won't display until screen size changed

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.

Categories

Resources