Procedurally generated buttons in Kivy - python

So, I want that my buttons call a function when released, so that my labels come up visible.
As you can see the button's on_release is set to buttonPress where I wanted to do this, but whichever button I press it passes the last label into the function. How can I fix this? Is there a better way to do this?
def buttonPress(self, label):
print("hi")
print(label)
label.height = '50dp'
label.font_size = '20sp'
def on_pre_enter(self, *args):
mainLayout = RelativeLayout()
self.add_widget(mainLayout)
scroll = ScrollView(do_scroll_x = False,pos_hint={"top":0.8,"center_x":0.75})
infoLayout = GridLayout(cols = 1, size_hint = (0.5, None))
mainLayout.add_widget(scroll)
scroll.add_widget(infoLayout)
files = glob.glob("user/*.txt")
InfoWidgetButtonArray = []
InfoWidgetLabelArray = []
for x in range(len(files)):
InfoWidgetLabelArray.append(Label(text="hello mate", size_hint_y=None,height = '0dp', font_size='0sp', color = (0, 0, 0, 1), id=str(x)))
print(InfoWidgetLabelArray[x])
InfoWidgetButtonArray.append(Button(text=files[x][5:][:-4], font_size=14, size_hint=(0.5, None), height=30,on_release=lambda lambdaFunction: self.buttonPress(InfoWidgetLabelArray[x])))
infoLayout.add_widget(InfoWidgetButtonArray[x])
infoLayout.add_widget(InfoWidgetLabelArray[x])
Here is a minimal reproducible example:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.core.window import Window
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.uix.widget import Widget
Window.clearcolor = (1,1,1,1)
class InfoScreen(Screen, Widget):
new = True
def buttonPress(self, label):
print("hi")
print(label)
label.height = '50dp'
label.font_size = '20sp'
def on_pre_enter(self, *args):
mainLayout = RelativeLayout()
self.add_widget(mainLayout)
scroll = ScrollView(do_scroll_x = False,pos_hint={"top":0.8,"center_x":0.75})
infoLayout = GridLayout(cols = 1, size_hint = (0.5, None))
mainLayout.add_widget(scroll)
scroll.add_widget(infoLayout)
InfoWidgetButtonArray = []
InfoWidgetLabelArray = []
for x in range(2):
InfoWidgetLabelArray.append(Label(text="hello mate", size_hint_y=None,height = '0dp', font_size='0sp', color = (0, 0, 0, 1), id=str(x)))
print(InfoWidgetLabelArray[x])
InfoWidgetButtonArray.append(Button(text="name"+str(x), font_size=14, size_hint=(0.5, None), height=30,on_release=lambda lambdaFunction: self.buttonPress(InfoWidgetLabelArray[x])))
infoLayout.add_widget(InfoWidgetButtonArray[x])
infoLayout.add_widget(InfoWidgetLabelArray[x])
SpacingButton = Button(text='Hello world', font_size=14, size_hint=(0.5, None), height=50, color=(0, 0, 0, 0), background_color=(0,0,0,0))
infoLayout.add_widget(SpacingButton)
infoLayout.bind(minimum_height=infoLayout.setter('height'))
sm = ScreenManager()
sm.add_widget(InfoScreen(name='Info'))
class TestApp(App):
def build(self):
return sm
if __name__ == '__main__':
TestApp().run()

I think you're looking for the partial function from the functools module. It binds the input in your for loop and returns the actual function you want it to have.
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.core.window import Window
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.properties import StringProperty
from functools import partial #Changed This
Window.clearcolor = (1,1,1,1)
class InfoScreen(Screen, Widget):
new = True
def buttonPress(self, label, *args):
print("hi")
print(label)
label.height = '50dp'
label.font_size = '20sp'
def on_pre_enter(self, *args):
mainLayout = RelativeLayout()
self.add_widget(mainLayout)
scroll = ScrollView(do_scroll_x = False, pos_hint={"top":0.8,"center_x":0.75}, size_hint_max_y=300) #To enable scrolling be sure to set a maximal value, or have enough buttons to cover the whole window
infoLayout = GridLayout(cols = 1, size_hint = (0.5, None))
mainLayout.add_widget(scroll)
scroll.add_widget(infoLayout)
InfoWidgetButtonArray = []
InfoWidgetLabelArray = []
for x in range(5):
InfoWidgetLabelArray.append(Label(text="hello mate", size_hint_y=None,height = '0dp', font_size='0sp', color = (0, 0, 0, 1), id=str(x)))
print(InfoWidgetLabelArray[x])
InfoWidgetButtonArray.append(Button(text="name"+str(x), font_size=14, size_hint=(0.5, None), height=30, on_release=partial(self.buttonPress, InfoWidgetLabelArray[x]))) #Changed This
infoLayout.add_widget(InfoWidgetButtonArray[x])
infoLayout.add_widget(InfoWidgetLabelArray[x])
SpacingButton = Button(text='Hello world', font_size=14, size_hint=(0.5, None), height=50, color=(0, 0, 0, 0), background_color=(0,0,0,0))
infoLayout.add_widget(SpacingButton)
infoLayout.bind(minimum_height=infoLayout.setter('height'))
sm = ScreenManager()
sm.add_widget(InfoScreen(name='Info'))
class TestApp(App):
def build(self):
return sm
if __name__ == '__main__':
TestApp().run()
Hope this is what you were looking for, I spent some time rereading the question before I hopefully understood.

Related

How to bind a function from main.py in kivyfilechooser.kv?

My application works like this: when you click the 'Ask a Question' button, a random question drops out of the file 1.txt in the Random directory. When you click the 'Add Question' button, it opens filechooser.py the path is Random. I want that when you click on the txt file, it is automatically read and random questions from it fall on the Label. How to do it:?
main.py
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from random import choice
from kivy.config import Config
from kivy.uix.label import Label
import os
Config.set('graphics', 'resizable', 0)
Config.set('graphics', 'width', 500)
Config.set('graphics', 'height', 800)
class MyApp(App):
def build(self):
bl1 = BoxLayout(orientation='vertical', spacing = 8)
self.lbl = Label(
text='Зачет по информатике',
text_size = (450, 500),
font_size = 30,
valign = "center",
halign = 'center' )
bl1.add_widget(self.lbl)
self.btn = Button(
text='Задать вопрос!',
on_press = self.change_text,
text_size = (250, 250),
font_size = 30,
size_hint = (1, .3),
valign = "center",
halign = 'center')
bl1.add_widget(self.btn)
self.dir = Button(
text = 'Добавить вопросы',
on_press = self.addqu,
text_size = (500, 500),
font_size = 30,
size_hint = (1, .1),
valign = "center",
halign = 'center')
bl1.add_widget(self.dir)
self.crbl = Button(
text = 'Выбрать вопросы',
on_press = self.selectqu,
text_size = (500, 500),
font_size = 30,
size_hint = (1, .1),
valign = "center",
halign = 'center')
bl1.add_widget(self.crbl)
return bl1
def change_text(self, instance):
file = open('3.txt', encoding="utf8")
b = file.read().split("\n")
self.lbl.text = (choice(b))
def addqu(self, startfile):
os.system('python kivyfilechooser.py')
def selectqu(self):
pass
if __name__ == "__main__":
MyApp().run()
kivyfilechooser.py
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
class MyWidget (GridLayout):
def selected(self, filename):
try:
self.ids.image.source = filename[0]
except:
pass
class FileChooserWindow(App):
def build(self):
return MyWidget()
if __name__ == "__main__":
window = FileChooserWindow()
window.run()
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
class MyWidget (GridLayout):
def selected(self, filename):
try:
self.ids.image.source = filename[0]
except:
pass
class FileChooserWindow(App):
def build(self):
return MyWidget()
if __name__ == "__main__":
window = FileChooserWindow()
window.run()
filechooserwindow.kv
#:kivy 1.11.0
:
cols:2
id:my_widget
FileChooserListView:
id:filechooser
path: "/Users/MSI GAMING/Desktop/Random"
on_submit: filechooser.selection, touch

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

Kivy TreeView with checkbox and editable text

How to add to Kivy TreeView a checkbox and editable text to each child.
The documentation for the TreeView touches on how to do that, but doesn't provide much detail. Here is a way to do that:
from kivy.app import App
from kivy.metrics import dp
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.checkbox import CheckBox
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.treeview import TreeView, TreeViewNode
class MyNode(BoxLayout):
def __init__(self, **kwargs):
text = kwargs.pop('text', 'None')
super(MyNode, self).__init__(**kwargs)
self.orientation = 'horizontal'
# make height reasonable
self.size_hint_y = None
self.height = dp(25)
# make the parts of the node
self.lbl = Label(text=text, size_hint_x=0.2)
self.chkbx = CheckBox(size_hint_x=0.1, color=(1, 1, 1, 3.5)) # alpha=3.5 to make it more visible
self.txtinpt = TextInput(multiline=False, font_size=15, padding=[6, 3, 6, 0])
# add the parts to the BoxLayout
self.add_widget(self.lbl)
self.add_widget(self.chkbx)
self.add_widget(self.txtinpt)
class MyTreeNode(MyNode, TreeViewNode):
pass
class MyTreeView(TreeView):
def __init__(self):
super(MyTreeView, self).__init__()
self.add_node(MyTreeNode(text='node 1'))
node2 = self.add_node(MyTreeNode(text='node 2'))
self.add_node(MyTreeNode(text='node 3'), node2)
self.add_node(MyTreeNode(text='node 4'), node2)
class TreeViewApp(App):
def build(self):
return MyTreeView()
if __name__ == '__main__':
TreeViewApp().run()

How to show label in certain time using Timer in python kivy?

I'm trying to display some label using kivy framework in specified time with Timer module. Here is what I have and what I want to include into my python kivy:
from threading import Timer
time_var = 2
def time():
print(time_var)
t = Timer(time_var, time)
t.start()
Here is my python kivy code:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivy.config import Config
from threading import Timer
Config.set("graphics", "resizable", 0)
Config.set("graphics", "width", 400)
Config.set("graphics", "height", 500)
time_var = 2
class myLayout(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.t = Timer(time_var, self.timer)
layout = BoxLayout(orientation="vertical",
padding=20,
spacing=10)
btn = Button(text="Click")
btn.bind(on_press=self.t.start())
self.add_widget(layout)
layout.add_widget(btn)
def timer(self, obj):
popup = Popup(content=Label(text="timer"),
size_hint=(None, None),
size=(300, 200))
return popup.open()
class ReminderApp(App):
def build(self):
return myLayout()
if __name__ == "__main__":
ReminderApp().run()
But I cannot figure it out how to make it work.
Maybe there is a way to make it using kivy modules.. Please help me because I am stuck..
I guess I figured it out.. correct me if I am wrong
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivy.config import Config
from threading import Timer
Config.set("graphics", "resizable", 0)
Config.set("graphics", "width", 400)
Config.set("graphics", "height", 500)
time_var = 2
class myLayout(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
layout = BoxLayout(orientation="vertical",
padding=20,
spacing=10)
btn = Button(text="Click")
self.add_widget(layout)
layout.add_widget(btn)
btn.bind(on_press=self.timer)
def popup(self):
popup = Popup(content=Label(text="timer"),
size_hint=(None, None),
size=(300, 200))
popup.open()
def timer(self, obj):
timer = Timer(time_var, self.popup)
timer.start()
class ReminderApp(App):
def build(self):
return myLayout()
if __name__ == "__main__":
ReminderApp().run()

Kivy: chow to obtain coordinates of FloatLayout center

I want to draw circle in center of FloatLayout. With my knowledges I obtained only default values for this. Why circle in showed code isn't red? Can You explain me process for obtaining necessary coordinates, please?
import kivy
from kivy.config import Config
kivy.config.Config.set('graphics','resizable', False)
from kivy.app import App
from kivy.graphics import Color, Ellipse
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
class Scene(FloatLayout):
def __init__(self, **kwargs):
super(Scene, self).__init__(**kwargs)
def draw_circle(self):
with self.canvas:
Color=(1,0,0)
circ = Ellipse(pos = (self.center_x, self.center_y), size=(20,20))
def on_touch_down(self, touch):
pass
class Game(BoxLayout):
def __init__ (self,**kwargs):
super(Game, self).__init__(**kwargs)
self.orientation = 'vertical'
but1 = Button(text = 'button 1')
self.add_widget(but1)
self.scene = Scene()
self.add_widget(self.scene)
class TestApp(App):
def build(self):
game = Game()
game.scene.draw_circle()
return game
if __name__ == '__main__':
TestApp().run()
You should define the size of your float layout when you create it.
self.scene = Scene(size=(300, 300))
Then your circle should be at the center of the FloatLayout dimensions.
I also think FloatLayout is better used with size_hint and pos_hint instead of fixed coordinates.
You can call draw_circle with Clock to make sure the layout is completely initiated first.
Then make sure to create your color like this Color(1, 0, 0). Not Color = ()
from kivy.config import Config
Config.set('graphics','resizable', False)
from kivy.app import App
from kivy.graphics import Color, Ellipse
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.clock import Clock
class Scene(FloatLayout):
def draw_circle(self, dt):
with self.canvas:
Color(1,0,0)
circ = Ellipse(pos = (self.center_x, self.center_y), size=(20,20))
class Game(BoxLayout):
def __init__ (self,**kwargs):
super(Game, self).__init__(**kwargs)
self.orientation = 'vertical'
but1 = Button(text = 'button 1')
self.add_widget(but1)
self.scene = Scene()
self.add_widget(self.scene)
class TestApp(App):
def build(self):
game = Game()
Clock.schedule_once(game.scene.draw_circle) # call draw_circle on next frame
return game
if __name__ == '__main__':
TestApp().run()

Categories

Resources