Hello and thanks for reading. I have trouble about move widget position on screen. Actually i checked widget's position, its changing as i want but at the screen i can't see any changing. I tried this too, in for loop self.remove_widget(i) and then self.add_widget(i). But there is nothing changed too.
.py file :
#!/usr/bin/python
# -*- coding: UTF-8 -*-
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager,Screen
from kivy.uix.widget import Widget
from kivy.graphics import Color,Rectangle
from random import randint
class Page(Screen):
def on_enter(self, *args):
self.list1 = []
self.list2 = []
self.currentball = 0
def add(self):
self.currentball += 1
self.list1.append('Ball[{}]'.format(self.currentball))
self.list2.append(self.list1[len(self.list1)-1])
print('List1 => ',self.list1)
print('List2 => ',self.list2)
#Widget oluşturalım:
self.list2[len(self.list1)-1] = Widget(size_hint=(.1,.1),pos=(randint(100,200),randint(250,500)))
with self.list2[len(self.list1)-1].canvas:
Color(1,1,1,1)
Rectangle(source='worker.png',size=self.list2[len(self.list1)-1].size,pos=self.list2[len(self.list1)-1].pos)
self.add_widget(self.list2[len(self.list1)-1])
def move(self):
for i in self.list2:
i.pos[0] += randint(0,20)
print(i.pos)
def remove(self):
self.update()
class ScMn(ScreenManager):
pass
class test2(App):
def build(self):
return ScMn()
if __name__ == '__main__':
test2().run()
.kv file :
<ScMn>:
Page:
name: 'page'
id: pageid
<Page>:
RelativeLayout:
Button:
size_hint: .1,.1
pos: 0,0
text: 'Add'
on_press: root.add()
Button:
size_hint: .1,.1
pos_hint:{'x':.1,'y':0}
text: 'Move'
on_press: root.move()
Button:
size_hint: .1,.1
pos_hint:{'x':.2,'y':0}
text: 'RemoveLast'
on_press: root.remove()
Thanks for answering already...
Implement a custom widget class, e.g. MyWidget() with method to update pos and size
Replace Widget(...) with MyWidget(...)
Remove with self.list2[len(self.list1)-1].canvas: ... because it is done in the new class MyWidget()
Snippets
class MyWidget(Widget):
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
with self.canvas:
self.rect = Rectangle(pos=self.pos, size=self.size)
self.bind(pos=self.update_rect, size=self.update_rect)
def update_rect(self, *args):
self.rect.pos = self.pos
self.rect.size = self.size
class Page(Screen):
...
def add(self):
...
self.list2[len(self.list1) - 1] = MyWidget(size_hint=(.1, .1), pos=(randint(100, 200), randint(250, 500)))
self.add_widget(self.list2[len(self.list1) - 1])
Related
I need to add a checkbox to this Kivy Canvas but it don"t know how to do it properly. I need the checkbox to display the name of all the file in the folder Data. Then the user check the file he wants and then by pressing a button (here the red one) it return a list of the file name and call some functions. Here is where i am right now.
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.image import Image
from kivy.uix.widget import Widget
from kivy.metrics import dp
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.base import runTouchApp
from kivy.uix.recycleview import RecycleView
from kivy.uix.popup import Popup
from kivy.uix.progressbar import ProgressBar
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
from kivy.uix.button import Button
from kivy.uix.popup import Popup
from kivy.uix.widget import Widget.
from kivy.properties import ObjectProperty
import time
import os
Builder.load_string('''<Game>
ShootButton:
<ShootButton>
Image:
source: 'logo.jpg'
allow_stretch: True
size: 300,300
pos: 300, 400
Image:
source: 'livre.jpg'
allow_stretch: True
size: 350, 350
pos: 490,130
Button:
text: "Modifier Correspondance"
size: 240,50
font_size: 20
pos: 100,300
background_color :0, 1, 0, 1,
on_press: root.shoot()
Button:
text: "Liste Fichiers"
size: 240,50
font_size: 20
pos: 100,400
background_color :0, 1, 0, 1,
on_press: root.fire_popup()
Button:
text: "Ajouter Fichier"
size: 240,50
font_size: 20
pos: 100,200
background_color :0, 1, 0, 1,
on_press: root.shoot2()
Button:
text: "Generer PDF"
size: 180,50
font_size: 20
pos: 580,60
background_color :1, 0, 0, 1,
on_press: root.display()
''')
Window.size = (900, 600)
Window.clearcolor = (1, 1, 1, 1)
class Game(Widget):
pass
class SimplePopup(Popup):
pass
class SimplePopup2(Popup):
pass
class ShootButton(Widget):
def shoot(self):
shooting = Bullet()
shooting.bullet_fly()
def shoot2(self):
shooting = Bullet()
shooting.bullet_fly2()
class Bullet(Widget):
def bullet_fly(self):
print("test")
def bullet_fly2(self):
os.system("open .")
class MyApp(App):
def build(self):
return Game()
if __name__ == '__main__':
MyApp().run()
Ok, it's a lot of things you're asking for.
First we will need to create a layout with the labels as the files name and the checkbox to the corresponding file.
For this I'll use GridLayout, but you can do it as you want. In this layout we will need to get the files list, create the labels with the checkboxes, and, eventually, see what boxes are marked. So we will create a class with three functions and use a dict to link each box to its file:
class Grid(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 2
self.files = self.get_files()
self.check_list = {}
self.create_checks()
def get_files(self):
# Get your files herre
return ['Hi', 'how', 'are', 'you?']
def create_checks(self):
# Create the checkboxes
for item in self.files:
self.add_widget(Label(text=item))
self.check_list[item] = CheckBox()
self.add_widget(self.check_list[item])
def get_actives(self):
# Check who is active (marked)
actives = []
for item in self.check_list:
if self.check_list[item].state == 'down':
actives.append(item)
return actives
Then we will ned to pass this class to our main content to display it for the user. For this I'll use a popup, but again, you can do as you please. So I'll start adding our content to a popup, and creating a function to call it.
class ShootButton(Widget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# Create popup with grid content
self.pop_content = Grid()
self.popup = Popup(title='', separator_height=0,
size_hint=(.8, .6),
content=self.pop_content)
def open_pop(self):
# You guessed! Opens the popup
self.popup.open()
Moslty done, now we just have to check which checkboxes are marked when the button is pressed. So I'll get your function display and use it to call our get_actives function:
def display(self):
actives = self.pop_content.get_actives()
print(actives)
And here is the full code:
from kivy.app import App
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
from kivy.uix.checkbox import CheckBox
from kivy.uix.label import Label
import os
Builder.load_string('''<Game>
ShootButton:
<ShootButton>
Image:
source: 'logo.jpg'
allow_stretch: True
size: 300,300
pos: 300, 400
Image:
source: 'livre.jpg'
allow_stretch: True
size: 350, 350
pos: 490,130
Button:
text: "Modifier Correspondance"
size: 240,50
font_size: 20
pos: 100,300
background_color :0, 1, 0, 1,
on_press: root.shoot()
Button:
text: "Liste Fichiers"
size: 240,50
font_size: 20
pos: 100,400
background_color :0, 1, 0, 1,
on_press: root.fire_popup()
Button:
text: "Ajouter Fichier"
size: 240,50
font_size: 20
pos: 100,200
background_color :0, 1, 0, 1,
on_press: root.shoot2()
Button:
text: "Generer PDF"
size: 180,50
font_size: 20
pos: 580,60
background_color :1, 0, 0, 1,
on_press: root.display()
''')
Window.size = (900, 600)
Window.clearcolor = (1, 1, 1, 1)
class Game(Widget):
pass
class SimplePopup(Popup):
pass
class SimplePopup2(Popup):
pass
class Grid(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 2
self.files = self.get_files()
self.check_list = {}
self.create_checks()
def get_files(self):
# Get your files here
return ['Hi', 'how', 'are', 'you?']
def create_checks(self):
# Create the checkboxes
for item in self.files:
self.add_widget(Label(text=item))
self.check_list[item] = CheckBox()
self.add_widget(self.check_list[item])
def get_actives(self):
# Check who is active (marked)
actives = []
for item in self.check_list:
if self.check_list[item].state == 'down':
actives.append(item)
return actives
class ShootButton(Widget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# Create popup with grid content
self.pop_content = Grid()
self.popup = Popup(title='Choose files', separator_height=0,
size_hint=(.8, .6),
content=self.pop_content)
def shoot(self):
shooting = Bullet()
shooting.bullet_fly()
self.open_pop()
def shoot2(self):
shooting = Bullet()
shooting.bullet_fly2()
def fire_popup(self):
pass
def open_pop(self):
# You guessed! Opens the popup
self.popup.open()
def display(self):
actives = self.pop_content.get_actives()
print(actives)
class Bullet(Widget):
def bullet_fly(self):
print("test")
def bullet_fly2(self):
os.system("open .")
class MyApp(App):
def build(self):
return Game()
if __name__ == '__main__':
MyApp().run()
I want to rotate a button with the pure python code instead of the kvlang.
With kvlang we can rotate a button as shown in the example.
The button is rotated 45 degree around its own center. Below is the original code:
from kivy.app import App
from kivy.lang import Builder
kv = '''
FloatLayout:
Button:
text: 'hello world'
size_hint: None, None
pos_hint: {'center_x': .5, 'center_y': .5}
canvas.before:
PushMatrix
Rotate:
angle: 45
origin: self.center
canvas.after:
PopMatrix
'''
class RotationApp(App):
def build(self):
return Builder.load_string(kv)
RotationApp().run()
But when I try to rewrite this example with the pure python code as below, the button is rotated around somewhere else, as shown here:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.graphics import PushMatrix, PopMatrix, Rotate
class MyButton(Button):
def __init__(self):
super().__init__()
self.text = 'hello world'
self.size_hint = (None, None)
self.pos_hint = {'center_x': .5, 'center_y': .5}
with self.canvas.before:
PushMatrix()
Rotate(origin=self.center, angle=45)
with self.canvas.after:
PopMatrix()
class RotationApp(App):
def __init__(self):
super().__init__()
self.layout = FloatLayout()
self.button = MyButton()
self.layout.add_widget(self.button)
def build(self):
return self.layout
RotationApp().run()
The above two pieces of code are not producing the same result. Is there anything we did wrong?
UPDATE:
The puzzle is solved as suggested by #inclement, solution as below:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.graphics import PushMatrix, PopMatrix, Rotate
class MyButton(Button):
def __init__(self):
super().__init__()
self.text = 'hello world'
self.size_hint = (None, None)
self.pos_hint = {'center_x': .5, 'center_y': .5}
with self.canvas.before:
PushMatrix()
# Rotate(origin=self.center, angle=45) # previous approach
self.rotation = Rotate(origin=self.center, angle=45)
self.bind(center=lambda _, value: setattr(self.rotation, "origin", value))
with self.canvas.after:
PopMatrix()
class RotationApp(App):
def __init__(self):
super().__init__()
self.layout = FloatLayout()
self.button = MyButton()
self.layout.add_widget(self.button)
def build(self):
return self.layout
RotationApp().run()
In your Python code, self.center is evaluated just once during the __init__. In the kv code, a binding is automatically created to reset the Rotate instruction's origin property every time it changes.
You need to put that missing functionality in the Python code, something like self.rotation = Rotate(...) and self.bind(center=lambda instance, value: setattr(self.rotation, "origin", value)) (although I'm sure you can think of a nicer way to set that up, that's just the inline example).
I am using Kivy to create a GUI.
If I go back to the Kivy operation after I activate another window, the Kivy button operation is ignored once.
This is a video of the operation.
I tried the following code
#-*- coding: utf-8 -*-
from kivy.lang import Builder
Builder.load_string("""
<TextWidget>:
BoxLayout:
orientation: 'vertical'
size: root.size
TextInput:
text: root.text
Button:
id: button1
text: "Test"
font_size: 48
on_press: root.buttonClicked()
""")
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import StringProperty
class TextWidget(Widget):
text = StringProperty()
def __init__(self, **kwargs):
super(TextWidget, self).__init__(**kwargs)
self.text = ''
def buttonClicked(self):
self.text += "test\n"
class TestApp(App):
def __init__(self, **kwargs):
super(TestApp, self).__init__(**kwargs)
self.title = 'greeting'
def build(self):
return TextWidget()
if __name__ == '__main__':
TestApp().run()
Is there a solution to this problem?
I am a newbie to Kivy and I want to know how can we create the circular or rounded button using Kivy (with and without .kv file).
There are many ways to create a circular Button. Here is just one way:
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.uix.floatlayout import FloatLayout
class CircularButton(FloatLayout):
button = ObjectProperty()
def __init__(self, **kwargs):
super(CircularButton, self).__init__()
# all size and position attributes get assigned to the CircularButton
# the below is not an exhaustive list
if 'size_hint' in kwargs:
self.size_hint = kwargs.pop('size_hint', (1,1))
if 'size' in kwargs:
self.size = kwargs.pop('size', (100, 100))
if 'width' in kwargs:
self.width = kwargs.pop('width', 100)
if 'height' in kwargs:
self.height = kwargs.pop('height', 100)
if 'pos_hint' in kwargs:
self.pos_hint = kwargs.pop('pos_hint', (None, None))
if 'pos' in kwargs:
self.pos = kwargs.pop('pos', (0,0))
if 'x' in kwargs:
self.x = kwargs.pop('x', 0)
if 'y' in kwargs:
self.y = kwargs.pop('y', 0)
# remaining args get applied to the Button
self.butt_args = kwargs
Clock.schedule_once(self.set_button_attrs)
def set_button_attrs(self, dt):
for k,v in self.butt_args.items():
setattr(self.button, k, v)
Builder.load_string('''
<CircularButton>:
button: button
canvas.before:
StencilPush
Ellipse:
# self.pos and self.size should work here
# adjustments are just hacks to get it to look right
pos: self.x+1, self.y+2
size: self.width-2, self.height-2
StencilUse
canvas.after:
StencilUnUse
Ellipse:
pos: self.x+1, self.y+2
size: self.width-2, self.height-2
StencilPop
Button:
id: button
pos_hint: {'center_x':0.5, 'center_y':0.5}
size_hint: None, None
size: root.size
''')
if __name__ == '__main__':
from kivy.app import App
class TestApp(App):
def build(self):
return CircularButton(text='Circular Button', size_hint=(None, None), size=(150, 150), pos_hint={'right':1, 'top':1}, on_release=self.butt)
def butt(self, *args):
print('button pressed')
TestApp().run()
The issue is a simple one, getting Kivy to integrate the Timer1 code as a label in FloatLayout.
I have this .py file:
import kivy
kivy.require('1.10.0')
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.image import Image
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.stacklayout import StackLayout
from kivy.clock import Clock
from kivy.properties import StringProperty, NumericProperty, ObjectProperty
from digitalclock import DigitalClock
from kivy.animation import Animation
import time
class IntroScreen(Screen):
pass
class ContScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
backbone = Builder.load_file("main.kv")
class Status(FloatLayout):
_change = StringProperty()
_tnd = ObjectProperty(None)
def update(self, *args):
self.time = time.asctime()
self._change = str(self.time)
self._tnd.text = str(self.time)
print (self._change)
class Timer1(Label):
a = NumericProperty(10) # seconds
color = 1, 1, 1, 1
font_size = 50
def start(self):
Animation.cancel_all(self) # stop any current animations
self.anim = Animation(a=0, duration=self.a)
def finish_callback(animation, incr_crude_clock):
incr_crude_clock.text = "COOL"
self.anim.bind(on_complete=finish_callback)
self.anim.start(self)
def on_a(self, instance, value):
self.text = str(round(value, 1))
class XGApp(App):
time = StringProperty()
def update(self, *args):
self.time = str(time.asctime())
def build (self):
Clock.schedule_interval(self.update, 1)
t1 = Timer1()
return backbone
xApp = XGApp()
if __name__ == "__main__":
xApp.run()
and the .kv:
<ContScreen>:
DigitalClock:
pos_hint: {'center_x': 0.1, 'center_y': 0.9}
size_hint: (0.075, 0.075)
StackLayout
orientation: "tb-rl"
spacing: 15
Button:
text: "1"
size_hint: None, .16
width: 225
on_press:
self.background_color = (1.7, 0, 1.7, 1)
t1.start()
I am trying to get the Timer1 aspect as a label in FloatLayout on the .kv, which appears as the button is triggered. At the moment, what I've been getting is the Timer1 as a full-screen label.
Please help!
Solution
Move the design view of Timer's Label from Python code into kv file.
Add constructor for class Timer and accepts arguments, root, instance, duration, bg_colour
In kv file, pass arguments root (screen 'cont'), instance of button, duration, background colour when instantiating Timer
In build method, remove t1 = Timer1()
Example
main.py
import kivy
kivy.require('1.11.0')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.clock import Clock
from kivy.properties import StringProperty, NumericProperty, ObjectProperty
from kivy.animation import Animation
import time
class IntroScreen(Screen):
pass
class ContScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
class Status(FloatLayout):
_change = StringProperty()
_tnd = ObjectProperty(None)
def update(self, *args):
self.time = time.asctime()
self._change = str(self.time)
self._tnd.text = str(self.time)
print (self._change)
class Timer(Label):
a = NumericProperty() # seconds
def __init__(self, root, instance, duration, bg_colour, **kwargs):
super(Timer, self).__init__(**kwargs)
self.obj = instance
self.a = duration
self.root = root
self.obj.disabled = True # disable widget/button
self.obj.background_color = bg_colour
self.root.add_widget(self) # add Timer/Label widget to screen, 'cont'
def animation_complete(self, animation, widget):
self.root.remove_widget(widget) # remove Timer/Label widget to screen, 'cont'
self.obj.background_color = [1, 1, 1, 1] # reset to default colour
self.obj.disabled = False # enable widget/button
def start(self):
Animation.cancel_all(self) # stop any current animations
self.anim = Animation(a=0, duration=self.a)
self.anim.bind(on_complete=self.animation_complete)
self.anim.start(self)
def on_a(self, instance, value):
self.text = str(round(value, 1))
class XGApp(App):
time = StringProperty()
def update(self, *args):
self.time = str(time.asctime())
def build (self):
Clock.schedule_interval(self.update, 1)
return Builder.load_file("main.kv")
if __name__ == "__main__":
XGApp().run()
kv file
#:import DigitalClock digitalclock
#:import Timer main.Timer
<ContScreen>:
DigitalClock:
pos_hint: {'center_x': 0.1, 'center_y': 0.9}
size_hint: (0.075, 0.075)
StackLayout
orientation: "tb-rl"
spacing: 15
Button:
text: "1"
size_hint: None, .16
width: 225
on_press:
Timer(root, self, 5, [0.17, 1.7, 0, 1]).start()
Button:
text: "2"
size_hint: None, .16
width: 225
on_press:
Timer(root, self, 10, [1.7, 0, 1.7, 1]).start()
<Timer>:
canvas.before:
Color:
rgba: 0, 0, 0.5, 1 # 50% blue
Rectangle:
size: self.size
pos: self.pos
size_hint: 0.3, .1
font_size: 50
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
Output
One way to make it work is to have the Button create the timer. Start by adding a start_timer() method to the ContScreen class:
class ContScreen(Screen):
def start_timer(self, *args):
timer = Timer1(size_hint=(0.2, 0.2))
self.add_widget(timer)
timer.start()
For this to work, make three other changes:
Change your main.kv file to make a root widget (eliminate the <> surrounding ContScreen).
Change the on_press for the button in the .kv file by replacing t1.start() with root.start_timer().
Eliminate the t1 = Timer1() statement from your build method in the XGApp class.
Another approach would be to create the Timer1 in the .kv file, and just start it running when the Button is pressed. To do this, change your .kv file to include a Timer:
ContScreen:
DigitalClock:
pos_hint: {'center_x': 0.1, 'center_y': 0.9}
size_hint: (0.075, 0.075)
StackLayout
orientation: "tb-rl"
spacing: 15
Button:
text: "1"
size_hint: None, .16
width: 225
on_press:
self.background_color = (1.7, 0, 1.7, 1)
timer.start()
Timer1:
id: timer
text: '0.0'
size_hint: (0.2, 0.2)
Move your backbone = Builder.load_file("main.kv") to after the definition of the Timer1 class. And change the ContScreen class back to:
class ContScreen(Screen):
pass