I want to build an app with Kivy in Python but I got some errors that I tried to solve many times but I can't.
I'm opening a camera screen firstly. In screen, we see our webcam screen and there is 2 buttons at the bottom (Play and Capture). While I pressing Play, webcam is on and if I press Capture button, I'm taking snapshot. After taking snapshot, we are going to the screen which is located on the left. Special thanks to Erik , he built this working code below.
But when I capture the photo and we are in the left screen, I want to show some text (coming from ANOTHER py file for example basic hello1, hello2... whatever) in left screen.
I know that how can I print basically a sentence in just Console with only print function but I want to write many print function's results in my application on the left screen synchronously from ANOTHER py file.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
import time
class CheckScreen(Screen):
pass
class CameraClickScreen(Screen):
def capture(self):
camera = self.ids['camera']
timestr = time.strftime("%Y%m%d_%H%M%S")
camera.export_to_png("IMG_{}.png".format(timestr))
print("Captured")
GUI = Builder.load_string("""
GridLayout:
cols: 1
ScreenManager:
id: screen_manager
CameraClickScreen:
name: "camera_click_screen"
id: camera_click_screen
CheckScreen:
name: "check_screen"
id: check_screen
<CameraClickScreen>:
orientation: 'vertical'
GridLayout:
cols: 1
Camera:
id: camera
resolution: (640, 480)
play: False
ToggleButton:
text: 'Play'
on_press: camera.play = not camera.play
size_hint_y: None
height: '48dp'
Button:
text: 'Capture'
size_hint_y: None
height: '48dp'
on_press:
root.capture()
# root refers to <CameraClickScreen>
# app refers to TestCamera, app.root refers to the GridLayout: at the top
app.root.ids['screen_manager'].transition.direction = 'left'
app.root.ids['screen_manager'].current = 'check_screen'
<CheckScreen>:
Button:
text: "Next Screen"
font_size: 50
""")
class TestCamera(App):
def build(self):
return GUI
TestCamera().run()
Normally with this code, on the left screen, we see "Next Screen" sentence with a button. I want to see variable texts coming from py file. For example Hello 1, and clear the screen, Hello 2, and clear the screen, ..., Hello 5 and stop.
How can I add this basic print functions and integrate with app to show us on the left screen?
Related
I have an issue with an kv file and can't solve it. In the app, I use different screens and every screen should have the same menu bar. The app is provided for Android, but in the future I want to use it on Windows too. For Android, the menu bar should be at the bottom of the screen. For Windows, I want to place the menu bar at the top of the screens and there is my issue: the menu bar at the top doesn't work.
I've created a simple example code, to reproduce the issue.
Here is the a simple Python code. There is only one function to switch the screens:
import kivy
kivy.require('1.10.1')
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
class MenuBar( Button ):
pass
class Screen1( Screen ):
pass
class Screen2( Screen ):
pass
class ScreenManager( ScreenManager ):
pass
class TestscreenApp( App ):
def switch_screen( self, screen ):
self.root.current = screen
if __name__ == '__main__':
TestscreenApp().run()
And here is the kv file. There are 2 screens and a menu bar. I want to reuse the menu bar on different screens.
As you can see, the menu bar on the first screen is on the bottom of the screen. On the second screen, I placed the menu bar at the top and nothing will be displayed :-(
If I place the menu bar on the second screen at the bottom, it works. If I place the menu bar on the first screen at the top, this also display me nothing.
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
#:import SlideTransition kivy.uix.screenmanager.SlideTransition
ScreenManager:
id: screen_manager
Screen1:
name: "screen_1"
screen_manager: "screen_manager"
Screen2:
name: "screen_2"
screen_manager: "screen_manager"
<Screen1>:
name: "screen_1"
GridLayout:
rows: 2
GridLayout:
cols: 2
Button:
text: 'Test 1'
font_size: 50
Button:
text: 'Test 2'
font_size: 50
MenuBar:
<Screen2>:
name: "screen_2"
GridLayout:
rows: 2
MenuBar:
Button:
text: 'Test 3'
font_size: 50
on_release:
app.switch_screen( "screen_1" )
<MenuBar>:
name: "menu_bar_top"
GridLayout:
cols: 2
size: root.width, root.height
Button:
text: 'Menu 1'
font_size: 20
on_release:
app.switch_screen( "screen_1" )
Button:
text: 'Menu 2'
font_size: 20
on_release:
app.switch_screen( "screen_2" )
I tried many things and used the search, but I can't find anything similar. I also tried to use BoxLayout instead of GridLayout, but I can't find a solution.
What I am doing wrong? Can anyone explain me this strange behavior?
The problem is that you are defining the MenuBar as extending Button, then using it as a widget container. A Button is not intended to be used as a container. A better choice would be one of the Layout classes. Try changing:
class MenuBar( Button ):
pass
to:
class MenuBar( RelativeLayout ):
pass
You made my day! That is a solution for my problem.
I did not think to change my Python code. All the time I've changed my kv file to fix the issue.
Thanks
I'am new in Kivy and have follow problem (Environment is Python 3.7 with Kivy-1.11.1.):
I need a navigation area and a view area (=ViewScreen). With the navigation area i change the view area (change of kv-files - look later at 'def next_screen'). My problem is, that i can't interact with widgets (e.g. 'lblTest') in the view area.
I use follow files:
testGUI.py (= GUI Application)
testGUIRoot.kv (= RootWidget as kv-file)
testGUIBtn1.kv (= view area 1 as kv-file)
testGUIBtn2.kv (= view area 2 as kv-file)
The GUI Application is simple and starts the GUI and change the view area.
testGUI.py:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
class RootWidget(BoxLayout):
# runs select application
def startApplication (self, instance):
print(self.lblTest)
class mainGUI(App):
def build(self):
# loading the content of root.kv
self.root = Builder.load_file('testGUIRoot.kv')
def next_screen(self, screen):
#Clear container and load the given screen object from file in kv folder.
filename = screen + '.kv'
# unload the content of the .kv file
# reason: it could have data from previous calls
Builder.unload_file(filename)
# clear the container
self.root.container.clear_widgets()
# load the content of the .kv file
screen = Builder.load_file(filename)
# add the content of the .kv file to the container
self.root.container.add_widget(screen)
if __name__ == '__main__':
'''Start the application'''
mainGUI().run()
I use follow kv-files:
testGUIRoot.kv:
#:kivy 1.11.1
RootWidget:
container: container
orientation: 'horizontal'
# Navigation
BoxLayout:
orientation: 'vertical'
size_hint: (0.35, 1)
Button:
text: 'testButton1'
on_release: root.startApplication(self,)
on_press: app.next_screen('testGUIBtn1')
Button:
text: 'testButton2'
on_release: root.startApplication(self,)
on_press: app.next_screen('testGUIBtn2')
# ViewScreen
BoxLayout:
size_hint: (0.65, 1)
id: container
orientation: 'vertical'
padding: 0
spacing: 3
Label:
text: 'no data'
color: (0.667,0.667,0.667,1)
font_size: 14
bold: True
testGUIBtn1.kv:
#:kivy 1.11.1
Label:
id: lblTest
text: 'Button 1'
color: (0.667,0.667,0.667,1)
font_size: 14
bold: True
testGUIBtn2.kv:
#:kivy 1.11.1
Label:
id: lblTest
text: 'Button 2'
color: (0.667,0.667,0.667,1)
font_size: 14
bold: True
Follow error appears:
AttributeError: 'RootWidget' object has no attribute 'lblTest'
Have you a solution to interact with the Object 'lblTest'? For example like self.lblTest.text = 'Test-Text'.
Thank you in advance
I have been working with Kivy just this week, also as a new user.
The thing I have learned is to define properties on your RootWidget, just as the labels you already defined in the .kv files. This 'links' the layout and the Python code to eachother.
First of all you need to import the Kivy ObjectProperty by adding:
from kivy.properties import ObjectProperty
Next up is to declare the property on your RootWidget class.
You can add lblTest = ObjectProperty(None) right after the class-declaration.
The top of your file testGUI.py should look like this then:
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout
class RootWidget(BoxLayout):
# Link the layout objects
lblTest = ObjectProperty(None)
# runs select application
def startApplication (self, instance):
print(self.lblTest)
The page that really helped me with this is https://techwithtim.net/tutorials/kivy-tutorial/object-properties/
A little sidenote is it would be best to keep your id attributes fully unique.
I found the solution
Content of 'def next_screen' of mainGUI(App) to 'def startApplication'. Now i can change widgets in object oScreen or using the object in other python libs.
def startApplication (self, instance, sScreen):
filename = sScreen + '.kv'
# unload the content of the .kv file
# reason: it could have data from previous calls
Builder.unload_file(filename)
# clear the container
self.container.clear_widgets()
# load the content of the .kv file
oScreen = Builder.load_file(filename)
# add the content of the .kv file to the container
self.container.add_widget(oScreen)
print(oScreen.ids.lblTest.text)
The follow should be added in kv-files testGUIBtn1.kv, testGUIBtn2.kv:
RootWidget:
In kv-file testGUIRoot.kv you have change on_release to
on_release: root.startApplication(self,'testGUIBtn1')
This is camera code
Camera Example
==============
This example demonstrates a simple use of the camera. It shows a window with
a buttoned labelled 'play' to turn the camera on and off. Note that
not finding a camera, perhaps because gstreamer is not installed, will
throw an exception during the kv language processing.
'''
# Uncomment these lines to see all the messages
# from kivy.logger import Logger
# import logging
# Logger.setLevel(logging.TRACE)
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
import time
Builder.load_string('''
<CameraClick>:
orientation: 'vertical'
Camera:
id: camera
resolution: (640, 480)
play: False
ToggleButton:
text: 'Play'
on_press: camera.play = not camera.play
size_hint_y: None
height: '48dp'
Button:
text: 'Capture'
size_hint_y: None
height: '48dp'
on_press: root.capture()
''')
class CameraClick(BoxLayout):
def capture(self):
'''
Function to capture the images and give them the names
according to their captured time and date.
'''
camera = self.ids['camera']
#timestr = time.strftime("%Y%m%d_%H%M%S")
camera.export_to_png("IMG_{}.png".format(timestr))
print("Captured")
class TestCamera(App):
def build(self):
return CameraClick()
TestCamera().run()
I want to save captured image in my current directory with a name like img.png instead of saving image name with a timestamp.
This code saves image with name like IMG_20200216_162830 and I want it like IMG.png
Can anyone help me?
Thank you
Remove the timestamp, that's all:
camera.export_to_png("IMG.png")
I am expecting the following kivy app to switch screens when I press and then release the button, but nothing happens and there's no error on the terminal. When I run the app, the GirisEkrani screen show up, and then when I press and release the button in GirisEkrani, the next screen (GirisEkrani2) should show up. Do you how to make that work?
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<ekranApp>
GirisEkrani:
id: ge
Button:
text: "İleri"
on_release: ge.manager.current = ge.manager.next()
GirisEkrani2:
Button:
id: ge2
text: "Ileri 2"
on_release: ge2.manager.current = ge2.manager.next()
KontrolEkrani:
id: ke
Button:
text: "Geri"
on_release: ke.manager.current = ke.manager.previous()
""")
class GirisEkrani(Screen):
pass
class GirisEkrani2(Screen):
pass
class KontrolEkrani(Screen):
pass
class ekranApp(App, ScreenManager):
def build(self):
#root = ScreenManager()
#root.add_widget(GirisEkrani(name = "giris_ekrani"))
#root.add_widget(GirisEkrani2(name = "giris_ekrani2"))
#root.add_widget(KontrolEkrani(name = "kontrol_ekrani"))
return self
if __name__ == "__main__":
ekranApp().run()
While people seem to advocate the use of .kv files as opposed to using pure Python, I find it very frustrating to have no errors showing up when something doesn't work.
The build() method of an App is expected to return a Widget which will be the root of the Widget tree for your App, but yours returns the App itself. I suspect that will cause problems. However, your code is not working because you are not setting the names of the Screens. Here is a corrected version of your kv:
Builder.load_string("""
<ekranApp>:
GirisEkrani:
id: ge
name: "giris_ekrani"
Button:
text: "İleri"
on_release: ge.manager.current = ge.manager.next()
GirisEkrani2:
id: ge2
name: "giris_ekrani2"
Button:
text: "Ileri 2"
on_release: ge2.manager.current = ge2.manager.next()
KontrolEkrani:
id: ke
name: "kontrol_ekrani"
Button:
text: "Geri"
on_release: ke.manager.current = ke.manager.previous()
""")
Also, your id for GirisEkrani2 is actually set on the Button.
I'm trying to create an app with 2 pages. I would like to switch between the two pages using a button: when a button is pressed the page switches. My code does not seem to work. I might be missing something.
How can I achieve this?
This is the code I am working with:
<TrafGridLayout>:
PageLayout:
BoxLayout:
orientation: 'vertical'
BoxLayout:
TextInput:
text: ''
BoxLayout:
TextInput:
text: ''
# Calculate and show page #2
BoxLayout:
Button:
text: "Calculate"
on_press:
traffictax.calculate(point_from.text, point_to.text)
traffictax.show_page(1)
BoxLayout:
orientation: 'vertical'
# Show page #1
BoxLayout:
Button:
text: "Back to first page"
on_press: traffictax.show_page(0)
Question 1
My code does not seem to work. I might be missing something.
Problem
Running the app with the code provided, displayed a small window at the bottom left hand corner.
Root Cause
The class rule, <TrafGridLayout>: is missing either cols or rows attribute.
In PageLayout, transitions from one page to the next are made by swiping in from the border areas on the right or left hand side. Therefore, it is not a best practice to use Button widget's on_press event for this.
Kivy GridLayout » constraint cols or rows
A GridLayout must always have at least one input constraint:
GridLayout.cols or GridLayout.rows.
Kivy » PageLayout
The PageLayout class is used to create a simple multi-page
layout, in a way that allows easy flipping from one page to another
using borders.
Transitions from one page to the next are made by swiping in from the
border areas on the right or left hand side.
Solution - kv
Add cols: 1 to class rule, <TrafGridLayout>:
Snippets
<TrafGridLayout>:
cols: 1
PageLayout:
Output
Question 2
I would like to switch between the two pages using a button: when a
button is pressed the page switches.
Solution
Use Kivy ScreenManager and button pressed to switch page.
Example
The following example illustrates the use of Kivy ScreenManager and one of button's events, on_press to switch page / screen.
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
class ScreenManagement(ScreenManager):
pass
class FirstPage(Screen):
def calculate(self, point_from, point_to):
print("point_from={0}, point_to={1}".format(point_from, point_to))
class SecondPage(Screen):
pass
class TestApp(App):
title = "Login Screen"
def build(self):
return ScreenManagement()
if __name__ == "__main__":
TestApp().run()
test.kv
#:kivy 1.10.0
<ScreenManagement>:
FirstPage:
id: page1
SecondPage:
<FirstPage>:
name: "first_page"
BoxLayout:
orientation: 'vertical'
BoxLayout:
Label:
text: "Point From:"
TextInput:
id: point_from
text: ''
BoxLayout:
Label:
text: "Point To:"
TextInput:
id: point_to
text: ''
# Calculate and show page #2
BoxLayout:
Button:
text: "Calculate"
on_press:
root.calculate(point_from.text, point_to.text)
root.manager.current = "second_page"
<SecondPage>:
name: "second_page"
BoxLayout:
orientation: 'vertical'
# Show page #1
BoxLayout:
Button:
text: "Back to first page"
on_press: root.manager.current = "first_page"
Output