I am making an app in which, on one screen, I want buttons stacked along the right edge of the screen(for which I need stack layout) and 2 buttons at the centre of the screen(for this I want to use float layout). I have searched for it but nowhere I can see any examples of using two different layouts on one screen.
Can we use two different layouts on a screen? if yes how can we do that?
hers a sample code-
from kivy.uix.stacklayout import StackLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
class screen_1(Screen,Stacklayout): ''' here I tried to inherit
floatlayout, but i guess it
doesnt work that way'''
pass
class main(App):
def build(self):
return screen_1()
m = main()
m.run()
kivy code-
<screen_1>:
StackLayout:
orientation: 'tb-rl'
spacing: 10
padding: 90
TextInput:
text: "write your word here"
color: 1,1,1,1
id: word_input
width: 300
size_hint: None, .10
stackLayout:
orientation: 'rl-tb'
spacing: 10
padding: 90
TextInput:
text: "write your word here"
color: 1,1,1,1
Layouts, lake all widgets, can be nested.
If you want two layouts in your screen, you just put them in an appropriate layout to manage their respective size/positions.
First, you likely don't want to do multiple-inheritance with widgets, at least not inheriting from multiple widgets (inheriting from one widget and one or multiple other objects can be fine, but inheriting from multiple widgets will cause issues). Also, Screen is already a RelativeLayout[1] which is almost the same as a FloatLayout (only it makes the pos coordinates of children relative to its own).
Instead, what you want is to compose (by nesting).
Screen:
StackLayout:
size_hint: .5, 1 # let's take half of the screen in width, and all of it in height
# whatever you want inside this layout
BoxLayout:
size_hint: .5, 1 # same, half of the width, all of the height
pos_hint: {'right': 1} # stick to the right of the screen
Button:
[1] https://kivy.org/docs/api-kivy.uix.screenmanager.html?highlight=screen#kivy.uix.screenmanager.Screen
[2] https://kivy.org/docs/api-kivy.uix.relativelayout.html#kivy.uix.relativelayout.RelativeLayout
Related
I have an example code and it works nice.
But in this code Scrollview and Gridlayout are created in KV File.
I have a scroll bug problem so i want to create them in pure python.
There are 350 boxlayouts and they are created in Scrollview and each boxlayout has 5 widgets (buttons label etc) So this causes some bugs in Scrollview when clearing screen and recreating results. There are no problem for 80 boxlayout, but they are created dynamically and some times there are 300+ boxlayouts in Scrollview.
I thought if i can create Scrollview and Gridlayout, i can fix this issue.
So i need your help.
How can i do that.
Test code:
PY File:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.metrics import dp
from kivy.uix.behaviors import ButtonBehavior
from kivy.clock import Clock, mainthread
import json
import threading
class Test(BoxLayout):
def __init__(self, **kwargs):
super(Test, self).__init__(**kwargs)
self.data = self.datas()
# Homepage Screen
def homepage(self, screenmanager):
screenmanager.current = 'homepage_screen'
Clock.schedule_once(self.clear_widgets)
# Clear Widgets
def clear_widgets(self, *args):
for child in [child for child in self.ids.gridsonuc.children]:
self.ids.gridsonuc.remove_widget(child)
#Second screen
def second(self,screenmanager):
screenmanager.current = 'second_screen'
Clock.schedule_once(self.clear_widgets)
Clock.schedule_once(self.datas) # Before calculation, each time app pulls data again, but Kivy Does Not Update The Refreshed Data in The Screen!
Clock.schedule_once(self.calculate)
# or, if i can use threading system as well but this time i must add #mainthread above def calculate(self, *args): to make code work.
# in both scenario, Kivy Does Not Update The Refreshed Data in The Screen While APP is Running.
# mythread1 = threading.Thread(target=self.clear_widgets)
# mythread1.start()
# mythread2 = threading.Thread(target=self.datas)
# mythread2.start()
# mythread3 = threading.Thread(target=self.calculate)
# mythread3.start()
# Calculation
##mainthread
def calculate(self, *args):
for i in self.data['home']:
box = BoxLayout(size_hint_y = None, height = dp(50))
hometeams = Label(text = f'{[i]}', font_name = 'Roboto', font_size = dp(15), size_hint = (0.225, 1), halign='center', bold = True )
box.add_widget(hometeams)
self.ids.gridsonuc.add_widget(box)
def datas(self, *args):
# PLEASE CHANGE THE LOCATION!!!!!!!!!!!!!!!!!
with open ("C:\\Users\\Messi\\Desktop\\Python\\Projects\\Football Tips\\Kivy\\Testing Bugs\\Test1\\data.json", "r") as dosya:
dataApi = json.load(dosya)
print('datas updated')
self.data = dataApi # update the self.data
return dataApi
class TestApp(App):
def build(self):
return Test()
if __name__ == '__main__':
TestApp().run()
KV File:
#:import NoTransition kivy.uix.screenmanager.NoTransition
<Test>:
ScreenManager:
transition: NoTransition()
id: sm
size: root.width, root.height
Screen:
name: 'homepage_screen'
BoxLayout:
size_hint: 1, 0.10
Button:
text: 'Calculate'
id: underOver_button_homepage
on_press: root.second(sm)
background_color: 0, 0, 0, 0
Screen:
name: 'second_screen'
BoxLayout:
spacing: '20dp'
orientation: 'vertical'
BoxLayout:
size_hint: 1, 0.80
ScrollView:
scroll_type: ['bars', 'content']
bar_margin: '5dp'
bar_color: 1, 0.4, 0.769, 1
bar_width: '5dp'
bar_inactive_color: 1, 0.4, 0.769, 1
GridLayout:
id: gridsonuc
cols: 1
spacing: '50dp'
size_hint_y: None
height: self.minimum_height
BoxLayout:
size_hint: 1, 0.10
Button:
text: 'Home'
id: home_button_underOver
on_press: root.homepage(sm)
background_color: 0, 0, 0, 0
For example:
I don't want this structure in KV file
BoxLayout:
size_hint: 1, 0.80
ScrollView:
scroll_type: ['bars', 'content']
bar_margin: '5dp'
bar_color: 1, 0.4, 0.769, 1
bar_width: '5dp'
bar_inactive_color: 1, 0.4, 0.769, 1
GridLayout:
id: gridsonuc
cols: 1
spacing: '50dp'
size_hint_y: None
height: self.minimum_height
My Goal:
BoxLayout:
size_hint: 1, 0.80
Newscroll: # New Scrollview Class and have also Gridlayout in it
GridLayout and Scrollview must have same settings and i want to use like below. I think scroll_type: ['bars', 'content'], bar_margin: '5dp', bar_color: 1, 0.4, 0.769, 1, bar_width: '5dp', bar_inactive_color: 1, 0.4, 0.769, 1, cols: 1, spacing: '50dp', size_hint_y: None, height: self.minimum_height should code in Python
And a new clear def() for cleaning full scrollview class, when clear and create Scrollview in Pure Python, there are created dynamically so I can fix my Scroll bug issue.
Thanks for your help.
I hope one day I can be at a level where I can help other people in Kivy.
Kivy: How can I recode Scrollview and Gridlayout in pure Python?
The shortest answer to your question is:
You can't.
See Pure python gui library? here on stackoverflow to read:
Notion of "pure python gui library" is wrong because ultimately you will be using system level calls and widgets, may be thru ctypes but that doesn't change the fact that if you start implementing your idea you will eventually end with using wxPython or some other module.
along with the statement in the accepted answer:
The path of least effort and best results would be to learn what it takes to deploy an app using some of the existing GUI libraries.
Kivy is a Python module you can use to create GUIs (Graphical User Interfaces) and comes with the possibility to create a Scrollview and a Gridlayout objects/widgets for by it created window with an user interface.
Your goal is to use "pure Python" to replace the objects/widgets, but it is not possible to use "pure Python" for this task, because Python requires usage of a module for GUIs.
Most native GUI module that comes with the standard Python installation and is nearest to the notion of "pure Python" is tkinter, but it needs GTK to be already installed on the system, else it won't be available and you need to learn how to use it properly like you need it when using Kivy.
In other words it will be a good idea to find out what actually causes the problems you experience while using Kivy and solve them instead of learning how to use another GUI library to recreate your application.
And if your idea is to create own widgets within Kivy you have to learn how to write own extensions to Kivy which can take you much more effort compared to finding out the reason for why you face problems while using it.
I am making a weather app in which text results are displayed in a scrollview. I have been testing my app and have decided I need to add in x-axis scrolling. In previous times I was making use of just y-axis scrolling but have found that some of the weather results include longer lines that end up not fitting in the screen width and as a result, are being moved down a line which makes the formatting look bad.
I have found that not having anything set in text_size: allows the words to run off the screen without having them forced onto the next line. That is what I needed in regards to that.
Now I am stuck trying to figure out how to have my text results start out being aligned on the left margin of the screen and then allowing the user to scroll across to view entire line(s) if necessary. I am planning on having the run on iPad and iPhone so the screen widths will obviously differ, and I don't want to make the font size any smaller because the user wont be able to read it.
I have tried a whole bunch of different combinations with the layouts in the scrollview, with the parent and child but can't seem to get it right.
I have made a very minimalistic reproducible version that seems to behave the same as my actual app. The scrollview in my homescreen is the culprit. If someone could please help me out with this, that would be so good, thank you.
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
class HomeScreen(Screen):
pass
class Results1Screen(Screen):
pass
class TestApp(App):
def build(self):
return Builder.load_file("main.kv")
def on_start(self):
brief_res_1 = self.root.ids["home_screen"].ids["brief_res_1"]
brief_res_1.text = (open("text.txt", "r")).read()
TestApp().run()
main.kv
#:include kv/homescreen.kv
GridLayout:
cols: 1
FloatLayout:
ScreenManager:
size_hint: 1, .95
pos_hint: {"top": .95, "left": 1}
id: screen_manager
HomeScreen:
name: "home_screen"
id: home_screen
homescreen.kv
<HomeScreen>:
BoxLayout:
pos_hint: {"top": .7, "left": 1}
size_hint: 1, 1
size_hint_y: None
ScrollView:
Label:
id: brief_res_1
font_size: '9sp' # need to keep this at '9sp'
do_scroll_x: True
size_hint_y: None
size_hint_x: None
height: self.texture_size[1]
halign: "left" # not too sure if this should be "left"
valign: "center"
text:
text.txt
LINE 1 TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT
LINE 2 TEXT TEXT TEXT TEXT TEXT TEXT
LINE 3 TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT
You can just replace:
height: self.texture_size[1]
with:
size: self.texture_size
If you do not set the width of the Label, it will take the default width of 100, and your x-scrolling will not happen until the width of your HomeScreen gets less than 100 pixels wide.
The same code for python layout returns different GUI. I'm terribly confused:
# ---------- VQCIA.kv ----------
VQCIA:
<VQCIA>:
orientation: "vertical"
goi: goi
padding: 10
spacing: 10
size: 400, 200
pos: 200, 200
size_hint:None,None
BoxLayout:
Label:
text: "Enter gene of interest with TAIR ID:"
font_size: '25sp'
BoxLayout:
TextInput:
hint_text: 'AT3G20770'
multiline: False
font_size: '25sp'
id: goi
BoxLayout:
Button:
text: "Submit"
size_hint_x: 15
on_press: root.submit_goi()
# ---------- vqcia.py ----------
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
class VQCIA(BoxLayout):
# Connects the value in the TextInput widget to these
# fields
goi = ObjectProperty()
def submit_goi(self):
# Get the student name from the TextInputs
goi = self.goi.text
print goi
return
class VQCIAApp(App):
def build(self):
return VQCIA()
dbApp = VQCIAApp()
dbApp.run()
my lab computer is macOS Sierra 10.12.6 with Kivy==1.10.1 and has ideal output:
on the other side my personal mac, macOS high Sierra 10.13.6 with Kivy==1.10.1, has wrong outputs:
what happens?
Try using Density-independent Pixels, dp.
In kv file, there are two roots. There is a root rule, VQCIA: and also a class rule, <VQCIA>: for the root. Since in Python code, it is using return VQCIA(), which is associated to class rule, <VQCIA>: in kv file. Therefore, remove root rule, VQCIA: to avoid confusion.
kv file - vqcia.kv
<VQCIA>: # class rule for root widget
orientation: "vertical"
goi: goi
padding: dp(10)
spacing: dp(10)
size: dp(400), dp(200)
pos: dp(200), dp(200)
size_hint: None, None
Dimensions
dp
Density-independent Pixels - An abstract unit that is based on the
physical density of the screen. With a density of 1, 1dp is equal to
1px. When running on a higher density screen, the number of pixels
used to draw 1dp is scaled up a factor appropriate to the screen’s
dpi, and the inverse for a lower dpi. The ratio of dp-to-pixels will
change with the screen density, but not necessarily in direct
proportion. Using the dp unit is a simple solution to making the view
dimensions in your layout resize properly for different screen
densities. In others words, it provides consistency for the real-world
size of your UI across different devices.
sp
Scale-independent Pixels - This is like the dp unit, but it is also
scaled by the user’s font size preference. We recommend you use this
unit when specifying font sizes, so the font size will be adjusted to
both the screen density and the user’s preference.
I will say first off I have tried every single example on the web involving kv lang. Not once have I had any success.
The idea is pretty simple: As I swipe up/down/scroll the contents of GridLayout() within ScrollView() are scrolled up or down.
The best I have been able to do is have the scroll bar fade into view when running the program. Not able to scroll unfortunately.
<Root>
grid_layout: grid_layout
ScreenManager:
...
Screen:
...
ScrollView:
GridLayout:
id: grid_layout
size_hint_y: None
cols: 1
height: self.minimum_height
<list of buttons>
Binding minimum_heightin the __init__ method of the root class (RelativeLayout):
grid_layout = ObjectProperty(None)
self.grid_layout.bind(minimum_height=self.grid_layout.setter('height'))
I have followed https://github.com/kivy/kivy/blob/master/examples/widgets/scrollview.py converting it to kv lang - scroll bar visible, unable to scroll. Also tried every example on Google Groups and here related to using kv lang. Still no scroll :\
Compiling using buildozer and running on Android fails for an unknown reason.
I would appreciate any assistance that can be given.. I am completely clueless at this point
This:
height: self.minimum_height
should be:
minimum_height: self.height
This is unnecessary:
grid_layout = ObjectProperty(None)
self.grid_layout.bind(minimum_height=self.grid_layout.setter('height'))
It also won't scroll unless the contents are larger than the scrollview's height:
Full code:
from kivy.lang.builder import Builder
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
Builder.load_string('''
<Root>:
ScrollView:
size_hint: 1, .1
# setting the width of the scrollbar to 50pixels
bar_width: 50
# setting the color of the active bar using rgba
bar_color: 5, 10, 15, .8
# setting the color of the inactive bar using rgba
bar_inactive_color: 5, 20, 10, .5
# setting the content only to scroll via bar, not content
scroll_type: ['bars']
GridLayout:
size_hint_y: None
cols: 1
minimum_height: self.height
Button
text: 'one'
Button:
text: 'two'
Button:
text: 'three'
Button:
text: 'four'
''')
class Root(FloatLayout):
pass
class DemoApp(App):
def build(self):
return Root()
if __name__ == '__main__':
DemoApp().run()
Being unable to scroll was due to a misunderstanding of Kivy's touch handlers. Completely unrelated to the code mentioned in my question.
The key is to have GridLayout be larger than ScrollView, so GridLayout can be panned within ScrollView.
For those wanting to use ScrollView inside ScreenManager using kvlang only:
ScreenManager:
id: screen_manager
Screen:
manager: screen_manager
id: main_screen
name: 'main'
ScrollView:
bar_width: 4
# pos_hint defaults to 1,1 so no need to declare it
GridLayout:
size_hint_y: None
cols: 1
# you do not need to manually bind to setter('height') in
# python - perfectly possible with kv lang
# this allows for height to update depending on the
# collective heights of its child widgets
height: self.minimum_height
<----- widgets here ----->
# for scroll to show/work there must be more widgets
# then can fit root.height. If not there is no need
# for scrollview :)
When I build an application using kivy, I can get everything where I want it, but when I rotate the screen from portrait to landscape, my widgets start colliding with one another. What is a good way to prevent this from happening?
In the attached example, I was able to bind the placement of a settings button to my header label widget, but I was unsuccessful in getting my scrollview to bind to it so when it rotates it stays X amount of space away from the bottom of the label.
Python:
class First_Screen(Screen):
def __init__(self, **kwargs):
super(First_Screen, self).__init__(**kwargs)
list_of_buttons = [
'button1',
'button2',
'button3',
'button4',
'button5',
'button6 ',
'button7',
'button8',
]
class My_Grid(FloatLayout, ScrollView):
grid = ObjectProperty(None)
def on_grid(self, *args):
for btn in list_of_buttons:
btn = Button(
text = btn,
)
btn.bind(on_press= First_Screen.print_message)
self.grid.add_widget(btn)
def print_message(self):
print ('Congratulations! You have clicked a button!')
class ScreenManagement(ScreenManager):
pass
class Stack_Overflow(App):
def build(self):
sm = ScreenManager(transition = FadeTransition())
sc0 = First_Screen(name = 'first_screen')
sm.add_widget(sc0)
return sm
if __name__ == '__main__':
Stack_Overflow().run()
kv:
ScreenManagement:
First_Screen:
<ImageButton#ButtonBehavior+Image>:
<First_Screen>:
name: 'first_screen'
Label:
id: header
text: 'header'
pos_hint: ({'left' : 1, 'top' : 1})
size_hint: (1,None)
height: dp(50)
canvas.before:
Color:
rgba: (.6, .6, .6, 1)
Rectangle:
pos: self.pos
size: self.size
ScrollView:
size_hint: (1,.8)
pos_hint: ({'center_x' : .5, 'center_y' : .5})
My_Grid:
grid: grid
GridLayout:
id: grid
cols: 1
size_hint_y: None
row_default_height: "40dp"
height: self.minimum_height
ImageButton:
id: settings
size_hint: None, None
height: dp(30)
width: dp(30)
pos: header.x, header.y + 10
pos_hint: {'right': 1}
source: 'settings_black.png'
Images: portrait, landscape
Thanks!
Edit: I misunderstood the question, I thought you were unsatisfied with the Image. In the case of ScrollView the problem isn't the ScrollView itself, because that uses relative positions and sizes.
The problem is the Label(header), which has height set as an absolute number i.e. 50 dense pixels. It means that whatever screen you have or however you size anything else, this widget will have 50 pixels corrected with dpi of the screen.
In reality this means that if your screen reaches 400x100 resolution, your Label will take half of the screen and ScrollView will be placed on top of that (most likely you won't see the Label then).
To fix that you have two options. If you want to play with FloatLayout alone, you'll need to use correct positioning/sizing i.e. relative and make sure widgets aren't overlaping.
Otherwise just drop the widgets to BoxLayout or a similar other layout that manages such things on its own:
class First_Screen(BoxLayout, Screen):
def __init__(self, **kwargs):
super(First_Screen, self).__init__(orientation='vertical', **kwargs)
or a little bit different (and better looking):
class First_Screen(BoxLayout, Screen):
def __init__(self, **kwargs):
and in kv:
<First_Screen>:
name: 'first_screen'
orientation: 'vertical'
Also note, that there's a screen module, which makes testing such things a way more easier than dropping the code to apk and to android when it's just unnecessary. Example:
python main.py -m screen:note2,landscape,scale=.5