Search code examples
pythoncanvaskivylabelkivy-language

Proper way of adding a canvas to a label


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: enter image description here

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)

Solution

  • 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]))
            ...