im traying to do a progress bar in kivy, i can build the app with it but it can't be udpated when i run a function (loop) how can i do this please ?
here is my code :
import libraries :
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.properties import ObjectProperty
from kivy.core.window import Window
from kivy.clock import Clock
from kivy.logger import Logger
from kivy.uix.progressbar import ProgressBar
My builder :
Builder.load_string("""
<RootWidget>:
pb : pb
some code here
ActionView:
ActionPrevious:
with_previous: False
ActionButton:
text: 'Run analysis'
color: 29/255, 185/255, 84/255, 1
on_release: root.clicked()
BoxLayout:
ProgressBar:
id : pb
min :0
max :1
pos_hint: {'x': .1}
size_hint_x :.8
size_hint_y :.5
value: 0
""")
other code with my update_bar function:
class Myapp(BoxLayout):
pb = ProgressBar()
box = ObjectProperty()
def __init__(self, *args, **kwargs):
super(Myapp, self).__init__(*args, **kwargs)
def update_bar(self,dt=None):
self.ids.pb.value = self.ids.pb.value
here is the problem : the progress bar is not updated each iteration, but it take the last value (for i = 9999)
def clicked(self):
for i in range(10000) :
self.ids.pb.value = i/10000
Clock.schedule_interval(self.update_bar,0.5)
self.update_bar()
Build my app :
class EventScreen(App,Myapp):
def on_stop(self):
Logger.critical("Good bye")
def build(self):
return Myapp()
if __name__ == "__main__":
EventScreen().run()
thank you in advance
Use Clock.create_trigger() to update progress bar.
Snippet
def __init__(self, *args, **kwargs):
super(Myapp, self).__init__(*args, **kwargs)
self.update_bar_trigger = Clock.create_trigger(self.update_bar)
def clicked(self):
self.i = 0
self.update_bar_trigger()
def update_bar(self, dt):
if self.i <= 10000:
self.ids.pb.value += self.i
self.i += 1
self.update_bar_trigger()
Related
I am extremely new to kivy (and making apps in general), and I am having trouble switching between screens. I have watched several tutorials, but one of them uses a build method that returns a MDBoxLayout (CameraScreen), and the others use .kv files. I want to be able to switch between the two, or be able to reformat it so that I only use .kv files. Here is my code:
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDRaisedButton
from kivymd.uix.label import MDLabel
from kivy.uix.screenmanager import Screen
from kivy.uix.image import Image
from kivy.lang import Builder
from kivy.graphics.texture import Texture
from kivy.clock import Clock
import cv2
import pytesseract
class HomeScreen(Screen):
pass
class ManageDataScreen(Screen):
pass
class CameraScreen(MDApp):
def build(self):
layout = MDBoxLayout(orientation='vertical')
self.image = Image()
self.label = MDLabel()
layout.add_widget(self.image)
layout.add_widget(self.label)
self.save_img_button = MDRaisedButton(
text="Capture",
pos_hint={'center_x': .5, 'center_y': .5},
size_hint=(None, None))
self.save_img_button.bind(on_press=self.take_picture)
layout.add_widget(self.save_img_button)
self.capture = cv2.VideoCapture(0)
Clock.schedule_interval(self.load_video, 1.0/30.0)
return layout
def load_video(self, *args):
ret, frame = self.capture.read()
# Frame initialize
self.image_frame = frame
buffer = cv2.flip(frame, 0).tostring()
texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt='bgr')
texture.blit_buffer(buffer, colorfmt='bgr', bufferfmt='ubyte')
self.image.texture = texture
def take_picture(self, *args):
image_name = "img_TIMESTAMP.png"
img = cv2.cvtColor(self.image_frame, cv2.COLOR_BGR2GRAY)
img = cv2.GaussianBlur(img, (3, 3), 0)
img = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
text_data = pytesseract.image_to_string(img, lang='eng', config="--psm 6")
self.label.text = text_data
cv2.imshow("cv2 final image", img)
cv2.imwrite(image_name, self.image_frame)
GUI = Builder.load_file("kv/main.kv")
class MainApp(MDApp):
def build(self):
return GUI
def change_screen(self, screen_name):
screen_manager = self.root.ids['screen_manager']
if screen_name == "camera_screen":
CameraScreen().run()
else:
screen_manager.current = screen_name
if __name__ == '__main__':
pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"
MainApp().run()
and here is my main.kv file
#:include kv/home_screen.kv
#:include kv/manage_data_screen.kv
GridLayout:
cols: 1
ScreenManager:
id: screen_manager
HomeScreen:
name: "home_screen"
id: home_screen
ManageDataScreen:
name: "manage_data_screen"
id: manage_data_screen
I am trying to use DragBehavior to help in moving my custom widget across RelativeLayout. Find below sample code. Why my widget is not moving on Drag action please. For simplicity I had included only rectangle in my custom widget MyPaintWidget
from kivy.app import App
from kivy.graphics import Line
from kivy.uix.scatter import Scatter
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.behaviors import DragBehavior
from kivy.lang import Builder
from kivy.graphics import Color, Rectangle
Builder.load_string("""
<MyPaintWidget>:
# Define the properties for the DragLabel
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 10000000
drag_distance: 0
""")
class MyPaintWidget(DragBehavior, Scatter):
def __init__(self, **kwargs) :
self.selected = None
self.touched = False
super(MyPaintWidget, self).__init__(**kwargs)
def create_figure(self, **kwargs):
print ('position is {}'.format(self.pos))
print ('width Height {}'.format(self.to_parent(self.width, self.height)))
self.canvas.add(Rectangle(pos = self.pos, size = self.size))
return self
def on_touch_move(self, touch):
print('Started to move x: {} y: {}'.format(touch.x, touch.y))
return super(MyPaintWidget, self).on_touch_move(touch)
class MyPaintApp(App):
def build(self):
parent = RelativeLayout()
self.painter = MyPaintWidget(pos_hint={"center_x": 0.5, 'center_y':0.5}, size_hint=(.2,.1))
parent.add_widget(self.painter.create_figure())
return parent
if __name__ == '__main__':
MyPaintApp().run()
The DragBehavior works by adjusting the pos of your MyPaintWidget, but you have set pos_hint on the MyPaintWidget. The pos_hint takes precedence over pos, so while the drag changes pos, it is ignored because there is a pos_hint. Also, the Rectangle that you draw in create_figure has its size and pos set when that method is called, and there is no mechanism to change it when the MyPaintWidget is moved. So, even if the Widget was being dragged, the Rectangle would not move.
Here is a version of your code with those problems corrected:
from kivy.app import App
from kivy.uix.scatter import Scatter
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.behaviors import DragBehavior
from kivy.lang import Builder
Builder.load_string("""
<MyPaintWidget>:
# Define the properties for the DragLabel
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 10000000
drag_distance: 0
canvas:
Color:
rgba: 1,0,0,1
Rectangle:
pos: 0,0 # only do this for RelativeLayout
size: self.size
""")
class MyPaintWidget(DragBehavior, Scatter):
def __init__(self, **kwargs) :
self.selected = None
self.touched = False
super(MyPaintWidget, self).__init__(**kwargs)
def on_touch_move(self, touch):
print('Started to move x: {} y: {}'.format(touch.x, touch.y))
return super(MyPaintWidget, self).on_touch_move(touch)
class MyPaintApp(App):
def build(self):
parent = RelativeLayout()
self.painter = MyPaintWidget( pos=(240, 200), size_hint=(.2,.1))
parent.add_widget(self.painter)
return parent
if __name__ == '__main__':
MyPaintApp().run()
I am trying my hands on ScrollView within anchorlayout. Unable to get scroll bar in Relativelayout. Please find below my sample code. Why scrollbar is not visible.
import kivy
from kivy.app import App
from kivy.uix.scrollview import ScrollView
from kivy.lang import Builder
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.button import Button
from kivy.factory import Factory
from kivy.core.window import Window
Builder.load_string('''
<ScrollView>:
size_hint: 1,1
DrawingSpace:
size_hint: 1,1
''')
class Main(App):
def build(self):
self.root=DrawingSpace()
return self.root
class DrawingSpace(RelativeLayout):
def __init__(self, **kwargs):
super(RelativeLayout, self).__init__(**kwargs)
self.add_widget( Button(text = 'Scroll', size_hint=(None,None), size=(50,200), pos = (100, 100)))
self.add_widget( Button(text = 'Scroll', size_hint=(None,None), size=(50,200), pos = (250, 300)))
Factory.register('DrawingSpace', cls=DrawingSpace)
if __name__ in ('__main__'):
Window.size = (200, 200)
app = Main()
app.run()
Thanks
For ScrollView, in the direction you want scrolling, you have to set size_hint to None and specify the size. I used 500 for testing.
import kivy
from kivy.app import App
from kivy.uix.scrollview import ScrollView
from kivy.lang import Builder
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.button import Button
from kivy.factory import Factory
from kivy.core.window import Window
Builder.load_string('''
<ScrollView>:
size_hint: 1,1
DrawingSpace:
size_hint: 1,None
height: 500
''')
class Main(App):
def build(self):
self.root=ScrollView()
return self.root
class DrawingSpace(RelativeLayout):
def __init__(self, **kwargs):
super(RelativeLayout, self).__init__(**kwargs)
self.add_widget( Button(text = 'Scroll', size_hint=(None,None), size=(50,200), pos = (100, 100)))
self.add_widget( Button(text = 'Scroll', size_hint=(None,None), size=(50,200), pos = (250, 300)))
Factory.register('DrawingSpace', cls=DrawingSpace)
if __name__ in ('__main__'):
Window.size = (200, 200)
app = Main()
app.run()
How can make a menu with two or more options when I click on FileBox object in below code? Plesae have a look on the attached pic if you don't know what I mean.
CODE
from kivy.config import Config
Config.set('graphics', 'multisamples', '0')
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
from kivy.lang.builder import Builder
from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.boxlayout import BoxLayout
kv = """
<FileBox>
size_hint: None, None
size: 100, 100
canvas.before:
Color:
rgba: (0.337, 0.760, 0.968, 1)
Rectangle:
pos: self.pos
size: self.size
RelativeLayout:
FileBox:
pos_hint: {'center_x':0.5, 'center_y': 0.5}
"""
class FileBox(BoxLayout):
def __init__(self, **kwargs):
super(FileBox, self).__init__(**kwargs)
self.oryg_text = ''
def on_touch_down(self, touch):
if touch.button == 'right':
print('DROP A MENU\n Delete \n Copy')
def function_called_from_drop_menu(self, choosed):
print('CHOOSED', choosed)
sm = Builder.load_string(kv)
class NewApp(App):
def build(self):
self.title = 'Drop Menu'
return sm
if __name__ == '__main__':
NewApp().run()
John must have answered while I was looking at the question. John's answer is perfectly reasonable. My answer is a bit different because it depends on Popup.
from kivy.config import Config
Config.set('graphics', 'multisamples', '0')
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
from kivy.lang.builder import Builder
from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.core.window import Window
kv = """
<FileBox>
size_hint: None, None
size: 100, 100
canvas.before:
Color:
rgba: (0.337, 0.760, 0.968, 1)
Rectangle:
pos: self.pos
size: self.size
RelativeLayout:
FileBox:
pos_hint: {'center_x':0.5, 'center_y': 0.5}
<MenuPopup>:
BoxLayout:
orientation: "vertical"
Button:
text: "Delete"
on_press: print("Delete")
Button:
text: "Copy"
on_press: print("Copy")
"""
class MenuPopup(Popup):
pass
class FileBox(BoxLayout):
def __init__(self, **kwargs):
super(FileBox, self).__init__(**kwargs)
self.oryg_text = ''
def on_touch_down(self, touch):
print(touch.pos)
if touch.button == 'right':
print('DROP A MENU\n Delete \n Copy')
popup = MenuPopup(title='Menu',
auto_dismiss=True,
size_hint=(None, None),
size=(180, 180),
pos_hint={'x': touch.pos[0] / Window.width,
'y':(touch.pos[1] - self.height)/ Window.height})
popup.open()
def function_called_from_drop_menu(self, choosed):
print('CHOOSED', choosed)
sm = Builder.load_string(kv)
class NewApp(App):
def build(self):
self.title = 'Drop Menu'
return sm
if __name__ == '__main__':
NewApp().run()
Update
If you don't like the frame by Popup you can also use the class on which Popup is based. It is called ModalView. You also need to delete the title since ModalView has no title.
from kivy.config import Config
Config.set('graphics', 'multisamples', '0')
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
from kivy.lang.builder import Builder
from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.modalview import ModalView
from kivy.uix.label import Label
from kivy.core.window import Window
kv = """
<FileBox>
size_hint: None, None
size: 100, 100
canvas.before:
Color:
rgba: (0.337, 0.760, 0.968, 1)
Rectangle:
pos: self.pos
size: self.size
RelativeLayout:
FileBox:
pos_hint: {'center_x':0.5, 'center_y': 0.5}
<MenuPopup>:
BoxLayout:
orientation: "vertical"
Button:
text: "Delete"
on_press: print("Delete")
Button:
text: "Copy"
on_press: print("Copy")
"""
class MenuPopup(ModalView):
pass
class FileBox(BoxLayout):
def __init__(self, **kwargs):
super(FileBox, self).__init__(**kwargs)
self.oryg_text = ''
def on_touch_down(self, touch):
print(touch.pos)
if touch.button == 'right':
print('DROP A MENU\n Delete \n Copy')
popup = MenuPopup(
auto_dismiss=True,
size_hint=(None, None),
size=(180, 180),
pos_hint={'x': touch.pos[0] / Window.width,
'y':(touch.pos[1] - self.height)/ Window.height})
popup.open()
def function_called_from_drop_menu(self, choosed):
print('CHOOSED', choosed)
sm = Builder.load_string(kv)
class NewApp(App):
def build(self):
self.title = 'Drop Menu'
return sm
if __name__ == '__main__':
NewApp().run()
Typically, a DropDown is attached to a Button, but that is not necessary. You can create the DropDown as described in the documentation and instead of binding it to a Button to open it, you can just call open() in your on_touch_down() method. Here is a modified version of your code that does that:
from kivy.config import Config
from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown
Config.set('graphics', 'multisamples', '0')
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
from kivy.lang.builder import Builder
from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.boxlayout import BoxLayout
kv = """
<FileBox>
size_hint: None, None
size: 100, 100
canvas.before:
Color:
rgba: (0.337, 0.760, 0.968, 1)
Rectangle:
pos: self.pos
size: self.size
RelativeLayout:
FileBox:
pos_hint: {'center_x':0.5, 'center_y': 0.5}
"""
class FileBox(BoxLayout):
def __init__(self, **kwargs):
super(FileBox, self).__init__(**kwargs)
self.oryg_text = ''
def on_touch_down(self, touch):
if touch.button == 'right':
print('DROP A MENU\n Delete \n Copy')
# create the DropDown
self.dropdown = DropDown(auto_dismiss=False)
# add Buttons
btn = Button(text='Delete', size_hint_y=None, height=44)
btn.bind(on_release=self.function_called_from_drop_menu)
self.dropdown.add_widget(btn)
btn = Button(text='Copy', size_hint_y=None, height=44)
btn.bind(on_release=self.function_called_from_drop_menu)
self.dropdown.add_widget(btn)
# open the DropDown
self.dropdown.open(self)
def function_called_from_drop_menu(self, choosed):
print('CHOOSED', choosed)
# dismiss the DropDown
self.dropdown.dismiss()
sm = Builder.load_string(kv)
class NewApp(App):
def build(self):
self.title = 'Drop Menu'
return sm
if __name__ == '__main__':
NewApp().run()
I have encountered the following problem:
The example (mainok.py, testok.kv) successfully starts,
$ cat mainok.py
from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout
class BuddyList(BoxLayout):
list_view = ObjectProperty()
def __init__(self, **kwargs):
super(BuddyList, self).__init__(**kwargs)
assert(self.list_view is not None)
self.list_view.adapter.data = ['v1', 'v2']
def roster_converter(self, index, txt):
result = {
"text": "%s %d" % (txt, index),
'size_hint_y': None,
'height': 25
}
return result
class TestForm(BoxLayout):
text_input = ObjectProperty()
def __init__(self, **kwargs):
super(TestForm, self).__init__(**kwargs)
self.buddy_list = BuddyList()
self.add_widget(self.buddy_list)
class TestokApp(App):
pass
if __name__ == '__main__':
TestokApp().run()
$ cat testok.kv
#:import la kivy.adapters.listadapter
#:import ku kivy.uix.listview
TestForm:
<BuddyList>:
list_view: list_view
ListView:
id: list_view
adapter:
la.ListAdapter(data=[], cls=ku.ListItemButton,
args_converter=root.roster_converter)
<TestForm>:
orientation: 'vertical'
BoxLayout:
Label:
text: 'the label'
The example (mainko.py, testko.kv) crashes:
$ cat mainko.py
from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout
class BuddyList(BoxLayout):
list_view = ObjectProperty()
def __init__(self, **kwargs):
super(BuddyList, self).__init__(**kwargs)
assert(self.list_view is not None)
self.list_view.adapter.data = ['v1', 'v2']
def roster_converter(self, index, txt):
result = {
"text": "%s %d" % (txt, index),
'size_hint_y': None,
'height': 25
}
return result
class TestForm(BoxLayout):
text_input = ObjectProperty()
def __init__(self, **kwargs):
super(TestForm, self).__init__(**kwargs)
class TestkoApp(App):
pass
if __name__ == '__main__':
TestkoApp().run()
$ cat testko.kv
#:import la kivy.adapters.listadapter
#:import ku kivy.uix.listview
TestForm:
<BuddyList>:
list_view: list_view
ListView:
id: list_view
adapter:
la.ListAdapter(data=[], cls=ku.ListItemButton,
args_converter=root.roster_converter)
<TestForm>:
orientation: 'vertical'
BoxLayout:
Label:
text: 'the label'
BuddyList:
Crash with the following error
assert(self.list_view is not None)
AssertionError
Differences between the 2 are:
$ diff -u mainko.py ../ok/mainok.py
--- mainko.py 2013-10-23 14:11:26.976723764 +0200
+++ ../ok/mainok.py 2013-10-23 14:12:34.976841090 +0200
## -26,10 +26,13 ##
def __init__(self, **kwargs):
super(TestForm, self).__init__(**kwargs)
+ self.buddy_list = BuddyList()
+ self.add_widget(self.buddy_list)
-class TestkoApp(App):
+
+class TestokApp(App):
pass
if __name__ == '__main__':
- TestkoApp().run()
+ TestokApp().run()
$ diff -u testko.kv ../ok/testok.kv
--- testko.kv 2013-10-23 14:10:11.948299722 +0200
+++ ../ok/testok.kv 2013-10-23 14:16:51.352688453 +0200
## -16,5 +16,4 ##
BoxLayout:
Label:
text: 'the label'
- BuddyList:
Any idea why the first one succeeds and the second fails?
Thanks,
It looks like the problem is that the __init__ method doesn't actually fully parse and setup the kv instructions, though I'm not sure of the details - I guess it leaves some stuff scheduled in the eventloop. When you try to check self.list_view, you get None because this hasn't been set yet.
If you use the clock, you can arrange to carry out your post_init stuff after everything else scheduled so far (and so after the kv instructions have been fully carried out) but before the next frame. Here's a modification to your mainko.py doing such a thing. It seems to work for me.
from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
class BuddyList(BoxLayout):
list_view = ObjectProperty()
def __init__(self, **kwargs):
super(BuddyList, self).__init__(**kwargs)
Clock.schedule_once(self.post_init, 0)
def post_init(self, *args):
assert self.list_view is not None
self.list_view.adapter.data = ['v1', 'v2']
def roster_converter(self, index, txt):
result = {
"text": "%s %d" % (txt, index),
'size_hint_y': None,
'height': 25
}
return result
class TestForm(BoxLayout):
text_input = ObjectProperty()
def __init__(self, **kwargs):
super(TestForm, self).__init__(**kwargs)
class TestkoApp(App):
pass
if __name__ == '__main__':
TestkoApp().run()