In the following code the ellipse gets displayed with no transparency
(alpha=1). Probably when self.color changes, the alpha is set to 1 (interestingly
this doesn't happen if the color is set to (1,1,1) in the setup method).
Is there a way to change the color without changing the alpha?
I would like to set the alpha in the kv file and change the color from python.
Thanks
alpha.kv:
#:kivy 1.9.1
<MyWidget>:
color: 1,1,1
canvas:
Color:
hsv: self.color
a: 0.3
Ellipse:
pos: (200, 200)
size: (30, 30)
main.py:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty
class MyWidget(Widget):
colorH = NumericProperty(1)
colorS = NumericProperty(1)
colorV = NumericProperty(1)
color = ReferenceListProperty(colorH,colorS,colorV)
def setup(self):
self.color = (0.5,1,1)
# self.color = (1,1,1) # here the alpha doesn't change
class AlphaApp(App):
def build(self):
w = MyWidget()
w.setup()
return w
if __name__ == '__main__':
AlphaApp().run()
Related
I want to make a launcher app which matches the windows 11 theme. i made the background and i want to make a button which would close the launcher, but the button does not work for some reason when i put it in a different class. i can put it in the same class but i want to learn this method as i would use it while making the app icons. the code it provided below.
from kivy.config import Config
from kivy.core.window import Window
from kivy.graphics import Rectangle
from kivy.graphics.context_instructions import Color
from kivy.properties import Clock
from kivy.uix.image import Image
import cv2
from kivy.uix.widget import Widget
Config.set('graphics', 'fullscreen', 'auto')
Config.set('graphics', 'borderless', 'true')
Config.set('graphics', 'window_state', 'maximized')
from kivy.app import App
import pyautogui
from kivy.uix.floatlayout import FloatLayout
theme = 'dark'
screen = 'main'
x_button_size = (100,100)
x_button_pos = (50,50)
class XButton(Image):
def __init__(self):
super().__init__(source='assets/images/bg_image.png')
class MainScreenWidget(FloatLayout):
bg = None
rectangle = None
def __init__(self, **kwargs):
super(MainScreenWidget, self).__init__(**kwargs)
Clock.schedule_interval(self.update, 1.0/60.0)
self.init_bg()
# x_button_size = (self.width , self.height )
self.x_button = XButton()
def init_bg(self):
with self.canvas.before:
self.bg = Image(source='assets/images/bg_image.png', size_hint=[1, 1])
if theme == 'light':
Color(0.9, .9, .9, 0.7)
elif theme == 'dark':
Color(0, 0, 0, 0.6)
self.rectangle = Rectangle(size=self.size)
pass
def update(self, dt):
self.update_bg()
self.update_x_button()
def update_bg(self):
self.bg.size = self.width, self.height
self.bg.pos_hint = {'center_x': self.width / 2, 'center_y': self.height / 2}
self.rectangle.size = self.size
pass
def update_x_button(self):
self.x_button.size = (Window.size[0], Window.size[1])
self.x_button.pos = (5, 5)
pass
class LauncherApp(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
bg_image = pyautogui.screenshot()
bg_image.save(r'assets/images/bg_image.png')
image = cv2.imread('assets/images/bg_image.png')
blurred_image = cv2.GaussianBlur(image, (75, 75), 0)
cv2.imwrite('assets/images/bg_image.png', blurred_image)
LauncherApp().run()
any recommendations for how to make the icons are also helpful
I'm trying to make an app where I can display a circular progressbar, I've been told to use a FloatLayout so the arrangements stay organized no matter the size of the mobile screen. I've used this example for the circular progressbar How to make circular progress bar in kivy? , however, I can't seem to put the progressbar within the FloatLayout so that it organizes itself as I change the size of the screen. Anyone has a suggestion?
EDIT: I want the progressbar to stay at the center of the window, relatively based on the current window size and height. With the current code, when I change the size of the window, the progressbar doesn't relocate itself to the middle.
Here's the code I'm using:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.widget import Widget
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.progressbar import ProgressBar
from kivy.core.text import Label as CoreLabel
from kivy.lang.builder import Builder
from kivy.graphics import Color, Ellipse, Rectangle
from kivy.clock import Clock
class CircularProgressBar(ProgressBar, FloatLayout):
def __init__(self,**kwargs):
super(CircularProgressBar,self).__init__(**kwargs)
self.thickness = 40
self.label = CoreLabel(text="0", font_size=self.thickness, )
self.texture_size = None
self.refresh_text()
self.draw()
def draw(self):
with self.canvas:
self.canvas.clear()
Color(0.26,0.26,0.26)
Ellipse(pos=self.pos, size=self.size)
Color(1,0,0)
Ellipse(pos=self.pos,size=self.size,angle_end=(self.value/100.0)*360)
Color(0,0,0)
Ellipse(pos=(self.pos[0] + self.thickness / 2, self.pos[1] + self.thickness / 2),size=(self.size[0] - self.thickness, self.size[1] - self.thickness))
Color(1, 1, 1, 1)
Rectangle(texture=self.label.texture,size=self.texture_size,pos=(self.size[0]/2-self.texture_size[0]/2,self.size[1]/2 - self.texture_size[1]/2))
self.label.text = str(int(self.value))
def refresh_text(self):
self.label.refresh()
self.texture_size=list(self.label.texture.size)
def set_value(self, value):
self.value = value
self.label.text = str(int(self.value))
self.refresh_text()
self.draw()
class MainWindow(Screen, Widget):
current = ""
class WindowManager(ScreenManager):
pass
kv = Builder.load_file("main.kv")
sm = WindowManager()
screens = [MainWindow(name="main")]
for screen in screens:
sm.add_widget(screen)
sm.current = "main"
class Main(App):
def animate(self,dt):
circProgressBar = self.root.get_screen('main').ids.cp
if circProgressBar.value < 99:
circProgressBar.set_value(circProgressBar.value+1)
else:
circProgressBar.set_value(0)
def build(self):
Clock.schedule_interval(self.animate, 0.1)
return sm
if __name__ == "__main__":
Main().run()
Here is the .kv file:
WindowManager:
MainWindow:
<MainWindow>:
name: "main"
FloatLayout:
CircularProgressBar:
id: cp
pos: 250, 250
size_hint:(None,None)
height:200
width:200
max:100
You can accomplish that by using pos_hint to set the position of the CircularProgressBar, and then making sure that everything in your draw() method is based on that position. For example, here is pos_hint in your kv:
WindowManager:
MainWindow:
<MainWindow>:
name: "main"
FloatLayout:
CircularProgressBar:
id: cp
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
size_hint:(None,None)
height:200
width:200
max:100
Then your draw() method can be:
def draw(self):
with self.canvas:
self.canvas.clear()
Color(0.26,0.26,0.26)
Ellipse(pos=self.pos, size=self.size)
Color(1,0,0)
Ellipse(pos=self.pos,size=self.size,angle_end=(self.value/100.0)*360)
Color(0,0,0)
Ellipse(pos=(self.pos[0] + self.thickness / 2, self.pos[1] + self.thickness / 2),size=(self.size[0] - self.thickness, self.size[1] - self.thickness))
Color(1, 1, 1, 1)
Rectangle(texture=self.label.texture,size=self.texture_size,pos=(self.pos[0]-self.texture_size[0],self.center[1] - self.texture_size[1]/2))
self.label.text = str(int(self.value))
The only modification to your draw() method is in the pos of the Rectangle.
I just started to learn Kivy so I am still familiar with its functionalities.
I am trying to put an image as a background to my app main page. This is what I did:
class Prin(BoxLayout):
def __init__(self,**kwargs):
super(Prin,self).__init__(**kwargs)
layout = BoxLayout(orientation='vertical')
with self.canvas:
self.rect = Rectangle(source='test.png', pos=layout.center, size=(self.width, self.height))
self.text = Label(text='Press start')
fb = Button(text='Start!', size_hint =(0.5, 0.1), pos_hint ={'center_x':.5, 'y':.5}, padding=(10, 0), on_press=self.start)
layout.add_widget(self.text)
layout.add_widget(fb)
self.add_widget(layout)
def start(self,event):
self.text.text = self.text.text+ "\nNew line"
class MainApp(App):
def build(self):
return Prin()
if __name__ == "__main__":
app = MainApp()
app.run()
The desired behavior is an image covering the whole screen, that's why I've put pos=self.center, size=(self.width, self.height)
This is the output:
So I have two questions:
1/ Why is the image appearing in the left bottom side ? What widget is actually there ? I am supposed to have only a BoxLayout with 2 widgets in a vertical orientation. I don't understand what is there.
2/ What should why put in size and pos to have the desired output ?
I would recommend putting all graphic elements in a .kv file, so there are fewer imports and it looks better.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
KV = ("""
<Prin>
BoxLayout:
orientation: 'vertical'
canvas.before:
Rectangle:
pos: self.pos
size: self.size
source: 'test.png'
Label:
id: label
text: 'TEXT'
Button:
text: 'Start!'
size_hint: None, None
size_hint: 0.5, 0.1
pos_hint: {'center_x': .5, 'center_y': .5}
padding: 10, 0
on_press: root.start()
""")
class Prin(BoxLayout):
Builder.load_string(KV)
def __init__(self, **kwargs):
super(Prin, self).__init__(**kwargs)
def start(self):
self.ids.label.text += "\nNew line"
class MainApp(App):
def build(self):
return Prin()
if __name__ == "__main__":
app = MainApp()
app.run()
If you still want to do this not in the kv file, then the problem is in self.size, by default, this value is [100, 100], only after calling the class and adding it to the main window it changes.
from kivy.core.window import Window
class Prin(BoxLayout):
def __init__(self, **kwargs):
super(Prin, self).__init__(**kwargs)
with self.canvas.before:
Rectangle(source='test.png', pos=self.pos, size=Window.size)
print(self.size) # [100, 100]
...
class MainApp(App):
def build(self):
self.screen = Prin()
return self.screen
def on_start(self):
print(self.screen.size) # [800, 600]
And don't forget about imports when you ask a question, the code should run without any manipulation
In response to your questions:
The image is appearing in that position because pos=layout.center is not a valid position and so instead sets it to a default value ([100, 100] I believe). To fix this, change pos=layout.center to pos=layout.pos
Your size is the default value also! This is getting a little technical but when you initialise your Prin class you are specifying the size of the Rectangle to be the current size of the BoxLayout. However, since it has not been initialised yet, the BoxLayout doesn't yet have a size! Again, Kivy handles this by giving it a default size.
Why are my Buttons and Labels correct?
Kivy automatically binds the children of a BoxLayout to the size and position of the BoxLayout. This binding ensures that when the position and size of the BoxLayout are changed, so too are the widgets within it (https://kivy.org/doc/stable/api-kivy.event.html).
Why doesn't Kivy bind the rectangle?
This has something to do with the canvas. The canvas is a drawing instruction shared by a widget, and not a property of any individual widget. Hence you'll programmatically bind your rectangle to the BoxLayout. (https://kivy.org/doc/stable/api-kivy.graphics.instructions.html)
How do I achieve this binding you speak of?
Two ways. Firstly (preferred), you can define your widgets in the KV language as this will automatically handle any binding you wish. Second, you can create an 'on_size' callback. Something like:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.graphics import Rectangle
class Prin(BoxLayout):
def __init__(self, **kwargs):
super(Prin, self).__init__(**kwargs)
layout = BoxLayout(orientation='vertical')
with self.canvas:
self.rect = Rectangle(source='test.png', pos=layout.pos, size=self.size)
self.text = Label(text='Press start')
fb = Button(text='Start!', size_hint=(0.5, 0.1), pos_hint={'center_x': .5, 'y': .5}, padding=(10, 0),
on_press=self.start)
layout.add_widget(self.text)
layout.add_widget(fb)
self.add_widget(layout)
def start(self, *_):
self.text.text = self.text.text + "\nNew line"
def resize(self, *_):
widgets = self.children[:]
self.canvas.clear()
self.clear_widgets()
with self.canvas:
self.rect = Rectangle(source='test.png', pos=self.pos, size=self.size)
for widget in widgets:
self.add_widget(widget)
on_size = resize
class TestApp(App):
def build(self):
return Prin()
if __name__ == "__main__":
app = TestApp()
app.run()
I just would like to add as a BIG P.S. although the above code solves your problem, it does so in probably the least efficient way imaginable. It is far better to define your widget in the kv file.
I want to draw multi-box on a image. How to chage the parameters of the box pos, size and lable in a LineRectangle class in python kivy?
class LineRectangle(Widget):
def __init__(self, **kwargs):
super(LineRectangle, self).__init__(**kwargs)
with self.canvas:
Color(1, 0, 0, 1)
self.line = Line(width=2, rectangle=(self.x, self.y, self.width, self.height))
self.label = Label(text='Rectangle', pos=(self.x, self.y), size=(10, 10))
class LineExtendedApp(App):
def build(self):
root = FloatLayout()
image = Image(source='000001.jpg', allow_stretch=False, keep_ratio=True)
root.add_widget(image)
bbox1 = LineRectangle()
bbox1.line = Line(width=1, rectangle=(100, 100, 100, 100))
bbox1.label = Label(text='bbox1', pos=(100, 100), size=(10, 10))
bbox2 = LineRectangle()
bbox2.line = Line(width=1, rectangle=(300, 300, 100, 100))
bbox2.label = Label(text='bbox1', pos=(300, 300), size=(10, 10))
root.add_widget(bbox1)
root.add_widget(bbox2)
return root
if __name__ == '__main__':
LineExtendedApp().run()
You can create Properties for the parameters that you want to be able to change. Then use a kv rule to create bindings (so that changes to a Property are automatically shown in your display). Here is an example of how to do that:
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.properties import ListProperty, NumericProperty, StringProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.uix.widget import Widget
class LineRectangle(Widget):
line_color = ListProperty([])
line_rect = ListProperty([])
line_width = NumericProperty()
label_text = StringProperty('')
label_pos = ListProperty([])
def __init__(self, **kwargs):
self.line_color = kwargs.pop('line_color', [1,1,1,1])
self.line_rect = kwargs.pop('line_rect', [0,0,50,50])
self.line_width = kwargs.pop('line_width', 1)
self.label_text = kwargs.pop('label_text', 'text')
self.label_pos = kwargs.pop('label_pos', [0,0])
super(LineRectangle, self).__init__()
Builder.load_string('''
<LineRectangle>:
canvas:
Color:
rgba: root.line_color
Line:
width: root.line_width
rectangle: root.line_rect
Label:
text: root.label_text
pos: root.label_pos
size: 10, 10
''')
class LineExtendedApp(App):
def build(self):
root = FloatLayout()
image = Image(source='000001.jpg', allow_stretch=False, keep_ratio=True)
root.add_widget(image)
self.bbox1 = LineRectangle(line_wdth=2, line_rect=[100, 100, 100, 100], line_color=[1,0,0,1], label_text='bbox1', label_pos=[100, 100])
bbox2 = LineRectangle(line_width=1, line_rect=[300, 300, 300, 300], line_color=[0,1,0,1], label_text='bbox2', label_pos=[300, 300])
root.add_widget(self.bbox1)
root.add_widget(bbox2)
Clock.schedule_once(self.adjust, 2) # just to demonstrate change parameter
return root
def adjust(self, dt):
self.bbox1.line_width = 10
if __name__ == '__main__':
LineExtendedApp().run()
Hi there when i run my code , it is supposed to show a rectangle shape moving up and down. However, i can only get the rectangle shape in static mode (not moving). However, according to the function under the widget class, it should work. Any help will be aprreciated !
countdown.py file
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import ListProperty
from kivy.uix.widget import Widget
from kivy.graphics import *
from kivy.clock import Clock
from kivy.uix.screenmanager import Screen, ScreenManager
class WindowManager(ScreenManager):
pass
class GameWindow(Screen):
def __init__(self, **kwargs):
super(GameWindow, self).__init__(**kwargs)
self.add_widget(Root())
self.add_widget(Rect())
class Root(Widget):
pass
class Rect(Widget):
velocity = ListProperty([10, 2])
def __init__(self, **kwargs):
super(Rect, self).__init__(**kwargs)
Clock.schedule_interval(self.update, 1/8)
def update(self, *args):
self.y += self.velocity[1]
if self.y < 0 or (self.y + self.height) > 250:
self.velocity[1] *= -1
kys = Builder.load_file("countdown.kv")
class MyMainApp(App):
def build(self):
return kys
if __name__ == "__main__":
MyMainApp().run()
countdown.kv file
WindowManager:
GameWindow:
<GameWindow>:
canvas:
Color:
rgba: 1, 0, 0, 1
Rectangle:
pos: self.pos
size: (42,15)
Rect:
pos: 85, 100
A Widget has some of the following characteristics:
If you are placed as a child of another Widget (Screen, Spinner, etc) this will take as much space as you can since the size_hint is (1,1), in your case it occupies the entire space of the Screen.
It cannot be distinguished since it does not have a background color or other visual element.
With the above mentioned it follows that even when "Rect" which is a Widget was moved this cannot be distinguished so we must use the canvas for its painting (I suppose that is what you tried when using the canvas in GameWindow), in addition to set a container independent size should be set the size_hint to (None, None).
Considering the above, the solution is:
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import ListProperty
from kivy.uix.widget import Widget
from kivy.clock import Clock
from kivy.uix.screenmanager import Screen, ScreenManager
class WindowManager(ScreenManager):
pass
class GameWindow(Screen):
pass
class Rect(Widget):
velocity = ListProperty([10, 2])
def __init__(self, **kwargs):
super(Rect, self).__init__(**kwargs)
Clock.schedule_interval(self.update, 1. / 8.)
def update(self, *args):
self.y += self.velocity[1]
if self.y < 0 or (self.y + self.height) > 250:
self.velocity[1] *= -1
kys = Builder.load_file("countdown.kv")
class MyMainApp(App):
def build(self):
return kys
if __name__ == "__main__":
MyMainApp().run()
WindowManager:
GameWindow:
<GameWindow>:
Rect:
<Rect>:
size_hint : None, None
size : 42, 15
canvas:
Color:
rgba: 1, 0, 0, 1
Rectangle:
pos: self.pos
size: self.size