I'm posting this because I want to add a canvas behind, inside, on top, I don't know, I want a background for the labels, and I have found out that I need a canvas to do that, but I don't know how to do it. I have been able to add a canvas as a background for a label, but since my app needs to be refreshed and resized I bump into problems that I am not able to fix.
I want to make the canvas so that it has the same size as the labels beside eachother, and I want to be able to remove it like when I remove the labels inside "theLayout".
I believe the problem is in the method "refresh" inside the class "HovedVindu".
If it is possible to not change the whole code and fix it so that a beginner like me is able to understand and modify it, I would appreciate it very much, if it is not possible I will still appreciate any help :)
I used paint 3D to visualize what I want to achieve:
main.py:
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
import kivy.uix.boxlayout
from kivy.uix.popup import Popup
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.graphics import Color, Rectangle, Canvas
Builder.load_file("struktur.kv")
sm = ScreenManager()
produkter = [["Melk", "04.06.2022"], ["Ost", "28.07.2022"], ["Tomat", "27.05.2022"], ["Banan", "02.06.2022"]]
class produktManager(object):
def lagreProdukt(self, navn, dato):
self.navn = navn
self.dato = dato
nyProdukt = [self.navn.text, self.dato.text]
if nyProdukt != ["", ""]:
produkter.insert(0, nyProdukt)
class HovedVindu(Screen):
def nyttProdukt(self):
show_popup()
def Refresh(self):
theLayout = self.ids.theLayout
for i in range(10):
for v in theLayout.children:
theLayout.remove_widget(v)
for i in produkter:
newLabel_navn = theLayout.add_widget(Label(text = i[0]))
newLabel_dato = theLayout.add_widget(Label(text = i[1]))
#with theLayout.canvas:
#(Color(0, 1, 1, .1))
#(Rectangle(size = (self.size), pos_hint = (self.pos)))
class P(FloatLayout):
def leggTilNyttProdukt(self, Navn, Dato):
produktManager().lagreProdukt(Navn, Dato)
Navn.text = ""
Dato.text = ""
class MainApp(App):
def build(self):
sm.add_widget(HovedVindu(name="hoved"))
return sm
def show_popup():
show = P()
popupWindow = Popup(title="Legg til ett nytt produkt", content=show, size_hint=(None,None),size=(400,400), title_align="center")
popupWindow.open()
if __name__ == "__main__":
MainApp().run()
struktur.kv:
#:kivy 2.1.0
#:import RiseInTransition kivy.uix.screenmanager.RiseInTransition
<HovedVindu>:
FloatLayout:
size_hint: 1, .1
pos_hint: {"x": 0, "top": 1}
canvas.before:
Color:
rgba: 1, 1, 1, .8
Rectangle:
pos: self.pos
size: self.size
Button:
text: "+"
size_hint: .15, .8
pos_hint: {"x": .8, "top": .9}
on_release: root.nyttProdukt()
Button:
text: "Refresh"
size_hint: .2, .8
pos_hint: {"x": .2, "top": .9}
on_release:
root.Refresh()
FloatLayout:
size_hint: 1, .9
canvas.before:
Color:
rgba: 0, 0, 0, 0
Rectangle:
pos: self.pos
size: self.size
GridLayout:
id: theLayout
cols: 2
rows: 10
Label:
text: "NAVN"
canvas.before:
Color:
rgba: 1, 0, 0, .2
Rectangle:
pos: self.pos
size: self.size
Label:
text: "DATO"
canvas.before:
Color:
rgba: 1, 0, 0, .2
Rectangle:
pos: self.pos
size: self.size
<P>:
TextInput:
id: navn
hint_text: "Navnet på produktet"
size_hint: .6, .15
pos_hint: {"x": .2, "top": .9}
TextInput:
id: dato
hint_text: "Siste forbruksdag"
size_hint: .6, .15
pos_hint: {"x": .2, "top": .7}
Button:
text: "Legg til produkt"
size_hint: 0.8, 0.2
pos_hint: {"x":0.1, "y":0.1}
on_release: root.leggTilNyttProdukt(navn, dato)
In order to have a label with some background color and use it dynamically you can create a dynamic class inherited from Label. You can also create any desired prop. for advanced usage.
First define a class in .py inherited from Label,
class CustomLabel(Label):
background_color = ListProperty([1, 1, 1, 1])
Now design it using kvlang,
<CustomLabel>:
canvas.before:
Color:
rgba: self.background_color
Rectangle:
pos: self.pos
size: self.size
Now your label is ready to use.
def Refresh(self):
...
for n, i in enumerate(produkter, start = 1):
newLabel_navn = theLayout.add_widget(CustomLabel(text = i[0], background_color = [1, 1/n, 1, 0.5]))
newLabel_dato = theLayout.add_widget(CustomLabel(text = i[1], background_color = [1/n, 1, 1, 0.8]))
...
Related
I would like to zoom in and zoom out of a collection of widgets. I would also like to scroll. I tried to accomplish this with a ScrollView as the root widget and a ScatterLayout as the child.The widgets I'd like to zoom in and out of are children to the ScatterLayout. This isn't behaving as expected. Here's a minimal version.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.scatter import Scatter
from kivy.uix.scatterlayout import ScatterLayout
from kivy.uix.scrollview import ScrollView
kv = '''
#:kivy 1.11.1
<MyScatter>:
do_translation_y: False
do_rotation: False
do_scale: False
canvas:
Color:
hsv: .1, 1, .5
Rectangle:
size: 100, 100
<ScrollView>:
size_hint: None, None
size: 640, 480
pos_hint: {'center_x': .5, 'center_y': .5}
scroll_type: ['bars']
bar_width: 10
bar_inactive_color: self.bar_color
canvas:
Color:
rgb: 1, 0, 0
Rectangle:
pos: self.pos
size: self.size
<MyScatterLayout>:
size_hint: None, None
size: 1280, 720
do_translation: False
do_rotation: False
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
canvas:
Color:
rgb: 0, 0, 1
Rectangle:
pos: self.pos
size: self.size
'''
Builder.load_string(kv)
class MyScatter(Scatter):
pass
class MyScatterLayout(ScatterLayout):
pass
class MyApp(App):
def build(self):
layout = MyScatterLayout()
layout.add_widget(MyScatter())
root = ScrollView()
root.add_widget(layout)
return root
if __name__ == '__main__':
MyApp().run()
When zooming out, the child of ScatterLayout ends up outside of the layout's bounds. I would expect all the children to stay within the layout's bounds no matter what the transformation is. What am I doing wrong?
Tangential to the question: when I zoom out enough that the ScatterLayout is smaller than the ScrollView, scrolling snaps the ScatterLayout to the origin (bottom left corner). Per the ScrollView docs it looks like a ScrollView's child is expected to be larger than the ScrollView itself. I assume to prevent this from happening I need to increase the size of the ScatterLayout as I scroll.
Not sure this is related to the issue, but ScatterLayout is a subclass of Scatter so its canvas instructions must be in local coordinates.
<MyScatterLayout>:
...
canvas:
Color:
rgb: 0, 0, 1
Rectangle:
pos: 0, 0 # <- shold be this
size: self.size
And you shouldn't write any rule for ScrollView because it affects all instances that might exist outside of your code. So instead, define a subclass and write a rule for it.
class MyScrollView(ScrollView):
pass
<MyScrollView>:
...
You have to scale the size of the ScatterLayout for the children's position to be consistent. I've also incorporated Nattosai Mito's answer.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.scatter import Scatter
from kivy.uix.scatterlayout import ScatterLayout
from kivy.uix.scrollview import ScrollView
kv = '''
#:kivy 1.11.1
<MyScatter>:
do_translation_y: False
do_rotation: False
do_scale: False
canvas:
Color:
hsv: .1, 1, .5
Rectangle:
size: 100, 100
<MyScrollView>:
size_hint: None, None
size: 640, 480
pos_hint: {'center_x': .5, 'center_y': .5}
scroll_type: ['bars']
bar_width: 10
bar_inactive_color: self.bar_color
canvas:
Color:
rgb: 1, 0, 0
Rectangle:
pos: self.pos
size: self.size
<MyScatterLayout>:
size_hint: None, None
size: 1280 * self.scale, 720 * self.scale # this is the correction
do_translation: False
do_rotation: False
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
canvas:
Color:
rgb: 0, 0, 1
Rectangle:
pos: 0, 0
size: self.size
'''
Builder.load_string(kv)
class MyScatter(Scatter):
pass
class MyScatterLayout(ScatterLayout):
pass
class MyScrollView(ScrollView):
pass
class MyApp(App):
def build(self):
layout = MyScatterLayout()
layout.add_widget(MyScatter())
root = MyScrollView()
root.add_widget(layout)
return root
if __name__ == '__main__':
MyApp().run()
I am trying to print an input given by a user but all I get is: < ObjectProperty name=input >
I can't use Text Input in my py because python quits if I try to run a program with it installed. I have tried putting the popup in the 'test' class but it just comes up with a different error.
Any help is appreciated,
Thank you.
Here is my code:
In my py:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.popup import Popup
class MyPopup(Popup):
pass
class Test(Widget):
pass
class TestApp(App):
def build(self):
return Test()
def Process(self):
text = MyPopup.input
print(text)
if __name__ == '__main__':
TestApp().run()
and in my kv:
#:import Factory kivy.factory.Factory
<MyPopup#Popup>
input: text
auto_dismiss: True
size_hint: 0.4, 0.4
TextInput:
id: text
hint_text: 'insert text'
multiline: False
on_text_validate: app.Process()
<Test>:
canvas:
Color:
rgba: 0, 0, 0, 0
Rectangle:
pos: 0, 0
size: self.width, self.height - self.height/6
Color:
rgba: 1, 1, 1, 0.1
Rectangle:
pos: 0, 0
size: self.width/2 + self.width/10, self.height - self.height/6
Color:
rgba: 1, 1, 1, 1
Rectangle:
pos: 0, self.height - self.height/6
size: self.width, self.height/6
Button:
id: GL
text: 'In here'
pos: 0 , root.height - root.height/6
on_parent: GLdropdown.dismiss()
on_release: GLdropdown.open(self)
size_hint_y: None
height: root.height/6
width: root.width/7
DropDown:
id: GLdropdown
on_select: btn.text = '{}'.format(args[1])
Button:
id: 'AV'
text: 'press me'
size_hint_y: None
height: 50
on_release: Factory.MyPopup().open()
If you change:
on_text_validate: app.Process()
to:
on_text_validate: app.Process(self.text)
And change the Process() method to:
def Process(self, text):
print(text)
I think it will work as you want.
The problem is that the line:
text = MyPopup.input
makes text a reference to the ObjectProperty of the class MyPopup. What you really want is a reference to the text of the TextInput, which is simply self.text in the TextInput.
I'm struggling to get the text in a button to align in the centre of the button, it's just sitting at the bottom of the button.
I've tried changing text_size and font_size to the RoundedButton but nothing has worked so far.
FloatLayout:
RoundedButton:
size_hint: 0.417, 0.15625
pos_hint: {"x": 0.0556, "y": 0.15}
text: "Holding text that isn't aligning in the centre of the button'"
color: 0,0,0,1
text_size: self.width , self.height
halign: "center"
font_size: self.height - 75
<RoundedButton#Button>:
background_normal: ""
background_color: 0, 0, 0, 0
back_color: 0.2,0.6,1,1
border_radius: 10
font_size: '25'
color: self.back_color
bold: True
canvas.before:
Color:
rgba: self.back_color
Line:
rounded_rectangle: self.x, self.y, self.width, self.height, self.border_radius
width: 1
and a trimmed version of the python file:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.core.window import Window
Window.clearcolor = (1,1,1,1)
class WindowManager(ScreenManager):
pass
kv = Builder.load_file("mykivy.kv")
sm = WindowManager()
class MyApp(App):
def build(self):
return sm
if __name__ == '__main__':
MyApp().run()
I've just figured it out, if anyone wants to know the answer in future I had to set valign to "center" (as well as having halign: "center")
I'm a beginner so please excuse the simplistic question. I'm trying to create a control screen that displays a count. Essentially I want it to get a low signal on one of the GPIO's and advance one integer. I also want the ability to edit the count with "+", "-", and "clear" buttons. This is one of the first bits of code I've ever written so I may be way off base here! Here is the code for the .py:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
import RPi.GPIO as GPIO
startPin = 17
GPIO.setmode(GPIO.BCM)
GPIO.setup(startPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
class InputButton(Button):
def update(self, dt):
if GPIO.input(startPin) == True:
self.ids.textbox.text +=1
class MainWidget(FloatLayout):
def adv_callback(self, text):
self.ids.textbox.text += 1
def fall_callback(self, text):
self.ids.textbox.text -= 1
def clear_callback(self, text):
self.ids.textbox.text = 0
class MainApp(App):
'''docstring for MainApp'''
def build(self):
return MainWidget()
if __name__=="__main__":
MainApp().run()
and the .kv
# File name main.py
<MainWidget>:
canvas.before:
Color:
rgba: .95, .95, 1, .7
Rectangle:
pos: self.pos
size: self.size
Button:
id: clear
text: 'Clear'
font_size: '50sp'
pos: (650, 0)
size_hint: .2, .3
on_press: root.clear_callback(self.text)
Button:
id: add
text: '+'
font_size: '50sp'
pos: (0, 142)
size_hint: .2, .3
on_press: root.adv_callback(self.text)
Button:
id: less
text: '-'
font_size: '50sp'
pos: (0, 0)
size_hint: .2, .3
on_press: root.fall_callback(self.text)
AsyncImage:
source: '/home/sysop/Pictures/Source_Files_Pocono_logo_on_White_Bg.gif'
size_hint: .5, .3
pos: (200, 285)
TextInput:
id: textbox
multiline: False
readonly: True
size_hint: .5, .3
font_size: '80sp'
pos: (325, 0)
ActionBar:
pos: (0, 432)
ActionView:
ActionPrevious:
title: ""
with_previous: False
ActionOverflow:
ActionButton:
text: "Shutdown"
on_press: app.open_settings()
I am creating an app with Screen Manager.
I want to get text input from one screen click Next and then use it in a label for another screen and save the value as a global variable.
My question is: How to make it work, because it doesn't work the way I want it to. The label is always 0 and is never the value I type in. The value of the global variable never changes.
main.py file:
from kivy.app import App
from kivy.properties import StringProperty, NumericProperty, ObjectProperty
from kivy.uix.screenmanager import ScreenManager, Screen
global var
var = 0
class FirstScreen(Screen):
global var
def SaveResult(self, text):
global var
g = int(text)
return var
class SecondScreen(Screen):
global var
a = NumericProperty('')
a = str(var)
and my test.kv
ScreenManager:
id: screen_manager
FirstScreen:
id: screen1
name: "FirstScreen"
manager: screen_manager
SecondScreen:
id: screen2
name: "SecondScreen"
manager: screen_manager
<FirstScreen>:
FloatLayout:
canvas.before:
Color:
rgba: 0.1, 0.1, 0.1, 1
Rectangle:
# self here refers to the widget i.e BoxLayout
pos: self.pos
size: self.size
TextInput:
id: text_input
multiline: False
size_hint_x: .4
size_hint_y: .1
pos_hint: {'x': .1, 'y': .20}
Button:
background_color: 0.2, 0.7, 1, 1,
font_size: root.width / 15
id: btn1
text: "Next"
on_press:
root.SaveResult(text_input.text)
root.manager.current = 'SecondScreen'
size_hint_x: .4
size_hint_y: .1
pos_hint: {'x': .5, 'y': .20}
<SecondScreen>:
FloatLayout:
canvas.before:
Color:
rgba: 0.1, 0.1, 0.1, 1
Rectangle:
# self here refers to the widget i.e BoxLayout
pos: self.pos
size: self.size
Label:
color: 0.2, 0.7, 1, 1,
font_size: root.width / 15
id: lb1
text: root.a
size_hint_x: .4
size_hint_y: .2
pos_hint: {'x': .3, 'y': .8}