How do I get a graph to show in Kivy - python-3.x

I am looking to add a graph to one of my kivy screen app. Basic App with a StartScreen and the SecondScreen with the graph itself and few buttons. I can't get the graph to show in Kivy. I've tried creating a class for it but I always end up getting this error:
AttributeError: 'Figure' object has no attribute 'set_canvas'
I have tried finding the answer by myself but I always get to an dead end. Here is the code for the graph below as well as the code for my app. If someone could point me in the right direction you would be my HERO!
Yours truly, the coding newbie!
Here is the graph:
import plotly.graph_objects as go
fig = go.Figure(go.Indicator(
mode = "number+gauge+delta",
gauge = {'shape': "bullet"},
delta = {'reference': 300},
value = 220,
domain = {'x': [0.1, 1], 'y': [0.2, 0.9]},
title = {'text': "Avg order size"}))
fig.show()
Here is my main.py:
import plotly.graph_objects as go
from kivy.app import App
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import ButtonBehavior
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.uix.screenmanager import Screen
from kivy.graphics import Rectangle
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg
from kivy.properties import ObjectProperty
import matplotlib.pyplot as plt
class StartScreen(Screen):
pass
Window.clearcolor = (1, 1, 1, 1)
class GraphScreen(Screen):
pass
class ImageButton(ButtonBehavior, Image):
pass
GUI = Builder.load_file("main.kv")
class Main(App):
def build(self):
return GUI
def change_screen(self, screen_name):
# Get the screen manager from the kv file
screen_manager = self.root.ids['screen_manager']
screen_manager.current = screen_name
if __name__ == '__main__':
Main().run()
Here is my main.kv:
#:include kv/startscreen.kv
#:include kv/graphscreen.kv
GridLayout:
cols: 1
ScreenManager:
id: screen_manager
StartScreen:
name: "start_screen"
id: start_screen
GraphScreen:
name: "graph_screen"
id: graph_screen
Here is my startscreen.kv:
#:import utils kivy.utils
<StartScreen>:
FloatLayout:
GridLayout:
rows: 1
pos_hint: {"top": 1, "left": 1}
size_hint: 1, .5
Label:
rows: 1
text: "Budget Meter"
font_size: 30
font_name: "font/Duldolar.ttf"
color: 0, 0, 0, 1
GridLayout:
rows: 1
pos_hint: {'x':.02, 'y':.6}
size_hint: 1, .1
ImageButton:
source: "images/Ecommerce_203.jpg"
on_release:
app.change_screen("graph_screen")
Here is my graphscreen.kv:
#:import utils kivy.utils
<GraphScreen>:
BoxLayout:
orientation: "vertical"
size_hint: .3, .5
pos_hint: {'x':.35, 'y':0}
ImageButton:
source: "images/ope.jpg"
on_release:
app.change_screen("start_screen")

Related

How do I connect the Spinner ids from the kivy file with the data that I provide from my products database?

How do I connect my ids to the python code considering I want to load the string from self.listcode variable into the values of the spinner, I already use self. ids. and I don't get anything from the KV file. Thanks in advance for ur help. One of the errors popping out is the one below:
AttributeError: 'super' object has no attribute '__getattr__
I think if I'm not wrong that it has to do with that Screen doesn't having the possibility to access ids
KIVY FILE###
<WindowManager>:
FirstWindow:
SecondWindow:
<FirstWindow>:
name: "menu"
canvas.before:
Color:
rgba: (242/255, 202/255, 5/255, 1)
RoundedRectangle:
size: self.size
pos: self.pos
radius: [55]
Image:
source: 'Logoapp.png'
BoxLayout:
orientation: "horizontal"
size: 50, 50
Button:
font_size: 20
size_hint: 0.3, 0.2
pos_hint: {"x":.5, "top":.2}
pos:100, 100
background_color: 0,0,0,0
text: "Cotizar"
color: 0,0,0,1
canvas.before:
Color:
rgba: (23/255,152/255,216/255,0.71)
RoundedRectangle:
size: self.size
pos: self.pos
radius:[55]
on_release:
app.root.current = "cotiza"
root.manager.transition.direction = "left"
<SecondWindow>:
name: "cotiza"
FloatLayout:
CodigoSpin:
id: "Cod_prod"
pos: 20, 100
Label:
text: "Descripcion"
pos: 20, 35
CodigoSpin:
id: "Cod_prod2"
pos: 20, 200
Label:
text: "Descripcion"
pos: 20, 130
CodigoSpin:
id: "Cod_prod3"
pos: 20, 300
Label:
text: "Descripcion"
pos: 20, 235
<CodigoSpin#Spinner>:
size_hint: None, None
size: 100, 44
text: "CodigoSpin"
Main Program
from types import new_class
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.config import Config
from kivy.core.window import Window
from kivy.properties import ListProperty
from kivy.uix.spinner import Spinner, SpinnerOption
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.graphics import Color, Rectangle, RoundedRectangle
import sqlite3
Window.size = (250, 500 ) #Changed the size of the screen to fit my phone
from kivy.uix.textinput import TextInput
from kivy.uix.screenmanager import ScreenManager, Screen
class FirstWindow(Screen, FloatLayout):
pass
class SecondWindow(Screen, Widget):
def __init__(self, **kwargs):
super(SecondWindow, self).__init__(**kwargs)
#Create de Database connection
conn = sqlite3.connect("inventingt.db")
cursor = conn.cursor()
cursor.execute("""SELECT Codigo FROM masterinv """)
rows = cursor.fetchall()
#Create the variable listcode of objects from the database
self.listcode = [str(t[0]) for t in rows]
#This method will initialize the values on the Spinner
self.inicializarvalores()
def inicializarvalores(self):
self.ids.Cod_prod.values = self.listcode
class WindowManager(ScreenManager, Widget):
pass
class Buendes(App):
def build(self):
#Inicializamos el screen manager para inicializar las paginas
self.window = WindowManager()
#add widgets to window
#Agregamos el boton de inicializar la aplicacion
#Conectamos una base de datos
return self.window
if __name__ == "__main__":
Buendes().run()
Other way to connect your widgets which are created in .kv file to .py file is:
Give id to your widget. ( ✓ You did it as Cod_prod )
in .kv file, top of your class (under <SecondWindow>:), type this: Cod_prod:Cod_prod ( ☓ )
Import this : from kivy.properties import ObjectProperty ( ☓ )
Now under class SecondWindow(in .py file), identify your object: Cod_prod = ObjectProperty()
Now you can access your widget which is created in .kv file with:
def inicializarvalores(self):
self.Cod_prod.values = self.listcode
I couldn't understand the situation on Spinner and Screen connection. I had same problem. Also tried both solutions but couldn't get the solution.
I couldn't assign values to Spinner on root class inherited Screen.
What I tried:
On py file: self.ids.spinnerid.values = list -> not working. But py connected the spinner.
On kv file: Spinner: values: root.list -> not working.
Try to ListProperty, ObjectProperty... -> not working.
I turned around the problem using values: app.list on kv file and define the list on App class.

Kivy, python: Dynamically resize text when zooming window in or out

My code is listed below. While running the application, I want to get the text size dynamically bigger and smaller when you "zoom" in/out the application window (dragging window bigger and smaller). Just like the button as an example (white square). Right now the text size stays the same size.
Note:
If you copy my code for testing, download also blue.png for in the same folder as the .py file.
Any help would be appreciated!
import kivy
kivy.require('1.11.1')
from kivy.app import App
from kivy.base import runTouchApp
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
class FirstScreen(Screen):
pass
class myScreenManager(ScreenManager):
pass
class ImageButton(ButtonBehavior, Image):
pass
root_widget = Builder.load_string('''
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
myScreenManager:
transition: FadeTransition()
FirstScreen:
<FirstScreen>:
name: 'first'
FloatLayout:
orientation: 'vertical'
canvas.before:
Rectangle:
pos: self.pos
size: self.size
source: 'blue.png'
ImageButton:
pos_hint: {'x': 0.25, 'y': 0.18}
size_hint: 0.5, 0.3
Label:
text: 'THIS\\nIS\\nA\\nTEST'
text_size: root.width, None
font_size: 40
pos_hint: {'x': 0, 'y': 0.19}
halign: 'center'
''')
class myScreenManagerApp(App):
def build(self):
return root_widget
myScreenManagerApp().run()
You can make the font_size depend on the size of the application window. Try replacing:
font_size: 40
with something like:
font_size: root.height/12

KivyMD DatePicker not resizing with screen size

I'm not sure how to dynamically resize the MDDatePicker with the screen size, it doesn't adjust correctly like Labels and Buttons but rather adjusts erratically.
I couldn't find much on KivyMD documentation like there in on Kivy widgets.
You've got to click the Select Date button to open the Date Picker
py file
import kivy
from kivy.app import App
from kivy.properties import ObjectProperty, StringProperty, NumericProperty, ListProperty
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.core.text import Label as CoreLabel
from kivy.uix.button import Button
from kivy.core.window import Window
from kivymd.theming import ThemeManager
import mysql.connector
from kivymd.uix.picker import MDDatePicker
from kivy.uix.scrollview import ScrollView
Window.clearcolor = (1,1,1,1)
class Information(Screen):
def select_date(self):
picker = MDDatePicker(callback=self.got_date)
picker.open()
def got_date(self, the_date):
print(the_date)
class WindowManager(ScreenManager):
pass
class MyApp(App):
theme_cls = ThemeManager()
def build(self):
kv = Builder.load_file("kivy.kv")
sm = WindowManager()
screens = [Information(name="information")]
for screen in screens:
sm.add_widget(screen)
sm.current = "information"
return sm
if __name__ == '__main__':
MyApp().run()
kv file
<Information>:
name: "information"
NavigationLayout:
id: nav_layout
MDNavigationDrawer:
NavigationDrawerIconButton:
text: "Test"
on_release: app.root.current = "login"
FloatLayout:
MDToolbar:
pos_hint: {'top': 1}
md_bg_color: 0.2, 0.6, 1, 1
left_action_items: [['menu', lambda x: root.ids.nav_layout.toggle_nav_drawer()]]
MDRaisedButton:
text: "Select date"
pos_hint: {"x": 0.35, "top": 0.6}
on_release: root.select_date()
Thanks in advance
you could try under MDRaisedButton putting this:
MDRaisedButton:
size: root.width, root.height
or where ever you need to resize. The size commands format is size: x, y in kv lang

Kivy RecycleView SelectableButton Does not switch to a Dynamic Layout Page

Am working on a Project that has a RecycleView that contains details of patients.My aim is that when i click on a specific patient on the recycleView row, it should take me to specific dynamic page layout containing specific details of the selected patient in the RecycleView without using the Screen Manager, the switching of dynamic pages.how can i go about this?
i have created a method called change_dynamic_Layout() for switching dynamic pages and it works fine when using the normal button to call it. I have used a print statement in the method to show if the method is executed or not. But when i use the SelectableButton,the print statement is executed but the statement for changing page-layout is not. No errors is shown and the dynamic page does not change. Here is the code try to run it and see what am talking about?
Demo.py
from kivy.app import App
from kivy.properties import ObjectProperty, StringProperty, ListProperty,
NumericProperty
from kivy.uix.behaviors import ButtonBehavior
from kivy.graphics import Color, Rectangle
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.factory import Factory
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.properties import BooleanProperty
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
lost = Builder.load_file('Demo.kv')
class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleGridLayout):
''' Adds selection and focus behaviour to the view. '''
class SelectableButton(RecycleDataViewBehavior, Button):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableButton, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableButton, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
#selectablebutton to call the change_dynamic_Layout() method in patient class
def on_enter(self):
layout=Patient()
layout.change_dynamic_Layout()
class victor(BoxLayout):
pass
class Patient(Screen):
manage_prescription: ObjectProperty(None)
#Method to change the dynamic pagelayout
def change_dynamic_Layout(self):
layout = Factory.victor()
self.manage_prescription.clear_widgets()
self.manage_prescription.add_widget(layout)
print ('pressed')
class DemoApp(App):
title = 'Hospital Management System'
def build(self):
n= Patient()
return n
if __name__ == "__main__":
DemoApp().run()
demo.kv
<Patient>:
manage_prescription:manage_prescription
BoxLayout:
GridLayout :
cols:1
BoxLayout:
id:manage_prescription
orientation:'vertical'
BoxLayout:
size_hint_y:None
height:40
Button:
text:"NO."
font_size: 25
Button:
text:"Date"
font_size: 25
Button:
text:"Patient"
font_size: 25
Button:
text:"Doctor"
font_size: 25
on_press: root.change_dynamic_Layout()
BoxLayout:
RecycleView:
bar_width: 10
bar_color: 1, 0, 0, 1 # red
bar_inactive_color: 0, 0, 1, 1 # blue
#effect_cls: "ScrollEffect"
scroll_type: ['bars']
viewclass: 'SelectableButton'
data:[{'text': str(x)} for x in range(20)]
SelectableRecycleGridLayout:
cols:4
default_size: None, dp(56)
default_size_hint:1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: True
touch_multiselect: True
<victor>:
Label:
text:" Switched to specific page for patient's details"
<SelectableButton>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected else (1, 1, 1, 1)
Rectangle:
pos: self.pos
size: self.size
on_press: root.on_enter()
Any insight or help is much appreciated, thanks in advance.
The problem is that your on_enter method of the SelectableButton is creating a new Patient layout (Screen), and calling the change_dynamic_Layout method of that new Patient Screen. That newly created Patient Screen is not the one displayed in your app, so it has no effect on what you see. You actually want to call change_dynamic_Layout on the displayed Patient Screen. One way of doing this is to access it via App.get_running_app().root. So that your on_enter method can be changed to:
def on_enter(self):
#layout=Patient()
layout = App.get_running_app().root
layout.change_dynamic_Layout()
Here is the entire python file:
from kivy.app import App
from kivy.properties import ObjectProperty, StringProperty, ListProperty, NumericProperty
from kivy.uix.behaviors import ButtonBehavior
from kivy.graphics import Color, Rectangle
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.factory import Factory
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.properties import BooleanProperty
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
lost = Builder.load_file('Demo.kv')
class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleGridLayout):
''' Adds selection and focus behaviour to the view. '''
class SelectableButton(RecycleDataViewBehavior, Button):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableButton, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableButton, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
#selectablebutton to call the change_dynamic_Layout() method in patient class
def on_enter(self):
#layout=Patient()
layout = App.get_running_app().root
layout.change_dynamic_Layout()
class victor(BoxLayout):
pass
class Patient(Screen):
manage_prescription: ObjectProperty(None)
#Method to change the dynamic pagelayout
def change_dynamic_Layout(self):
layout = Factory.victor()
self.manage_prescription.clear_widgets()
self.manage_prescription.add_widget(layout)
print ('pressed')
class DemoApp(App):
title = 'Hospital Management System'
def build(self):
n= Patient()
return n
if __name__ == "__main__":
DemoApp().run()
And here is the Demo.kv:
<Patient>:
manage_prescription:manage_prescription
BoxLayout:
GridLayout :
cols:1
BoxLayout:
id:manage_prescription
orientation:'vertical'
BoxLayout:
size_hint_y:None
height:40
Button:
text:"NO."
font_size: 25
Button:
text:"Date"
font_size: 25
Button:
text:"Patient"
font_size: 25
Button:
text:"Doctor"
font_size: 25
on_press: root.change_dynamic_Layout()
BoxLayout:
RecycleView:
bar_width: 10
bar_color: 1, 0, 0, 1 # red
bar_inactive_color: 0, 0, 1, 1 # blue
#effect_cls: "ScrollEffect"
scroll_type: ['bars']
viewclass: 'SelectableButton'
data:[{'text': str(x)} for x in range(20)]
SelectableRecycleGridLayout:
cols:4
default_size: None, dp(56)
default_size_hint:1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: True
touch_multiselect: True
<victor>:
Label:
text:" Switched to specific page for patient's details"
<SelectableButton>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected else (1, 1, 1, 1)
Rectangle:
pos: self.pos
size: self.size
on_press: root.on_enter()

Kivy: Popup can have only one widget as content

I am having an issue with using a popup in my .kv file. I understand that a popup can only have one widget as it's content, however if I am only passing a GridLayout as a child that includes a Label and Button, shouldn't this work?
Here is my Python code:
import kivy, LabelB
from kivy.app import App
from kivy.graphics import Color, Rectangle
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.properties import ObjectProperty, StringProperty
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
Builder.load_file('main.kv')
class CustomPopup(Popup):
pass
class MenuScreen(Screen):
def open_popup(self):
the_popup = CustomPopup()
the_popup.open()
class SurveyScreen(Screen):
pass
sm = ScreenManager(transition=FadeTransition())
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SurveyScreen(name='survey'))
class MainApp(App):
def build(self):
return sm
if __name__ == '__main__':
MainApp().run()
Here is my .kv file:
<CustomPopup>:
title: 'Terms of Service'
size_hint: .5, .5
auto_dismiss: False
GridLayout:
cols: 1
Label:
size_hint: .9, .9
halign: 'center'
valign: 'middle'
text: 'Insert terms of service text here'
text_size: self.width, None
Button:
text: 'Close'
on_release: root.dismiss()
<MenuScreen>:
FloatLayout:
canvas.before:
Rectangle:
source: 'menu.png'
size: self.size
pos: self.pos
Label:
pos_hint: {'x': .7, 'y': .85}
text_size: self.size
font_name: 'Arial'
font_size: 26
text: 'Sample'
bold: True
Button:
text: 'Take Survey'
size_hint: .2, .1
pos_hint: {'x': .15, 'y': .1}
on_release:
root.manager.transition.duration = 0.5
root.manager.current = 'survey'
Button:
text: 'Terms of Service'
size_hint: .2, .1
pos_hint: {'x': .6-self.size_hint_x, 'y': .1}
on_release: root.open_popup()
Button:
text: 'Quit'
size_hint: .2, .1
pos_hint: {'x': .85-self.size_hint_x, 'y': .1}
on_release: app.stop()
<SurveyScreen>:
GridLayout:
cols: 1
padding: 20
spacing: 10
Label:
text: 'WELCOME!'
font_size: 20
Label:
text: 'Some boring text'
The error is as follows: 'Popup can have only one widget as content'
Am I missing something obvious here? Thanks in advance.
Yes, it should work as you say, your code is correct.
The problem is that the loading of the .kv file is duplicated. As your App subclass is called MainApp, main.kv is loaded automatically if it is in same directory (Doc: How to load kv). On the other hand, you explicitly upload the file using Builder.load_file ('main.kv').
You must remove the call to Builder or rename MainApp/main.kv.
If you delete the call to Builder.load_file you must create the ScreenManager instance once the .kv is loaded. You can do something like:
class MainApp (App):
def build (self):
sm = ScreenManager (transition = FadeTransition ())
sm.add_widget (MenuScreen (name = 'menu'))
sm.add_widget (SurveyScreen (name = 'survey'))
return sm

Resources