I am trying to put a given pictures in a gridlayout that can scroll, and when i select the picture the color of the image change, here is my code:
CONTAINER_PNG = os.path.join(AllImage_ROOT, 'images')
IMAGES_NAMES = [c[:-4] for c in os.listdir(CONTAINER_PNG)]
LIST_IM = os.listdir(CONTAINER_PNG)
class ImageButton(ButtonBehavior, Image):
pass
class AllImage(BoxLayout):
# screen_manager = ObjectProperty()
def __init__(self, **kwargs):
BoxLayout.__init__(self, **kwargs)
self.orientation='vertical'
splitter = Splitter(sizable_from = 'bottom')
root = ScrollView()
layout = GridLayout(cols=4, spacing=10)
layout2 = GridLayout(cols=4, spacing=10)
button = ImageButton(source = 'mix.png')
layout2.add_widget(button)
self.add_widget(layout2)
for im in IMAGES_NAMES:
if IMAGES_NAMES != None :
btn = ImageButton(source = im+'.png')
btn.bind(on_press= lambda a:layout.add_widget( ToggleButton(text = 'work') ))
btn.bind(on_press= lambda b:self.background_color(1,1,1))
layout.add_widget(btn)
layout2.add_widget(splitter)
root.add_widget(layout)
self.add_widget(root)
class TryApp(App):
def build(self):
return AllImage()
def on_pause(self):
return True
if __name__ == "__main__":
TryApp().run()
I know i am doing things wrong, so i have several questions :
1- Why when i add a Splitter between my 2 Grids it don't work(the splitter is not visible)
2- How can i change the color of my ImageButton ?
3- The scrollview isn't working on my GridLayout, how can i custom my Grid that can be bigger than my window.
Thank you for your time :)
kivy tries to make things simple by separating the UI from the logic..from the kivy docs,it says You must deactivate at least one of the size_hint instructions (x or y) of the child to enable scrolling..
<AllImage>:
orientation:'vertical'
ScrollView:
do_scroll_x:False
GridLayout:
cols:4
spacing:10
size_hint_y:None
height: 8*dp(80)
for clarity sake,try to implement the UI stuffs in a kv file to make things easier to read.
btn.bind(on_release= lambda a:layout.add_widget( ToggleButton(text = 'work') ))
btn.bind(on_press= lambda b:self.background_color(1,1,1))
i dont think the on_press can handle two methods at the sametime,so try this
Related
I want to display the color name that i found at { closest_colour(requested_colour) } function
in popup window.
In practice application supposed to ask for file from your computer (only specific image types) then ask for mouse input while displaying image that you choose.Finally opens popup window to display color of pixel that you clicked.
.py
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.factory import Factory
from kivy.properties import ObjectProperty, StringProperty
from kivy.uix.popup import Popup
import cv2
import webcolors
class LoadDialog(FloatLayout):
load = ObjectProperty(None)
cancel = ObjectProperty(None)
class Root(FloatLayout):
loadfile = ObjectProperty(None)
def dismiss_popup(self):
self._popup.dismiss()
def show_load(self):
content = LoadDialog(load=self.load, cancel=self.dismiss_popup)
self._popup = Popup(title="Load file", content=content,
size_hint=(0.9, 0.9))
self._popup.open()
def load (self,path, filename):
filename=str(filename)
filename = filename.replace("[", "")
filename = filename.replace("]", "")
filename = filename.replace("\\\\", "\\")
filename = filename.replace("'", "")
print(filename)
img=cv2.imread(filename)
cv2.imshow('image', img)
self.dismiss_popup()
def closest_colour(requested_colour):
min_colours = {}
for key, name in webcolors.CSS3_HEX_TO_NAMES.items():
b_c, g_c, r_c = webcolors.hex_to_rgb(key)
rd = (r_c - requested_colour[0]) ** 2
gd = (g_c - requested_colour[1]) ** 2
bd = (b_c - requested_colour[2]) ** 2
min_colours[(rd + gd + bd)] = name
return min_colours[min(min_colours.keys())]
def click_event(event, x, y, flags, params):
if event == cv2.EVENT_LBUTTONDOWN:
print(x, ' ', y)
requested_colour=img[y,x]
global answer
answer=closest_colour(requested_colour)
print(closest_colour(requested_colour))
print(img[y,x])
cv2.destroyAllWindows()
cv2.setMouseCallback('image', click_event)
cv2.waitKey(0)
return answer
class MyPopup(Popup):
PopUp=ObjectProperty(None)
text = StringProperty(Root.answer)
def MyPopup(self):
content = MyPopup(PopUp=self.PopUp)
self._popup = Popup(title="Color Found!!", content=content, size_hint=(0.2, 0.4))
self._popup.open()
class main(App):
pass
Factory.register('Root', cls=Root)
Factory.register('LoadDialog', cls=LoadDialog)
Factory.register('MyPopup', cls=MyPopup)
if __name__ == '__main__':
main().run()
.kv
#:kivy 1.1.0
#:import Factory kivy.factory.Factory
Root:
FloatLayout:
Button:
id: load
size_hint:(1,.1)
pos_hint:{'x':0, 'y':0}
text: 'Load'
on_release: root.show_load()
background_color :(0, 0, 1, 1)
Image:
id: img
size_hint:(1,.9)
pos_hint:{'x':0, 'y':.1}
source: 'palet.jpg'
size: self.texture_size
keep_ratio: False
allow_stretch: True
<LoadDialog>:
id:load_dialog
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserIconView:
id: filechooser
filters:['*.png','*.jpg','*.jpeg']
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Cancel"
on_release: root.cancel()
Button:
text: "Load"
on_release: root.load(filechooser.path, filechooser.selection)
on_release: Factory.MyPopup().open()
<MyPopup#Popup>
auto_dismiss: False
size_hint:0.4,0.2
pos_hint:{"x":0.3,"y":0.4}
title:"Color Found!!"
BoxLayout:
Label:
text:root.text
font_size:24
Button:
text:"Ok"
width:.5
size_hint:0.2,0.2
pos_hint:{"x":0.2,"y":0}
on_release: root.dismiss()
I tried to create global answer variable but it didnt work out.
Okay, the structure of what you're trying to do is a little confusing, the class LoadDialog(FloatLayout) looks like it's being called just to make the connection between the two classes, is that it?
But from the looks of it, you are trying to pass the text from variable
answer
that is returned when you call the function def load(). if so, try creating a variable eg text_answer = None in class Root()
then, before returning the result, pass the variable's value to the new variable created within the class, in the code snippet
cv2.setMouseCallback('image', click_event)
cv2.waitKey(0)
# add this
self.text_answer = answer
# don't need the return
return answer
later in class MyPopup(Popup), add the variable that was created in the Root() class. But remember that, for object-oriented concepts, you need to use the variable from the instance of class Root() and not directly from the class, and that's the tricky part.
There are many ways to do this:
Pass the Root() class instance to the Popup constructor.
Use an ObjectProperty to use the Root instance.
Use kivy's own tree of objects, doing self.parent.parent.[...] until
finding the instance of Root()
For this case, the easiest way is to use the self.parent method, until you find the Root instance and retrieve the text value, try doing something like this:
class MyPopup(Popup):
PopUp=ObjectProperty(None)
# go looking for the instance of Root in the parentage
text = StringProperty(self.parent.parent.text_answer)
I can't give the answer to your specific problem because I don't have some of the libraries you are using, but I believe this should help with communication between classes.
Always remember to try to reference the instances of the classes, not the classes themselves.
I'm trying to make a horizontal scroll view in Kivy (Python) which would display an image (buttons for now) that you can scroll through. I've been able to figure out how to do the horizontal scroll view, but I just can't seem to figure out what I'm doing wrong to center on the screen. Here is what I have so far:
import kivy
kivy.require('1.8.0')
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
class MyGrid(GridLayout):
def __init__(self, **kwargs):
super(MyGrid, self).__init__(**kwargs)
self.rows = 1
self.spacing=50
self.size_hint=(None, .5)
self.bind(minimum_width=self.setter('width'))
class ScrollViewApp(App):
def build(self):
scroll = ScrollView( size_hint=(1,1), do_scroll_x=True, do_scroll_y=False )
grid = MyGrid()
for i in range(60):
grid.add_widget(Button(text='#00' + str(i),size=(100,100), size_hint=(1,1)))
scroll.add_widget(grid)
return scroll
if __name__ == '__main__':
ScrollViewApp().run()
I looked through the documentation and I think it has something to do with pos_hint: https://kivy.org/doc/stable/api-kivy.uix.widget.html?highlight=pos_hint#kivy.uix.widget.Widget.pos_hint
I tried adding:
self.pos={'center_y':1}
in my MyGrid() class, but nothing is being changed. No matter what value is I put after center_y, my the ScrollView contained in the grid still appears at the top of the screen, rather than centered halfway down. I've also tried adjusting things in the ScrollViewApp() class too, using
scroll.pos={'center_y':1}
as well. I can't seem to figure out what I'm doing wrong. Does anyone have any ideas on what I'm not understanding here? I feel like I'm so close!!!
Edit:
I'm trying to have the buttons centered, but also be able to scroll by dragging the black space around the buttons. In the picture below, you can see there is a bunch of black space beneath the buttons. I can click a button or this black space and scroll horizontally. I just need this to happen with the buttons centered!
The layout is behaving as you told it to.
You have a ScrollView as a root widget, and inside it you put your Grid that has size_hint_y = 0.5.
It takes half of the parent widget height, that takes the full window height.
One way to center your Grid is to put some spacers over and under it, but this is not possible, since ScrollView accepts onle one widget as content.
So, one solution is to..
let the Grid have the full height of the ScrollView
add the ScrollView inside another Layout (e.g. a BoxLayout)
and finally, add two widgets to force the ScrollView in the center.
class MyGrid(GridLayout):
def __init__(self, **kwargs):
super(MyGrid, self).__init__(**kwargs)
self.rows = 1
self.spacing = 50
self.size_hint = (None, 1)
self.bind(minimum_width=self.setter('width'))
class ScrollViewApp(App):
def build(self):
base = BoxLayout(orientation="vertical", size_hint=(1, 1))
base.add_widget(Widget(size_hint_y=.3))
scroll = ScrollView(size_hint=(1, .4), do_scroll_x=True, do_scroll_y=False)
grid = MyGrid()
for i in range(60):
grid.add_widget(
Button(text='#00' + str(i), size=(100, 100), size_hint=(1, 1)))
scroll.add_widget(grid)
base.add_widget(scroll)
base.add_widget(Widget(size_hint_y=.3))
return base
if __name__ == '__main__':
ScrollViewApp().run()
Another approach would be to..
put the ScrollView inside a FloatLayout
and position it using pos_hint
class MyGrid(GridLayout):
def __init__(self, **kwargs):
super(MyGrid, self).__init__(**kwargs)
self.rows = 1
# self.spacing = 50
self.size_hint = (None, 1)
self.bind(minimum_width=self.setter('width'))
class ScrollViewApp(App):
def build(self):
base = FloatLayout(size_hint=(1, 1))
scroll = ScrollView(size_hint=(1, .4), do_scroll_x=True, do_scroll_y=False,
pos_hint={"center_y": .5})
grid = MyGrid()
for i in range(60):
grid.add_widget(
Button(text='#00' + str(i), width=100, size_hint=(None, 1)))
scroll.add_widget(grid)
base.add_widget(scroll)
return base
if __name__ == '__main__':
ScrollViewApp().run()
Edit:
OK. After the update of the question, this is a way to to center and use the black bars to scroll:
Builder.load_string("""
<BaseWidget>:
ScrollView:
do_scroll_y: False
BoxLayout:
orientation: "vertical"
size_hint_x: None
width: self.minimum_width
Widget:
size_hint_y: .3
GridLayout:
id: grid
rows: 1
size_hint_y: .4
size_hint_x: None
width: self.minimum_width
Widget:
size_hint_y: .3
""")
class BaseWidget(FloatLayout):
pass
class ScrollViewApp(App):
def build(self):
base = BaseWidget()
for i in range(60):
base.ids.grid.add_widget(Button(text='#00' + str(i),
width=100, size_hint_x=None))
return base
if __name__ == '__main__':
ScrollViewApp().run()
It uses kv_lng to create the layout because I find it easier to the eye.
For a "Python only" code you can use this:
class MyGrid(GridLayout):
def __init__(self, **kwargs):
super(MyGrid, self).__init__(**kwargs)
self.rows = 1
self.size_hint = None, .4
self.bind(minimum_width=self.setter('width'))
class MyBox(BoxLayout):
def __init__(self, **kwargs):
super(MyBox, self).__init__(**kwargs)
self.orientation = "vertical"
self.size_hint_x = None
self.bind(minimum_width=self.setter('width'))
class ScrollViewApp(App):
def build(self):
base = FloatLayout()
grid = MyGrid()
box = MyBox()
box.add_widget(Widget(size_hint_y=.3))
box.add_widget(grid)
box.add_widget(Widget(size_hint_y=.3))
for i in range(60):
grid.add_widget(Button(text='#00' + str(i), width=100, size_hint_x=None))
scroll = ScrollView(do_scroll_y=False, pos_hint={"center_y": .5})
scroll.add_widget(box)
base.add_widget(scroll)
return base
if __name__ == '__main__':
ScrollViewApp().run()
It seems a lot of people have trouble with the on_press argument with Kivy, but I haven't found answers to my issue...
Here is what's happening:
I'm getting started with my first app in python/kivy. I know python, but perhaps not enough concerning classes... I am able to create a button, with a on_press action that opens a popup.
Now the goal is the following: I have a function affiche_grille, which displays a grid with lines on the screen. Inside each square, I create a button, with a text (a number). This works. I would like to bind an on_press event on these buttons : but now, the syntax does not work any more... Maybe is it because the button is created in a "with self.canvas" instruction, and the self.function is not appropriate any more ?
Here is the (complete after edit) code :
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.popup import Popup
from kivy.graphics import Color, Line
from kivy.core.window import Window
from kivy.core.text.text_layout import layout_text
from kivy.uix.floatlayout import FloatLayout
import numpy as np
from functools import partial # for on_press syntax
class Ecran_Principal(BoxLayout):
def build(self):
self.orientation='vertical'
self.liste_txt = np.zeros([9,9], dtype=object) # will contain Label
self.grille_affichee = np.zeros([9,9])
self.lGrille() # layout for the grid
self.lMenu() # layout for the menu
def lGrille(self):
LayGrille = GridLayout(cols=1,size_hint_y=0.8)
# window dimensions, in pixels
(L, H) = Window.size
H = int(0.8*H) # because of the 20% menu
if L > H: # landscape format - computer case
self.ymin = int(0.25*H) + int(0.05*H)
self.delta = int(0.1*H)
self.xmin = int((L-9*self.delta)/2.)
else: # portrait format - phone case
self.xmin = int(0.05*L)
self.delta = int(0.1*L)
self.ymin = int(0.25*H) + int((H-9*self.delta)/2.)
# end dimensions
self.deltaxrel = self.delta/H
self.deltayrel = self.delta/L
# grid definition (without numbers)
with self.canvas:
Color(1, 1, 1) # white
# automatic line traces
for i in range(4):
# big vertical lines
ymax = self.ymin+9*self.delta
xligne = self.xmin+i*3*self.delta
Line(points=[xligne, self.ymin, xligne, ymax], width=2)
# big horizontal lines
xmax = self.xmin+9*self.delta
yligne = self.ymin+i*3*self.delta
Line(points=[self.xmin, yligne, xmax, yligne], width=2)
# little intermediary lines
for ipetit in range(3):
if i ==3:
break
xpetit = xligne + ipetit*self.delta
Line(points=[xpetit, self.ymin, xpetit, ymax], width=1)
ypetit = yligne + ipetit*self.delta
Line(points=[self.xmin, ypetit, xmax, ypetit], width=1)
# end little lines
# end for
# grid display :
self.affiche_grille()
self.add_widget(LayGrille)
# end with
def affiche_grille(self):
# I tried to remove this 'with' instruction and does not change anything
with self.canvas:
for i in range(9): # line
for j in range(9): # colomn
valeur = self.grille_affichee[i,j]
val = "{0:.0f}".format(valeur)
layout = FloatLayout(size=(self.xmin + (j+0.5)*self.delta,
self.ymin + (8.5-i)*self.delta),
pos_hint=(None, None))
montexte = Button(text=val,
size_hint=(self.deltaxrel,
self.deltayrel),
pos=(self.xmin + (j+0.5)*self.delta,
self.ymin + (8.5-i)*self.delta),
background_color = (0,0.2,0,1),
background_normal = '',
on_press=partial(self.choisir_valeur, i, j)
)
self.liste_txt[i, j] = montexte
# THE BUTTONS AND THE TEXT ARE DISPLAYED,
# BUT NOTHING HAPPENS WHEN YOU PRESS THE BUTTONS
layout.add_widget(self.liste_txt[i, j])
# end j
# end i
# end with
def choisir_valeur(self, i, j):
print("Hello !") # NEVER DISPLAYED :(
#champ = TextInput(text=str(self.grille_affichee[i, j]),
# font_size=30,
# focus=True,
# multiline=False)
champ = Button(text=str(self.grille_affichee[i, j]))
popup = Popup(title='Value in line {} - colomn {}'.format(i, j),
content=champ,
size_hint=(0.5,0.5))
champ.bind(on_press=popup.dismiss)
popup.open()
def lMenu(self):
LayMenu = GridLayout(cols=2, rows=2, size_hint_y=0.2)
# Bouton Résoudre
self.BoutonResoudre=Button(text='Resoudre',size_hint=(0.5,0.1),pos_hint={'left': 0.},background_color=[0.9,0.9,0.9,1])
self.BoutonResoudre.bind(on_press=self.resoudre)
LayMenu.add_widget(self.BoutonResoudre)
# Bouton Remplir
self.BoutonScanner=Button(text='Scanner',size_hint=(0.5,0.1),pos_hint={'left': 0.5},background_color=[0.9,0.9,0.9,1])
self.BoutonScanner.bind(on_press=self.scanner)
LayMenu.add_widget(self.BoutonScanner)
# Bouton Indice
self.BoutonIndice=Button(text='Indice',size_hint=(0.5,0.1),pos_hint={'bottom': 0.},background_color=[0.9,0.9,0.9,1])
self.BoutonIndice.bind(on_press=self.indice)
LayMenu.add_widget(self.BoutonIndice)
# Bouton Quitter
self.BoutonQuitter=Button(text='Quitter',size_hint=(0.5,0.1),background_color=[0.9,0.9,0.9,1])
self.BoutonQuitter.bind(on_press=self.quitter)
LayMenu.add_widget(self.BoutonQuitter)
self.add_widget(LayMenu)
def resoudre(self, instance):
content = Button(text='Resolution du sudoku', font_size=20)
popup = Popup(title='RESOLUTION',content=content, size_hint=(0.5,0.5))
content.bind(on_press=popup.dismiss)
popup.open()
def scanner(self, instance):
content = Button(text='Remplissage auto par photo', font_size=20)
popup = Popup(title='SCAN PHOTO',content=content, size_hint=(0.5,0.5))
content.bind(on_press=popup.dismiss)
popup.open()
def indice(self, instance):
content = Button(text='Affichage d\'un indice', font_size=20)
popup = Popup(title='INDICE',content=content, size_hint=(0.5,0.5))
content.bind(on_press=popup.dismiss)
popup.open()
def quitter(self, instance):
content = Button(text='Au revoir !', font_size=20)
popup = Popup(title='QUITTER',content=content, size_hint=(0.5,0.5))
content.bind(on_press=exit())
popup.open()
class Sudoku(App):
def build(self):
ecran=Ecran_Principal()
ecran.build()
return ecran
if __name__ == '__main__':
Sudoku().run()
Everything is interpreted, but the buttons inside the grid are not working...
I've seen the functools.partial() tip, but it does not seem to be enough...
Does anyone have an idea of what is happening ? I am not very familiar with classes in python and I have certainly missed something.
Thank you in advance, and sorry if the question is too basic.
Well, you now know that you can't add a widget to canvas. Also, you shouldn't have a build method in your Ecran_Principal class. build() only belongs in the Sudoku() App class. Use __init__ instead.
I think if you try separating the visual stuff into kv language things will be easier. Below is an example utilizing spacing and padding with GridLayouts to 'draw' the game board. The buttons are hooked up with a simple callback.
btngrid.py
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.button import Button
class SmallGrid(GridLayout):
pass
class BigGrid(GridLayout):
pass
class GameBoard(AnchorLayout):
# A nice layout to use to keep things centered. Only one widget can be added to this.
def __init__(self, *args, **kwargs):
super(GameBoard, self).__init__(*args, **kwargs)
big = BigGrid()
for i in range(9):
small = SmallGrid()
for j in range(9):
small.add_widget(Button(text="{}, {}".format(i, j), on_release=self.callback))
big.add_widget(small)
self.add_widget(big)
def callback(self, button):
print button.text
class RootWidget(BoxLayout):
def __init__(self, *args, **kwargs):
super(RootWidget, self).__init__(*args, **kwargs)
self.orientation = 'vertical'
self.add_widget(GameBoard())
bottom_btns_container = GridLayout(cols=2, size_hint=(1, .2))
for i in range(4):
# Just for show, they don't do anything
bottom_btns_container.add_widget(Button(text="Button {}".format(i)))
self.add_widget(bottom_btns_container)
class BtnGridApp(App):
def build(self):
return RootWidget()
btngird.kv
<BigGrid>:
cols: 3
size_hint: (None, .8) # scales
width: self.height # scales
spacing: 5 # Width of lines
padding: 5 # perimeter border
# This draws a background for the whole grid.
# When used with spacing and padding, part of the background will show.
# Same with SmallGrid below
canvas.before:
Color:
rgba: [.9, .9, .9, 1]
Rectangle:
pos: self.pos
size: self.size
<SmallGrid>:
cols: 3
size_hint: (None, .8) # scales
width: self.height # scales
spacing: .25
canvas.before:
Color:
rgba: [.6, .6, .6, 1] # White lines
Rectangle:
pos: self.pos
size: self.size
<GameBoard>:
anchor_x: "center"
anchor_y: "center"
I had found a similar topic at stackoverflow but unluckily it didn't help me.
It's the first time I try to seriously program a GUI and I'm really getting mad.
I'm doing one step at a time, towards what I will finally need.
Now I'm trying to add a simple drop down menu in the top left corner of my widget, whose element should call a function whenever they are selected. I really looked for this in kivy documentation, and in this forum but I can't really solve this.
import multiprocessing
from mesh import MeshApp
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
import os
MAINDIR = os.path.dirname(os.path.realpath(__file__))
categories = {}
def getCategories():
for dir in os.walk(MAINDIR):
if len(dir[1]) == 0:
filelist = set()
for mesh in dir[2]:
filelist.add(mesh.replace('_FRONT.png','').replace('_SIDE.png','').replace('_TOP.png',''))
categories.update({dir[0]: filelist})
#class CategoriesList(DropDown):
# pass
class MainWindow(Widget):
def __init__(self):
#self.categorieslist = CategoriesList()
categories_list = DropDown()
for i in categories.keys():
btn = Button(text=i.replace(MAINDIR, ''), size_hint_y=None, height=30)
btn.bind(on_release=lambda btn: categories_list.select(btn.text))
categories_list.add_widget(btn)
mainbutton = Button(text='Choose directory', size_hint=(1, 1))
mainbutton.bind(on_release=categories_list.open)
categories_list.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', x))
#and now???
class RenderApp(App):
def build(self):
self.launchMeshApp()
return MainWindow()
def launchMeshApp(self):
app = MeshApp()
p = multiprocessing.Process(target=app.run)
p.start()
if __name__ == '__main__':
getCategories()
RenderApp().run()
And:
#:kivy 1.9.1
<MainWindow>:
canvas.before:
Color:
rgba: 0.6, 0.6, 1, 1
Rectangle:
pos: self.pos
size: self.size
canvas:
Color:
rgba: 0, 0, 0, 0.5
Rectangle:
pos: 0, self.height * 5 / 6 - 1
size: self.width, 2
I've created the dropdown as seen in the docs and in several other forum. But I need to place it in the top left corner, and I never found, or understood, the way to do this. Moreover I didn't get how to make them call a function with a parameter whenever they are clicked.
Thank you very much
EDIT: I don't know why but the first line with "Hi all" is automatically deleted
I don't know about the dropdown menu, but I can answer the data one.
When you bind a callback, the first argument you receive will be which widget that is bound to the callback. So the idea is to create a class that uses Button as its base class, then you can define whatever extra information you need.
Here is a rough, non tested example based on the button API example:
class MyAwesomeButton(Button):
def __init__(self, **kwargs):
super(MyAwesomeButton, self).__init__(**kwargs)
self.my_data = {} # enter your data here
def callback(instance):
print('The button <%s> is being pressed' % instance.text)
print instance.my_data
btn1 = MyAwesomeButton(text='Hello world 1')
btn1.bind(on_press=callback)
btn2 = MyAwesomeButton(text='Hello world 2')
btn2.bind(on_press=callback)
I can't find the syntax for setting on_press in python code to change the screen anywhere. I keep getting errors for anything like Button(text = 'hi', on_press = self.current = 'start_menu. Here's the code and it works as is.
class LoadMenu(Screen):
def __init__(self, **kwargs):
super(LoadMenu, self).__init__(**kwargs)
Clock.schedule_once(self.update)
def update(self, dt):
L = [x for x in range(len(os.listdir('saves')))]
for x in L:
x = self.add_widget(Button(text = os.listdir('saves')[x]))
I haven't positioned the buttons so they just are on top of each other but I can fix that later. What I need to happen is for each button to change to the play screen on press so that will be the same for each button but I also need each one to load the Shelve file they a referencing.(I know I'll need another function for that) Can I have an on_press trigger two events at once, and again how do I set it in the python code?
Consider the following programme:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.uix.button import Button
from kivy.clock import Clock
from kivy.properties import StringProperty
dirlist = ['aaa', 'bbb', 'ccc', 'ddd']
class MyButton(Button):
prop = StringProperty('')
def on_press(self):
print "Class-defined on_press handler (I'm {})".format(self.text)
def other_on_press_handler(sender):
print "other_on_press_handler, from {}".format(sender.text)
def some_func(text):
print "yeah: " + text
class LoadMenu(Screen):
def __init__(self, **kwargs):
super(LoadMenu, self).__init__(**kwargs)
Clock.schedule_once(self.update)
def on_press_handler(self, sender):
print "on_press_handler, from {}".format(sender.text)
self.parent.current = 'sc2'
def yet_another_on_press_handler(self, sender):
print "yet_another_on_press_handler, from {}".format(sender.text)
self.parent.current = 'sc2'
def update(self, dt):
for x in range(len(dirlist)):
my_b = Button(text = dirlist[x], on_press=self.on_press_handler)
self.parent.ids.button_container.add_widget(my_b)
if x > 1:
my_b.bind(on_press=other_on_press_handler)
if x == 3:
my_b.bind(on_press=lambda sender: some_func("Whoa, lambda was here ({})".format(sender.text)))
for x in range(len(dirlist)):
my_b = MyButton(text = 'my '+ dirlist[x], prop="{} {}".format(dirlist[x], x))
self.parent.ids.button_container.add_widget(my_b)
my_b.bind(on_press=self.yet_another_on_press_handler)
root = Builder.load_string("""
ScreenManager:
LoadMenu:
name: 'sc1'
GridLayout:
cols: 4
id: button_container
Screen:
name: 'sc2'
BoxLayout:
Button:
text: "Go back"
on_press: root.current = 'sc1'
""")
class MyApp(App):
def build(self):
return root
if __name__ == '__main__':
a = MyApp()
a.run()
Let's start by looking at the update method in LoadMenu: In the first loop, a bunch of buttons is generated, and each receives an on_press callback at creation. The last two buttons in the loop get bound to another callback, and the last example shows how to use a lambda expression to generate a callback.
In the second for loop, we instantiate object of class MyButton, a child of Button. Note that we also define an on_press handler in the class definition; this gets called in addition to other functions we may have bound.
But really, this is actually all pretty nicely explained in the kivy Events and Properties docs.