1.why output is blank? - python

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)

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

How to stack multi-line labels vertically in BoxLayout

I am trying to create a stack of multiline Labels on Kivy inside a BoxLayout. How do I get the BoxLayout to expand according to its contents? Now the BoxLayout is squeezing the Labels instead.
I do not wish to hardcode the multiline Label size as I want each to be flexible to accommodate varying lines of text.
My demo code is as follows:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.graphics import Color, Rectangle
class MyApp(App):
def build(self):
self.root = GridLayout(rows=1)
self.root.add_widget(Scroller())
return self.root
class Scroller(ScrollView):
def __init__(self):
ScrollView.__init__(self)
self.view = GridLayout(cols=1, size_hint=(1, None))
self.add_widget(self.view)
self.view.bind(minimum_height=self.view.setter("height"))
for i in range(20):
self.view.add_widget(MyWidget(i % 2 is 1))
class MyWidget(BoxLayout):
def __init__(self, odd, **kwargs):
super().__init__(**kwargs)
self.orientation = "vertical"
self.size_hint = (1, None)
self.odd = odd
for i in range(3):
ll = Label(text=f"I am line\nNumber {i + 1}")
self.add_widget(ll)
self.bind(pos=self.format_background_color)
self.bind(size=self.format_background_color)
def format_background_color(self, *args):
with self.canvas.before:
if self.odd:
Color(0.0, 0.0, 0.2, mode="rgb")
else:
Color(0.0, 0.0, 0.8, mode="rgb")
Rectangle(pos=self.pos, size=self.size)
MyApp().run()
If the Labels you added to MyWidget have fixed amount of text you can just set a specific value for MyWidget's height as self.height = "150dp" (Adjust to your need).
Otherwise if you want the MyWidget's height enough to contain its children (here, Labels) then set it to its minimum_height. Also you have to set each Label's height to its texture height.
Thus your modified MyWidget class should now look like,
class MyWidget(BoxLayout):
def __init__(self, odd, **kwargs):
super().__init__(**kwargs)
self.orientation = "vertical"
self.size_hint_y = None
self.odd = odd
self.bind(
pos = self.format_background_color,
size = self.format_background_color,
minimum_height = self.setter("height")
)
for i in range(3):
ll = Label(text=f"I am line\nNumber {i + 1}")
ll.bind(texture_size=self.resize_label)
self.add_widget(ll)
def resize_label(self, instance, value):
instance.size_hint_y = None
instance.height = value[1]
...
You can also utilize padding and spacing as,
class MyWidget(BoxLayout):
def __init__(self, odd, **kwargs):
super().__init__(**kwargs)
self.orientation = "vertical"
self.size_hint_y = None
self.padding = "10dp"
self.spacing = "10dp"
...

Cannot change ByteFitImage data after it is defined

I have a music player app that reads the cover of songs and displays them on the screen. For this I created my own class ByteFitImage for the image to read bytes and for it to adapt to different sizes.
The problem I am encountering is that if I decide to change the data, I get a key error:
KeyError: 'source'
And obviously this line is causing the problem:
self.kv.ids.img.data = BytesIO(second_img.read())
Why is it asking for source? My Code:
from io import BytesIO
from kivy.clock import Clock
from kivy.core.image import Image
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import Rectangle
from kivy.lang import Builder
from kivy.properties import BooleanProperty, ObjectProperty, VariableListProperty, Property
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.app import App
class ByteFitImage(BoxLayout):
data = Property(None)
radius = VariableListProperty([0], length=4)
mipmap = BooleanProperty(False)
_container = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
Clock.schedule_once(self._late_init)
def _late_init(self, *args):
self._container = Container(self.data)
self.bind(data=self._container.setter("source"))
self.add_widget(self._container)
def reload(self):
self._container.image.reload()
class Container(Widget):
# source = ObjectProperty()
image = ObjectProperty()
def __init__(self, source, **kwargs):
super().__init__(**kwargs)
# self.image = AsyncImage(mipmap=mipmap)
self.bind(size=self.adjust_size, pos=self.adjust_size)
self.image = Image(source, ext="jpg")
self.image.bind(on_load=self.adjust_size)
# self.source = source
def on_source(self, instance, value):
if isinstance(value, str):
self.image.source = value
else:
self.image.texture = value
self.adjust_size()
def adjust_size(self, *args):
if not self.parent or not self.image.texture:
return
(par_x, par_y) = self.parent.size
if par_x == 0 or par_y == 0:
with self.canvas:
self.canvas.clear()
return
par_scale = par_x / par_y
(img_x, img_y) = self.image.texture.size
img_scale = img_x / img_y
if par_scale > img_scale:
(img_x_new, img_y_new) = (img_x, img_x / par_scale)
else:
(img_x_new, img_y_new) = (img_y * par_scale, img_y)
crop_pos_x = (img_x - img_x_new) / 2
crop_pos_y = (img_y - img_y_new) / 2
subtexture = self.image.texture.get_region(
crop_pos_x, crop_pos_y, img_x_new, img_y_new
)
with self.canvas:
self.canvas.clear()
Color(1, 1, 1)
Rectangle(texture=subtexture, pos=self.pos, size=(par_x, par_y))
class Example(App):
def __init__(self, **kwargs):
with open("images/song_img.jpg", "rb") as song_img:
self.song_img_bytes = BytesIO(song_img.read())
super(Example, self).__init__(**kwargs)
self.kv = Builder.load_string('''
#:kivy 2.0.0
BoxLayout:
orientation: "vertical"
padding: 10
ByteFitImage:
id: img
data: app.song_img_bytes
Button:
text: "Change cover!"
font_size: 32
on_release: app.change_cover()''')
def build(self):
return self.kv
def change_cover(self):
with open("images/second_img.jpg", "rb") as second_img:
self.kv.ids.img.data = BytesIO(second_img.read())
if __name__ == '__main__':
Example().run()

How can I prevent widgets from overlapping?

How to resolve widget overlapping in kivy framework, I am adding widgets in for cycle so I have more than one widget but all of them comes to be in one place, how can I prevent this?
My python code:
from kivy.app import App
from kivymd.app import MDApp
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.graphics import Color
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivymd.uix.card import MDCard
from kivy.properties import StringProperty
from kivy.lang import Builder
import requests, time, collections
class request_sender():
return 'DATA'
class CustomLabel(Label):
pass
class CustomBox(BoxLayout):
pass
class AuctionCard(Widget):
auc_timer = ''
auc_img = ''
auc_name = ''
def __init__(self, **kwargs):
super(AuctionCard, self).__init__(**kwargs)
with self.canvas.before: Color(1, 0, .4, mode='rgb')
Clock.schedule_once(self.load_info)
def load_info(self, dt):
print(self.auc_name)
self.size_hint = None, None
box = BoxLayout(orientation='vertical', size = (800, 600))
box.size_hint_x = 50;
box.height = 100
AuctionName = CustomLabel(text=self.auc_name, pos_hint={'top': 300.9})
AuctionImage = CustomLabel(text=self.auc_img)
AuctionTimer = CustomLabel(text=self.auc_name)
box.add_widget(AuctionName)
box.add_widget(AuctionTimer)
box.add_widget(AuctionImage)
self.add_widget(box)
class MyWidget(Widget):
prop = StringProperty('')
array_of_labels = []
prop1 = StringProperty('Second card')
n = 0
all_cards = collections.defaultdict()
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
self.screen_load()
def timer_le(self, dt):
returned_data = request_sender().sender_update('ajax.php')
for key in self.all_cards:
for data in returned_data:
if data['pid'] == key:
self.all_cards[key][0].text = str(data['remaining_time'])
def screen_load(self):
returned_data = request_sender().sender_update('ajax.php')
box = GridLayout(cols=2)
self.size_hint = None, None
for data in returned_data:
AucCard = AuctionCard()
AucCard.auc_name = str(data['auc_name'])+'\n\n'
AucCard.auc_timer = str(data['remaining_time'])+'\n\n'
AucCard.auc_img = str(data['auc_img'])+'\n\n'
box.add_widget(AucCard)
print('Widget added')
self.add_widget(box)
#self.all_cards[str(data['pid'])] = [AucCard]
#Clock.schedule_interval(self.timer_le, 1/30)
class TestApp(App):
def build(self):
box = GridLayout(cols=2)
return MyWidget()
TestApp().run()
My kv code:
<CustomLabel>:
size_hint_y: None
text_size: self.width, None
height: self.texture_size[1]
Result:
Kind of result that I want to create:
Actually I found easy way to solve this issue. The problem was in parent class of my AuctionCard and MyWidget classes, I set parent class to Widget but for the AuctionCard it should be BoxLayout and for MyWidget GridLayout. So from there I managed to set cols = 2 and size to window.size. From here it works exactly how it should work.
from kivy.lang import Builder
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.uix.gridlayout import GridLayout
class AuctionCard(BoxLayout):
auc_timer = ''
auc_img = ''
auc_name = ''
def __init__(self, **kwargs):
super(AuctionCard, self).__init__(**kwargs)
Clock.schedule_once(self.load_info)
def load_info(self, dt):
self.orientation = 'vertical'
AuctionName = Label(text=self.auc_name)
AuctionImage = Label(text=self.auc_img)
AuctionTimer = Label(text=self.auc_timer)
self.add_widget(AuctionName)
self.add_widget(AuctionTimer)
self.add_widget(AuctionImage)
class MyWidget(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 2
self.size = (Window.size[0], self.size[1])
self.load_app()
print('MyWidget size: '+str(self.size))
def load_app(self):
self.size_hint = None, None
returned_data = [{'auc_name':'name 1', 'remaining_time':'100', 'auc_img':'https://img.src'},
{'auc_name':'name 2', 'remaining_time':'200', 'auc_img':'https://img.src'},
{'auc_name':'name 3', 'remaining_time':'300', 'auc_img':'https://img.src'}]
for data in returned_data:
AucCard = AuctionCard()
AucCard.auc_name = str(data['auc_name'])+'\n\n'
AucCard.auc_timer = str(data['remaining_time'])+'\n\n'
AucCard.auc_img = str(data['auc_img'])+'\n\n'
self.add_widget(AucCard)
print('Widget added')
class MyTestApp(App):
def __init__(self, **kwargs):
self.title = "My Material Application"
super().__init__(**kwargs)
def build(self):
return MyWidget()
if __name__ == "__main__":
MyTestApp().run()
Result:
Result

screen update in screen manager

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

Categories

Resources