Creating KivyMD class within Kivy App

1,300 views
Skip to first unread message

Bartłomiej Dębowski

unread,
Aug 23, 2021, 8:38:17 AM8/23/21
to Kivy users support
I want to implement a calendar within my app. I found that KivyMD has a feature named MDDatePicker and it looks almost exactly like something I need. My problem is I cannot run it; I don't know how to run Kivymd app inside MyMainApp. Is it possible? I also want it to run in the same app window as the main app.

Elliot Garbus

unread,
Aug 23, 2021, 9:02:43 AM8/23/21
to kivy-...@googlegroups.com

You would need to install kivyMD and Kivy.  Use the MDApp to create your main App class.  You can mix kivy widgets with KivyMD widgets.

--
You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/74dcada2-b7e3-4e44-805a-e175c78055b7n%40googlegroups.com.

 

Bartłomiej Dębowski

unread,
Aug 23, 2021, 9:25:20 AM8/23/21
to Kivy users support
And it works! Thanks!
And do you know how can I input some specific values under each day in the calendar? Like when I click on a day I can see text that I wrote?

Elliot Garbus

unread,
Aug 23, 2021, 1:03:41 PM8/23/21
to kivy-...@googlegroups.com

And do you know how can I input some specific values under each day in the calendar? Like when I click on a day I can see text that I wrote?

The datepicker is used for selecting dates.  It does not have that capability.  You would need to create that capability.

Bartłomiej Dębowski

unread,
Aug 24, 2021, 7:39:42 AM8/24/21
to Kivy users support
Thank you for answer.
I have another problem with MD widgets. I don't quite understand the whole structure and I'm having a hard time implementing those widgets into .py and .kv files. 
Here's my problem: I want to place a chart into one of two layouts in my "calendar" screen

<Calendar>:
    name: "Calendar"
    canvas.before:
        Color:
            rgba: (0, 0, 0, 1)
        Rectangle:
            pos: self.pos
            size: self.size

    MDScreen:
        MDBoxLayout:
            orientation: "vertical"

            ScrollView:
                MDBoxLayout:
                    orientation: "vertical"
                    spacing: 20
                    padding: 10
                    adaptive_high: True

                    AKBarChart:
                        id:chart
                        size_hint_y: None
                        height: "280dp"

    GridLayout:
        cols: 1
        size_hint: 1, 0.2

        Button:
            size_hint: 0.25, 0.25
            pos_hint: {"x": 0.4, "y": 0.01}
            text: "Go Back"
            on_release:
                app.root.current = "Menu"
                root.manager.transition.direction = "right"

My .py file looks like this:

(...)
class Calendar(Screen):
    def show_cal(self):
        chart = self.root.ids.chart
        chart.x_values = [1, 2, 3, 4, 5]
        chart.y_values = [2, 4, 6, 8, 10]


kv = Builder.load_file("my.kv")


class MyMainApp(MDApp):
    def build(self):
        return kv


if __name__ == '__main__':
    MyMainApp().run()

When I try to run it I got an error:
"ValueError: KivyMD: App object must be initialized before loading root widget."

Answer to that due to the github kivymd doc is:
"Some of your widgets are created before MainApp().run(). To fix this issue, move Builder.load_... (that loads root widget) to build method of app class and move widget creation to methods that call after app initialization."

When I move my "kv" variable above Calendar class, I got an error:
"raise FactoryException('Unknown class <%s>' % name)
 kivy.factory.FactoryException: Unknown class <Calendar>"

How to make it works?

Elliot Garbus

unread,
Aug 24, 2021, 10:41:35 AM8/24/21
to kivy-...@googlegroups.com

Share a complete (minimal) runnable example that demonstrates the issue.

Bartłomiej Dębowski

unread,
Aug 24, 2021, 11:02:26 AM8/24/21
to Kivy users support
.py file:

import kivy

kivy.require('2.0.0')

import kivy.core.text
import numpy as np
import time
import PoseModule as pm
import cv2
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.base import EventLoop
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
from kivymd.app import MDApp
from kivymd_extensions.akivymd.uix.charts import AKBarChart
from kivy.core.window import Window
from kivy.uix.image import Image
from kivy.graphics.texture import Texture
from kivy.clock import Clock

detector = pm.PoseDetector()


class WindowManager(ScreenManager):
    pass


class Menu(Screen):
    pass


class KivyCamera(Image):

    def __init__(self, **kwargs):
        super(KivyCamera, self).__init__(**kwargs)
        self.cap = None
        self.counter = 0
        self.direction = 0
        self.event = None

    def start(self, cap, fps=30):
        self.cap = cap
        self.event = Clock.schedule_interval(self.update, 1.0 / fps)

    def stop(self):
        if self.event:
            Clock.unschedule(self.event)
        self.cap = None

    def update(self, dt):
        ### (just cv2 functions)


class WorkingScreen(Screen, BoxLayout):
        def init_qrtest(self):
            pass

        def dostart(self, *largs):
            global cap
            cap = cv2.VideoCapture(0)
            self.ids.qrcam.start(cap)

        def doexit(self):
            global cap
            self.ids.qrcam.stop()


class Calendar(Screen):
    def show_cal(self):
        chart = self.root.ids.chart
        chart.x_values = [1, 2, 3, 4, 5]
        chart.y_values = [2, 4, 6, 8, 10]


kv = Builder.load_file("my.kv")


class MyMainApp(MDApp):
    def build(self):
        return kv


if __name__ == '__main__':
    MyMainApp().run()

and .kv file:

#:kivy 2.0.0

WindowManager:
    Menu:
    WorkingScreen:
    Calendar:

<Menu>:
    name: "Menu"
    canvas.before:
        Color:
            rgba: (0, 0, 0, 1)
        Rectangle:
            pos: self.pos
            size: self.size

    GridLayout:
        cols: 1
        size: root.width - 400, root.height - 400
        pos: 200, 200

    Button:
        size_hint: 0.25, 0.25
        pos_hint: {"x": 0.4, "y": 0.55}
        text: "START WORKOUT"
        on_release:
            app.root.current = "Workout"
            root.manager.transition.direction = "left"

    Button:
        size_hint: 0.25, 0.25
        pos_hint: {"x": 0.4, "y": 0.3}
        text: "CALENDAR"
        on_release:
            app.root.current = "Calendar"
            root.manager.transition.direction = "left"

<WorkingScreen>:
    name: "Workout"
    canvas.before:
        Color:
            rgba: (0, 0, 0, 1)
        Rectangle:
            pos: self.pos
            size: self.size

    GridLayout:
        cols: 1
        size: root.width, root.height

        KivyCamera:
            id: qrcam
            allow_stretch: True

        GridLayout:
            cols: 3
            size_hint: 1, 0.2

            Button:
                size_hint: 0.25, 0.25
                pos_hint: {"x": 0.25, "top":1}
                text: "START"
                on_release:
                    root.dostart()

            Button:
                size_hint: 0.25, 0.25
                pos_hint: {"x": 0.5, "top":1}
                text: "STOP"
                on_release:
                    app.root.current = "Menu"
                    root.manager.transition.direction = "right"
                    root.doexit()

            Button:
                size_hint: 0.25, 0.25
                pos_hint: {"x": 0.75, "top":1}
                text: "GO BACK"

Elliot Garbus

unread,
Aug 24, 2021, 2:18:34 PM8/24/21
to kivy-...@googlegroups.com

Looking at the source code for kivymd_extensions, you need to add an import to register all of the widgets.

import kivymd_extensions.akivymd   # this import registers the widgets in kv

You also need to define the initial x_values and y_values in the bar chart.  I have added them to your kv code.

 

import kivy

kivy.require(
'2.0.0')

# import kivy.core.text
# import numpy as np
# import time
# import PoseModule as pm
# import cv2
# from kivy.app import App
# from kivy.uix.widget import Widget
# from kivy.base import EventLoop
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
from kivymd.app import MDApp
import kivymd_extensions.akivymd   # this import registers the widgets in kv
# from kivy.core.window import Window
from kivy.uix.image import Image
# from kivy.graphics.texture import Texture
from kivy.clock import Clock

# detector = pm.PoseDetector()


class WindowManager(ScreenManager):
   
pass


class
Menu(Screen):
   
pass


class
KivyCamera(Image):

   
def __init__(self, **kwargs):
       
super(KivyCamera, self).__init__(**kwargs)
       
self.cap = None
       
self.counter = 0
       
self.direction = 0
       
self.event = None

    def
start(self, cap, fps=30):
       
self.cap = cap
       
self.event = Clock.schedule_interval(self.update, 1.0 / fps)

   
def stop(self):
       
if self.event:
            Clock.unschedule(
self.event)
       
self.cap = None

    def
update(self, dt):
       
### (just cv2 functions)
       
pass


class
WorkingScreen(Screen, BoxLayout):
       
def init_qrtest(self):
           
pass

        def
dostart(self, *largs):
           
# global cap
            # cap = cv2.VideoCapture(0)
            # self.ids.qrcam.start(cap)
           
pass

        def
doexit(self):
           
# global cap
            # self.ids.qrcam.stop()
           
pass


class
Calendar(Screen):
   
def show_cal(self):
        chart =
self.root.ids.chart
        chart.x_values = [
1, 2, 3, 4, 5]
        chart.y_values = [
2, 4, 6, 8, 10]



class MyMainApp(MDApp):
   
def build(self):
       
return Builder.load_file("my.kv")


if __name__ == '__main__':
    MyMainApp().run()

 

Kv Code:

 

"280dp"
                       
x_values: [1, 2, 3, 4, 5]
                        y_values: [
2, 4, 6, 8, 10]


   
GridLayout:
       
cols: 1
       
size_hint: 1, 0.2

       
Button:
           
size_hint: 0.25, 0.25
           
pos_hint: {"x": 0.4, "y": 0.01}
           
text: "Go Back"
           
on_release:
               
app.root.current = "Menu"
               
root.manager.transition.direction = "right"

Bartłomiej Dębowski

unread,
Aug 24, 2021, 2:43:44 PM8/24/21
to Kivy users support
Still dosn't work, I just gave up on MD widgets. Instead I try to insert circular progress bar from  "from kivy.uix.progressbar import ProgressBar" 

I guess my biggest problem is that everybody in youtube tutorials always create just an empty class where they insert all the functionality from builder.load_file that is later use as a main return and I don't know how to combine all of it. Is it possible to have 2 or more builders? I have no idea how to create those widgets in class and how to alocate them i kv file.

Elliot Garbus

unread,
Aug 24, 2021, 2:53:42 PM8/24/21
to kivy-...@googlegroups.com

You can choose to do what you want – But I downloaded and installed the extension, made the changes and ran your code, with the changed below.

 

 

Bartłomiej Dębowski

unread,
Aug 24, 2021, 7:48:44 PM8/24/21
to Kivy users support
How can I assign values to apear on the progress bar and label? Here is my code:

class Calendar(Screen, Widget):
    def push_counter(self):
        current = self.ids.circle_bar.value
        current += "some data"
        self.ids.circle_bar.value = current
        # update the label
        self.ids.push_ups.text = f"{int(current*100)}% Daily Push-ups Goal!"

    def sit_counter(self):
        current = self.ids.line_bar.value
        current += "some data"
        self.ids.line_bar.value = current
        # update the label
        self.ids.sit_ups.text = f"{int(current*100)}% Daily Sit-ups Goal!"

and .kv file:

<Calendar>:
    name: "Calendar"
    BoxLayout:
        orientation: "vertical"
        size: root.width, root.height - 200

        Label:
            id: push_ups
            text: "0% Daily Push-ups Goal"

        ProgressBar:
            id: circle_bar
            min: 0
            max: 1
            pos_hint: {"x": .1}
            size_hint_x: .8

        Label:
            id: sit_ups
            text: "0% Daily Sit-ups Goal"

        ProgressBar:
            id: line_bar
            min: 0
            max: 1
            pos_hint: {"x": .1}
            size_hint_x: .8

When I run the program the labels and bars does not change a bit. How to connect it?

Elliot Garbus

unread,
Aug 24, 2021, 11:25:24 PM8/24/21
to kivy-...@googlegroups.com

Here is an example of using Clock and Kivy properties to set values, and also setting values by writing to widget attributes.

 

from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty, NumericProperty
from time import asctime

kv =
"""
<TimeLabel>:
    text: self.time
    font_size: 40

RootBoxLayout:
    orientation: 'vertical'
    TimeLabel:
    Label:
        text: 'Count Down Timer'
        font_size: 30
    ProgressBar:
        id: progress_bar
        min: 0
        max: 100
        size_hint_y: None
        height: 24
        size_hint_x: .8
        pos_hint: {'center_x': 0.5}
    Label:
        text: str(root.count_down)
        font_size: 30
        size_hint_y: None
        height: 96
    Button:
        text: 'Start'
        size_hint_y: None
        height: 48
        on_release: root.start_count_down()
        disabled: root.count > 0
"""


class TimeLabel(Label):
    time = StringProperty(asctime())

   
def on_kv_post(self, base_widget):
        Clock.schedule_interval(
self.update_time, 1)

   
def update_time(self, dt):
       
self.time = asctime()


class RootBoxLayout(BoxLayout):
    count_down = NumericProperty(
10)
    count = NumericProperty()

   
def __init__(self, **kwargs):
       
super().__init__(**kwargs)
       
self.schedule = None

    def
start_count_down(self):
       
self.schedule = Clock.schedule_interval(self._update, .1)

   
def _update(self, dt):
       
if self.count > 100# 100 * .1 is 10 sec
           
self.schedule.cancel()
           
self.schedule = None
           
self.ids.progress_bar.value = self.count = 0
           
self.count_down = 10
       
else:
           
self.count += 1
           
self.ids.progress_bar.value  = self.count
           
if not self.count % 10:
               
self.count_down -= 1


class ProgressDemoApp(App):
   
def build(self):
       
return Builder.load_string(kv)


ProgressDemoApp().run()
Reply all
Reply to author
Forward
0 new messages