I am kind of new to kivy and i am trying to display time in it
Here is the python code snippet(removed the unnecessary screens/parts for arbitrary reasons):
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
import time
from kivy.uix.label import Label
class MyScreen(Screen):
def update(self, *args):
self.timeb.text = time.asctime()
return time.asctime()
class MyApp(App):
def build(self):
x=MyScreen()
root = ScreenManager()
Clock.schedule_interval(x.update, 1)
root.add_widget(password(name='Screen0'))
root.add_widget(Correct(name='Screena'))
root.add_widget(MyScreen(name='Screen1'))
s=[x,root]
for i in s:
return i
if __name__ == '__main__':
MyApp().run()
.kv file(removed the unnecessary screens/parts for arbitrary reasons):
MyScreen:
<MyScreen>: #
timeb:time_box
BoxLayout:
orientation: "horizontal"
pos_hint: {'top':1}
height: "40dp"
size_hint_y: None
Label:
id:time_box
text:root.update()
size_hint_x: 6
font_size:30
font_name:"Roboto-Light.ttf"
As you can see in the code i have added a few screens but the My screen is the first to come up also if i change the
s=[x,root]
for i in s:
return i
to just
return root
then the time doesn't update itself.
Could anyone help?
Thanks!
Maybe you forgot to type something, most probably your kv is coded wrong. Why is there that hanging MyScreen: and what is it assigned to? You need a main rule somewhere, but I see none. I assigned your MyScreen: to <MyApp>: as a normal return MyScreen() or return some_scrmanager would do and it works.
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.clock import Clock
import time
from kivy.uix.label import Label
from kivy.lang import Builder
Builder.load_string('''
<MyApp>:
MyScreen:
<MyScreen>: #
timeb:time_box
BoxLayout:
orientation: "horizontal"
pos_hint: {'top':1}
height: "40dp"
size_hint_y: None
Label:
id:time_box
text:root.update()
size_hint_x: 6
font_size:30
''')
class MyScreen(Screen):
def update(self, *args):
self.timeb.text = time.asctime()
return time.asctime()
class MyApp(App):
def build(self):
x=MyScreen()
root = ScreenManager()
Clock.schedule_interval(x.update, 1)
root.add_widget(MyScreen(name='Screen1'))
s=[x,root]
for i in s:
return i
if __name__ == '__main__':
MyApp().run()
And the same kv, but more reasonable python code would looke like this:
<ScreenMgr>:
MyScreen:
<MyScreen>:
...
class MyScreen(Screen):
def __init__(self, **kw):
super(MyScreen, self).__init__(**kw)
Clock.schedule_interval(self.update, 1)
def update(self, *args):
self.timeb.text = time.asctime()
return time.asctime()
class ScreenMgr(ScreenManager):
pass
class MyApp(App):
def build(self):
x=MyScreen()
root = ScreenMgr()
root.add_widget(MyScreen(name='Screen1'))
return root
if __name__ == '__main__':
MyApp().run()
Because there's no need to call update() from Screen class in the building function and even outside a class where you want to use it presuming it'll run forever(Do you plan to stop time?).
Related
imports:
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import StringProperty
from kivy.uix.textinput import TextInput
kv = '''
BoxLayout:
orientation: 'vertical'
TextInput:
id: t1
TextInput:
id: t2
TextInput:
id: t3
TextInput:
id: t4
'''
MyApp class:
class MyApp(App):
text = StringProperty('-.text')
def build(self):
return Builder.load_string(kv)
def on_pre_enter(self):
self.ids['t1'].text = "textinput1"
def on_enter(self):
self.ids['t2'].text = "textinput2"
def on_pre_enter(self):
self.ids['t3'].text = "textinput3"
def on_enter(self):
self.ids['t4'].text = "textinput4"
if __name__ == '__main__':
MyApp().run()
This is nothing to do with Kivy, it's simply how Python works that writing a second method with the same name replaces the previous definition.
In below code I need the button on the edit_button_tab to switch to edit_input_tab. I really need to switch it that way as I need to switch between predefined classes EditButton and EditInput. This is a part of a bigger program with few Buttons in different location of a layout and I cant define them within <MainTabbedPanel> class. I've tried many ways to call switch_to (example in the quotes) but they didn't work.
CODE
from kivy.animation import Animation
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelStrip
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelHeader
from kivy.factory import Factory
theRoot = """
#:import Factory kivy.factory.Factory
<EditButton>
orientation: 'vertical'
Button:
text: 'Switch to Edit Screen'
on_press: root.change_tab('edit_screen')
<EditInput>
orientation: 'vertical'
TextInput:
<UnCloseableHeader>
color: 0,0,0,1
disabled_color: self.color
# variable tab_width
text: 'tabx'
size_hint_x: None
width: self.texture_size[0] + 40
BoxLayout:
pos: root.pos
size_hint: None, None
size_y: 20
padding: 3
Label:
id: lbl
text: root.text
<MainTabbedPanel#BoxLayout>
size_hint: (1, 1)
default_tab: edit_button_tab
tab_width: 130
FloatLayout:
EditButton:
id: edit_button
EditInput:
id: edit_input
UnCloseableHeader:
id: edit_button_tab
text: 'Edit'
content: edit_button.__self__
UnCloseableHeader:
id: edit_input_tab
text: 'Edit Tab'
content: edit_input.__self__
MainTabbedPanel:
"""
class EditInput(BoxLayout):
def __init__(self, **kwargs):
super(EditInput, self).__init__(**kwargs)
class EditButton(BoxLayout):
def __init__(self, **kwargs):
super(EditButton, self).__init__(**kwargs)
def change_tab(self, tab):
print('TAB', tab)
#call switch method from MainTabbedPanel
'''the way I've tried
mtp = MainTabbedPanel
mtp.switch_to('edit_input_tab')'''
class MainTabbedPanel(TabbedPanel):
def __init__(self, **kwargs):
super(MainTabbedPanel, self).__init__(**kwargs)
def switch(self, tab):
print("SWITCH TO", tab, self.ids.keys())
self.switch_to(self.ids[tab])
class UnCloseableHeader(TabbedPanelHeader):
pass
Factory.register('UnCloseableHeader', cls=UnCloseableHeader)
sm = Builder.load_string(theRoot)
class TabbedPanelApp(App):
def build(self):
return sm
if __name__ == '__main__':
TabbedPanelApp().run()
EDIT
I've tried with below snippet. It prints IDS of MainTabbedPanel but does not change the tabs.
class EditButton(BoxLayout):
def __init__(self, **kwargs):
super(EditButton, self).__init__(**kwargs)
def change_tab(self, tab):
print('TAB', tab)
MainTabbedPanel.tab = tab
MainTabbedPanel()
#call switch method from MainTabbedPanel
'''the way I've tried
mtp = MainTabbedPanel
mtp.switch_to('edit_input_tab')'''
class MainTabbedPanel(TabbedPanel):
tab = ''
def __init__(self, **kwargs):
super(MainTabbedPanel, self).__init__(**kwargs)
self.tabs_showing = True
if self.tab != '':
Clock.schedule_once(self.switch)
def switch(self, dt):
print("SWITCH TO", self.tab, self.ids.keys())
self.switch_to(self.ids[self.tab])
Use App.get_running_app() to get an instance of your app
Use root to get an instance of your root
Snippets
def change_tab(self, tab):
print('TAB', tab)
mtp = App.get_running_app().root
mtp.switch_to(mtp.ids.edit_input_tab)
Notes
In your kv, you defined a Dynamic class,
<MainTabbedPanel#BoxLayout>. It should be a class rule,
<MainTabbedPanel> because in your Python code, you have defined
class MainTabbedPanel(TabbedPanel): i.e. inheritance mismatch and
class type mismatch.
There is an example of how to bind and unbind files drag&drop from system into Kivy app.
(Perhaps this can be done better, but now it is important that it works)
There are 2 buttons, the left one (roughly speaking) enables files dropping, and the right one - turns this function off.
from kivy.core.window import Window
from kivy.lang import Builder
KV = '''
BoxLayout:
Button:
text: 'bind dropfile'
on_press: app.bind_dropfile()
Button:
text: 'unbind dropfile'
on_press: app.unbind_dropfile()
'''
class DropFileApp(App):
def build(self):
self.root = Builder.load_string(KV)
def manage_dropfile(self, window, fn):
print('do something')
def bind_dropfile(self):
Window.bind(on_dropfile=self.manage_dropfile)
print ('bound!')
def unbind_dropfile(self):
Window.unbind(on_dropfile=self.manage_dropfile)
print ('unbound!')
if __name__ == '__main__':
DropFileApp().run()
There is another similar example.
The app also has two buttons.
I expect that when clicking on the left button, the mouse will become limited to the app window area (will not be able to exit it), and when I click on the right button, this function will be disabled.
But only the left button works.
from kivy.app import App
from kivy.core.window import Window
from kivy.lang import Builder
KV = '''
BoxLayout:
Button:
text: 'bind grab_mouse'
on_press: app.bind_grab_mouse()
Button:
text: 'unbind grab_mouse'
on_press: app.unbind_grab_mouse()
'''
class DropFileApp(App):
def build(self):
self.root = Builder.load_string(KV)
def manage_grab_mouse(self, instance):
print('do something')
Window.grab_mouse()
def bind_grab_mouse(self):
Window.bind(on_cursor_leave=self.manage_grab_mouse)
print ('bound!')
def unbind_grab_mouse(self):
Window.unbind(on_cursor_leave=self.manage_grab_mouse)
print ('unbound!')
if __name__ == '__main__':
DropFileApp().run()
What could be the problem?
You are deactivating the connection between the on_cursor_leave event and the manage_grab_mouse function, but you are not disabling the grab_mouse functionality. What you have to do is call grab_mouse() in bind_grab_mouse() function and ungrab_mouse() in ungrab_mouse() function, it is not necessary to create the function manage_grab_mouse.
from kivy.app import App
from kivy.core.window import Window
from kivy.lang import Builder
KV = '''
BoxLayout:
Button:
text: 'bind grab_mouse'
on_press: app.bind_grab_mouse()
Button:
text: 'unbind grab_mouse'
on_press: app.unbind_grab_mouse()
'''
class DropFileApp(App):
def build(self):
self.root = Builder.load_string(KV)
def bind_grab_mouse(self):
Window.grab_mouse()
def unbind_grab_mouse(self):
Window.ungrab_mouse()
if __name__ == '__main__':
DropFileApp().run()
My Main class returns a instance of ScreenManager. This ScreenManager has a widget with a Screen class, and I want this Screen class to use a widget which is a Layout that I defined earlier.
When I execute the code, it only show a black screen with no more information. It should show a Button instead.
This is my file minimum.py:
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.app import App
class LayoutWithButton(BoxLayout):
def __init__(self, **kwargs):
super(LayoutWithButton, self).__init__(**kwargs)
class MainScreenApp(Screen):
def __init__(self, **kwargs):
super(MainScreenApp, self).__init__(**kwargs)
button_layout = LayoutWithButton()
self.add_widget(button_layout)
screen_manager = ScreenManager()
screen_manager.add_widget(MainScreenApp(name='main'))
class TestMainApp(App):
def build(self):
return screen_manager
if __name__ == '__main__':
TestMainApp().run()
And this is my file testmain.kv:
<LayoutWithButton>:
Button:
text: 'hello'
Even so, if I replace the line self.add_widget(button_layout) of the class MainScreenApp with the line self.add_widget(Button()) it works well.
What am I doing wrong?
The problem is that kv file is not loaded until TestMainApp is initialized. As you instanciate MainScreenApp before this, the rules defined within the kv have no effect.
A very simple solution is to move instanciation of MainScreenApp to App subclass:
class MainApp(App):
def build(self):
screen_manager = ScreenManager()
screen_manager.add_widget(MainScreenApp(name='main'))
return screen_manager
You can also force the kv load before instantiating:
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang.builder import Builder
from kivy.app import App
class LayoutWithButton(BoxLayout):
pass
class MainScreenApp(Screen):
def __init__(self, **kwargs):
super(MainScreenApp, self).__init__(**kwargs)
button_layout = LayoutWithButton()
self.add_widget(button_layout)
Builder.load_file("testmain.kv")
screen_manager = ScreenManager()
screen_manager.add_widget(MainScreenApp(name='main'))
class TestApp(App):
def build(self):
return screen_manager
if __name__ == '__main__':
TestApp().run()
Another option is do everything in your kv:
from kivy.uix.screenmanager import ScreenManager
from kivy.app import App
class RootWidget(ScreenManager):
pass
class MainTestApp(App):
def build(self):
return MainWindow()
if __name__ == '__main__':
MainTestApp().run()
maintest.kv:
<LayoutWithButton#BoxLayout>:
Button:
text: 'hello'
<MainScreenApp#Screen>:
LayoutWithButton:
<RootWidget>:
MainScreenApp:
name: "main"
From testmain.kv you should remove that < and > from the class name.
You only use it when you are styling it and will also be calling it many many times.
Example how your code should look like:
LayoutWithButton:
Button:
text: 'hello'
When to use < and >:
<LayoutWithButton>:
pos_hint: 0, .5
Button:
text: 'hello'
BoxLayout:
LayoutWithButton:
pos_hint: 0, .5
Good luck!
In python2.7 + kivy1.9, I use AsyncImage like:
class Foo(BoxLayout):
..def bar(self):
....file_name=StringProperty()
..
..
....self.file_name="/../../image.png"
..
and in kivy,
BoxLayout:
..AsyncImage:
....source: root.file_name
Before the second call of function bar I change the content of image.png.
But the image displayed doesn't change.
I tried "nocache: True" after source command in kivy but it doesn't work.
How can I get the correct display everytime I call the func bar.
The problem is that you declare file_name property in each bar call. The file_name property must be a class attribute:
main.py:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
class RootWidget(BoxLayout):
image_path = StringProperty('image1.png') # <<<<<<<<
def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)
def change_image(self, path):
self.image_path = path
class TestApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
TestApp().run()
test.kv:
<RootWidget>:
orientation: "vertical"
AsyncImage:
id: image
source: root.image_path
BoxLayout:
size_hint_y: 0.1
Button:
text: 'Image 1'
on_press: root.change_image("image1.png")
Button:
text: 'Image 2'
on_press: root.change_image("image2.png")
EDIT
If you would like to use only one file name and change the content of the image, you need call reload method (with nocache property True):
main.py:
import os
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
class AsyncTest(BoxLayout):
img = ObjectProperty()
def show_icon(self):
self.img.source = "/sdcard/archive/icon_0.png"
def switch(self):
os.system("mv /sdcard/archive/icon_1.png /sdcard/archive/icon_0.png")
self.img.reload()
class TestApp(App):
def build(self):
return AsyncTest()
if __name__ == '__main__':
TestApp().run()
test.ḱv:
<AsyncTest>:
orientation: "vertical"
img: asyn_image
AsyncImage:
id: asyn_image
nocache: True
BoxLayout:
size_hint_y: 0.1
Button:
text: 'Show'
on_press: root.show_icon()
Button:
text: 'Switch'
on_press: root.switch()
Output: