For the sake of brevity I'll explain my problem before posing the code. Here it goes:
I have a Screen class in Kivy that holds two widgets, a GridLayout and an Image. The latter is fine, but the buttons are extremely oversized:
And here's my main.py:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.factory import Factory
from kivy.uix.gridlayout import GridLayout
from kivy.uix.image import Image
from kivy.config import Config
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
import configparser
Builder.load_file('kv\MainMain.kv')
#read configurations
config = configparser.RawConfigParser()
config.read('config.ini')
#read config values
width = config.getint('Default', 'MAX_WINDOW_WIDTH')
height = config.getint('Default', 'MAX_WINDOW_HEIGHT')
border = config.getint('Default', 'BORDERLESS')
#apply config values
Config.set('graphics','width', width)
Config.set('graphics', 'height', height)
Config.set('graphics', 'borderless', border)
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
#create screen manager
class ScreenManager(ScreenManager):
pass
#create background widget
class BackGround(Image):
pass
#image buttons
class MainButtons(GridLayout):
pass
#create main screen:
class MainScreen(Screen):
pass
#create main app
class MainMainApp(App):
def build(self):
return MainScreen()
#register class
Factory.register(MainScreen, cls=MainScreen)
#run
if __name__ == '__main__':
MainMainApp().run()
And here's my kv file:
<ScreenManager>:
id: screen_manager
MainScreen:
id: main_screen
name: 'MainScreen'
manager: screen_manager
ReadScreen:
id: read_screen
name: 'ReadScreen'
manager: screen_manager
<MainScreen>:
BackGround:
id: back_ground
source: 'images\\app_background.jpg'
size: root.width, root.height
MainButtons:
cols: 1
pos: root.width / 2 - 100, root.height / 4
size: 20, 10
Button:
id: button_read
text: "Read"
on_press: root.callback_read()
Button:
id: button_add
text: "Add"
Button:
id: button_manage
text: "Manage"
I'm really swamped on this one. Thanks for your help.
You can use size_hint for this.
Here is an example
The pyhton file:
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen,ScreenManager
Builder.load_file("my.kv")
class MainButtons(GridLayout):
pass
class MainScreen(Screen):
pass
class MainMainApp(App):
def build(self):
self.sm = ScreenManager()
self.sm.add_widget(MainScreen(name="main"))
return self.sm
if __name__ == '__main__':
MainMainApp().run()
and kivy file:
<MainScreen>:
MainButtons:
cols: 1
rows: 4
Label:
font_size: "40sp"
text: "Something"
Button:
size_hint: (1,0.1)
text: "Read"
Button:
size_hint: (1,0.1)
text: "Add"
Button:
size_hint: (1,0.1)
text: "Manage"
And output will be:
And if you want the buttons to be smaller in width, you add ´size_hint_x: None´ to your kv file, like this.
<MainScreen>:
MainButtons:
cols: 1
rows: 4
Label:
font_size: "40sp"
text: "Something"
Button:
size_hint: (1,0.1)
size_hint_x:None
text: "Read"
Button:
size_hint: (1,0.1)
size_hint_x:None
text: "Add"
Button:
size_hint: (1,0.1)
size_hint_x:None
text: "Manage"
Output will now be:
Related
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.button import Button
Window.size = (500,700)
Builder.load_file('calc.kv')
class MyLayout(Widget):
pass
class App(App):
def build(self):
return MyLayout()
if __name__ == "__main__":
App().run()
this is my calc.py please help me to solve the problem
#:kivy 2.0.0
BoxLayout:
orientation:"vertical"
size: root.width, root.height
TextInput:
id: clac_input
text: "0"
halign: "right"
font_size : 65
size_hint: (1, .15)
GridLayout:
cols:4
rows:5
Button:
size_hint: (.2, .2)
font_size: 32
text: "%"
this is my calc.kv
From what I'm seeing the app is not returning any screens for your widgets to show on. I've changed a bit of your code to what I believe is correct that solves the issue.
.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.core.window import Window
from kivy.uix.button import Button
Window.size = (500,700)
Builder.load_file('calc.kv')
class MyLayout(Widget):
pass
class Screen1(Screen):
pass
class WindowManager(ScreenManager):
pass
class App(App):
def build(self):
return WindowManager()
if __name__ == "__main__":
App().run()
calc.kv
<WindowManager>:
Screen1
<Screen1>:
BoxLayout:
orientation:"vertical"
size: root.width, root.height
GridLayout:
cols:4
rows:5
TextInput:
id: clac_input
text: "0"
halign: "right"
font_size : 65
size_hint: (1, .15)
Button:
size_hint: (.2, .2)
font_size: 32
text: "%"
I'm trying to make an app. On button click on the bottom right of the screen there appears a dialog window(popup). On "Done" click the popup window closes (close_dialog method), and a new List Item is expected to appear.
Unfortunately the error occurs on "Done" click:
AttributeError: 'DialogContent' object has no attribute 'get_screen'
Could you please tell me why does the error occur and how can I fix it?
I suppose that it is caused by the fact that DialogContent class inherits from BoxLayout (not from Screen) but I don't know how to fix it.
Code .py:
from kivy.lang import Builder
from kivy.core.window import Window
from kivymd.app import MDApp
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
from kivymd.uix.button import MDFlatButton
from kivymd.uix.dialog import MDDialog
from kivymd.uix.textfield import MDTextField
from kivy.uix.textinput import TextInput
from kivy.uix.screenmanager import Screen, ScreenManager
from kivymd.uix.list import TwoLineAvatarListItem
Window.size = (288, 511)
class GroupScreen(Screen):
pass
class DialogContent(BoxLayout):
pass
class MainScreen(Screen):
dialog = None
def show_dialog(self, *args):
'''
Create group creation popup
'''
if not self.dialog:
self.dialog = MDDialog(
title="Create new group",
type="custom",
content_cls=DialogContent(),
auto_dismiss=False
)
self.dialog.open()
def close_dialog(self, *args):
'''
Close popup on Done click
'''
self.dialog.dismiss()
self.new_window()
def new_window(self, *args):
'''
Create new group button
'''
mylist = TwoLineAvatarListItem(text = self.dialog.content_cls.textfield.text,
secondary_text = "1,2,3...")
self.mdlist.add_widget(mylist)
class test2App(MDApp):
def build(self):
sm = ScreenManager()
sm.add_widget(MainScreen(name='main'))
sm.add_widget(GroupScreen(name='group'))
scroll = ScrollView()
return sm
if __name__ == '__main__':
test2App().run()
Code .kv:
ScreenManager:
MainScreen:
GroupScreen:
<DialogContent>:
textfield: textfield
orientation: "vertical"
spacing: "12dp"
size_hint_y: None
height: "120dp"
MDTextField:
id: textfield
hint_text: "Group name"
MDFlatButton:
id: btn1
text: "Done"
text_color: self.theme_cls.primary_color
on_release: root.get_screen['main'].close_dialog()
<MainScreen>:
name: 'main'
mdlist: mdlist
FloatLayout:
size_hint: 1, 0.89
ScrollView:
MDList:
id: mdlist
MDFloatingActionButton:
pos_hint: {'right': 0.95, 'y': 0.05}
icon: "android"
theme_text_color: "Custom"
text_color: app.theme_cls.primary_color
on_release:
root.show_dialog()
<GroupScreen>:
name: 'group'
MDLabel:
text: 'Welcome'
halign: 'center'
MDRectangleFlatButton:
text: 'Back'
pos_hint: {'center_x': 0.5, 'center_y': 0.3}
on_release: root.manager.current = 'main'
Change:
on_release: root.get_screen['main'].close_dialog()
to:
on_release: app.root.get_screen('main').close_dialog()
The app.root gets you a reference to the root widget of the app, which is the ScreenManager. Then you can use get_screen('main') to access the main Screen and call its close_dialog() method.
I was using my main class as widget and was passing IDs from kv file to python without problems.
Now changed it to a screen as I need to switch between 2 screens but now I cant access the IDs declared in kv file. I get error - 'MainScreen'object has no attribute 'labINFO' - (this is the first label id I am trying to modify.)
Here are parts of py and kv files
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.core.window import Window
from kivy.uix.popup import Popup
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.clock import Clock
class GSMNosScreen(Screen):
def gsmclose(self, obj):
Clock.schedule(self.startgetTemps,dataInterval)
BoilerApp.sm.current = 'main'
class MainScreen(Screen):
....
....
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
self.labINFO.text = "Waiting for Pic..."
self.labM1.text = str(b1Min)
self.labM2.text = str(b2Min)
sm = ScreenManager() #transition=NoTransition()
sm.add_widget(MainScreen(name='main'))
sm.add_widget(GSMNosScreen(name='gsmnos'))
class BoilerApp(App):
def build(self):
self.title = 'Boiler Monitor'
return sm
if __name__ == "__main__":
from kivy.core.window import Window
Window.fullscreen = True
app = BoilerApp()
app.run()
KV file
<GSMNosScreen>:
labN1: labN1
size_hint: .5, .5
auto_dismiss: False
title: "GSM Numbers" # <<<<<<<<
separator_height: 0 # <<<<<<<<
GridLayout:
cols: 1
size: root.width, root.height
MyTLab:
id:labN1
font_size: 20
text: "Number 1 : ..."
MyBut:
text: "Close"
font_size: 20
size_hint: .2, .2
on_press: gsmclose()
<MainScreen>
labM1: labM1
labM2: labM2
labM3: labM3
labM4: labM4
labM5: labM5
labINFO: labINFO
labDT: labDT
GridLayout:
cols: 1
size: root.width, root.height
MyTLab:
text: "Monitor"
underline: True
size_hint: .5, .2
BoxLayout:
orientation: "horizontal"
size_hint_y: .2
MySTLab:
color: 0.8,.2,.2,0.7
text: "Min °C"
size_hint_x: 1.5
font_size: 25
MyMLab:
id: labM1
text: "50"
MyMLab:
id: labM2
text: "50"
MyMLab:
id: labM3
text: "50"
MyMLab:
id: labM4
text: "50"
BoxLayout:
orientation: "horizontal"
#cols:2
size_hint_y: .1
Label:
id: labDT
color: 0,1,0.5,0.8
text: "Starting ...."
font_size: sp(20)
size_hint_x: .3
Label:
id: labINFO
color: 0,1,0.5,0.8
text: "...."
font_size: sp(20)
size_hint_x: .7
I believe the problem is that the kv file has not been loaded yet when the lines:
sm = ScreenManager() #transition=NoTransition()
sm.add_widget(MainScreen(name='main'))
sm.add_widget(GSMNosScreen(name='gsmnos'))
are executed.
Try moving the above lines into the build() method of the App:
class BoilerApp(App):
def build(self):
self.title = 'Boiler Monitor'
sm = ScreenManager() # transition=NoTransition()
sm.add_widget(MainScreen(name='main'))
sm.add_widget(GSMNosScreen(name='gsmnos'))
return sm
The kv file (if it is named boiler.kv) is loaded before the build() mehod is called.
I am trying to create a simple Chatbot application UI using Kivy and Python but I'm stuck at the very first stage.
How do I access the TextInput widget inside the BoxLayout to fetch it's contents?
I read that the function on_text_validate() is called whenever you press 'Enter' on the TextInput. However this code down below doesn't seem to be functioning.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.screenmanager import Screen, ScreenManager, FadeTransition
from kivy.uix.scrollview import ScrollView
from kivy.properties import StringProperty
from kivy.lang import Builder
Builder.load_string('''
#:import AnchorLayout kivy.uix.anchorlayout
#:import Layout kivy.uix.layout
<ChatBotScreen>:
BoxLayout:
orientation: 'vertical'
ScrollView:
Label:
size_hint_y: None
height: self.texture_size[1]
text_size: self.width, None
botOutput: root.botOutput
ScrollView:
Label:
size_hint_y: None
height: self.texture_size[1]
text_size: self.width, None
userInput: root.userInput
TextInput:
id: ti_userinput
multiline: False
''')
class ChatBotScreen(Screen):
userInput = StringProperty()
botOutput = StringProperty()
def on_text_validate(self):
text_input_userInput = self.ids['ti_userinput'].text
self.ids['ti_userinput'].text = ''
print(text_input_userInput)
def UserInput(self):
pass
def BotOutput(self):
pass
sm = ScreenManager(transition=FadeTransition())
sm.add_widget(ChatBotScreen(name='mainchat'))
class MyApp(App):
def build(self):
return sm
Please guide me.
The problem in your case is that on_text_validate is not a method of the Screen class, but of TextInput, so you will never call on_text_validate of the ChatBotScreen class, what you can do is invoke that method from the on_text_validate event of the TextInput:
Builder.load_string('''
#:import AnchorLayout kivy.uix.anchorlayout
#:import Layout kivy.uix.layout
<ChatBotScreen>:
BoxLayout:
orientation: 'vertical'
ScrollView:
Label:
size_hint_y: None
height: self.texture_size[1]
text_size: self.width, None
botOutput: root.botOutput
ScrollView:
Label:
size_hint_y: None
height: self.texture_size[1]
text_size: self.width, None
userInput: root.userInput
TextInput:
id: ti_userinput
multiline: False
on_text_validate: root.on_text_validate()
''')
class ChatBotScreen(Screen):
userInput = StringProperty()
botOutput = StringProperty()
def on_text_validate(self):
text_input_userInput = self.ids['ti_userinput'].text
self.ids['ti_userinput'].text = ''
print(text_input_userInput)
sm = ScreenManager(transition=FadeTransition())
sm.add_widget(ChatBotScreen(name='mainchat'))
class MyApp(App):
def build(self):
return sm
I am trying to implement a custom closable tab header in kivy.
What I did was combine a class:TabbedPanelHeader object with a custom class:CloseButton object. Both of these widgets are inside a class:BoxLayout, side-by-side.
However, once I add this into a class:TabbedPanel object, nothing shows up..
I am not sure how to move forward and would greatly appreciate all the help!
Below is the relevant part of the code.
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
from kivy.graphics import *
from kivy.uix.tabbedpanel import TabbedPanelHeader
class CloseButton(ButtonBehavior, Image):
def __init__(self, **kwargs):
super(CloseButton, self).__init__(**kwargs)
self.source = 'atlas://data/images/defaulttheme/close'
self.size_hint_x = .2
def on_press(self):
self.source = 'atlas://data/images/defaulttheme/checkbox_radio_off'
def on_release(self):
self.source = 'atlas://data/images/defaulttheme/checkbox_radio_off'
## do the actual closing of the tab
class ClosableTabHeader(BoxLayout):
def __init__(self, **kwargs):
super(ClosableTabHeader, self).__init__(**kwargs)
self.size = (100, 30)
self.size_hint = (None, None)
self.canvas.before.add(Color(.25, .25, .25))
self.canvas.before.add(Rectangle(size=(105, 30)))
self.add_widget(TabbedPanelHeader(background_color=(.65, .65, .65, 0), text='testing'))
self.add_widget(CloseButton())
if __name__ == '__main__':
from kivy.app import App
class TestApp(App):
def build(self):
return ClosableTabHeader()
TestApp().run()
Here is some code which comes close to achieve what you are trying to achieve
from kivy.app import App
from kivy.animation import Animation
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelHeader
from kivy.factory import Factory
from kivy.lang import Builder
class CloseableHeader(TabbedPanelHeader):
pass
class TestTabApp(App):
def build(self):
return Builder.load_string('''
TabbedPanel:
do_default_tab: False
FloatLayout:
BoxLayout:
id: tab_1_content
Label:
text: 'Palim 1'
BoxLayout:
id: tab_2_content
Label:
text: 'Palim 2'
BoxLayout:
id: tab_3_content
Label:
text: 'Palim 3'
CloseableHeader:
text: 'tab1'
panel: root
content: tab_1_content.__self__
CloseableHeader:
text: 'tab2'
panel: root
content: tab_2_content.__self__
CloseableHeader:
text: 'tab3'
panel: root
content: tab_3_content.__self__
<CloseableHeader>
color: 0,0,0,0
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: root.size
padding: 3
Label:
id: lbl
text: root.text
BoxLayout:
size_hint: None, 1
orientation: 'vertical'
width: 22
Image:
source: 'tools/theming/defaulttheme/close.png'
on_touch_down:
if self.collide_point(*args[1].pos) :\
root.panel.remove_widget(root); \
''')
if __name__ == '__main__':
TestTabApp().run()
It is based on https://github.com/kivy/kivy/blob/master/examples/widgets/tabbed_panel_showcase.py