Reputation: 590
I'm looking to make a popup on the python-side that has a dynamic height.
So far, I have this within the screens __init__
class. The kv file has another widget that called the popup on_release. Anyways, I have found that this produces a popup with very wonky formatting:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import Screen, ScreenManager
kv = '''
ScreenManagement:
id: 'manager'
BrokenPopup:
name: 'broken'
manager: 'manager'
<BrokenPopup>:
BoxLayout:
Button:
text: 'Test'
on_release: root.p.open()
'''
class ScreenManagement(ScreenManager):
pass
class BrokenPopup(Screen):
def __init__(self, **kwargs):
super(BrokenPopup,self).__init__(**kwargs)
self.p = Popup(auto_dismiss=False, size_hint_x=.6, size_hint_y=None, title='A popup')
self.g = GridLayout(cols=1, spacing=10)
self.g.add_widget(Button(text='Test1', size_hint_y=None, height=32))
self.g.add_widget(Button(text='Test2', size_hint_y=None, height=32))
self.g.bind(minimum_height=self.g.setter('height'))
self.p.add_widget(self.g)
self.p.bind(height=self.g.setter('height')) #<- this does not work to change the popup height!
class TheApp(App):
def build(self):
return Builder.load_string(kv)
TheApp().run()
The popup size is set to fit only one widget, leaving the second button (and all others that may be included) to float beyond the confines of the popup border.
How should I change the code so that all of the widgets fit within the confines of the popup? I am trying to do that by dynamically setting the height of the popup, however that is not proving effective.
Upvotes: 1
Views: 1481
Reputation: 590
I have found a solution for my original problem that is influenced by John Anderson's answer. I'll provide a walkthrough below for how I came to this solution.
1) Here's a photo of my original problem; I needed to dynamically set the popup height based on the widgets that are assigned to it. Before finding the below solution, my popup looked like this with the code in the OP:
As you can see, the widgets go beyond the borders of the popup.
2) I found something interesting while looking inside the popup widget with the inspector tool.
python '/path/to/your/file.py' -m inspector
Using control-e, I can click widgets and inspect their attributes. I clicked the popup button and cycled through the parent widgets until I found the popup widget.
As you can see in the photo, the popup widget has one child: a grid layout. Here are the children of that grid layout:
Interestingly, the grid layout contains:
One label, with a height of 33
One line, with a height of 4
A box layout, which contains the contents of the popup
2 units of spacing between these three widgets
12 units of padding all-around; so 24 additional units to consider for the height
3) In my solution, I have hard-written the default heights of the label, the line widget, and all default popup spacing and padding. Then, I cycle through the children inside the box layout, and add their heights. I also add 10 to those children heights, as the gridlayout that contains all of these widgets uses a spacing of 10.
Solution:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import Screen, ScreenManager
kv = '''
ScreenManagement:
id: 'manager'
BrokenPopup:
name: 'broken'
manager: 'manager'
<BrokenPopup>:
BoxLayout:
Button:
text: 'Test'
on_release: root.p.open()
'''
class ScreenManagement(ScreenManager):
pass
class BrokenPopup(Screen):
def __init__(self, **kwargs):
super(BrokenPopup,self).__init__(**kwargs)
self.p = Popup(auto_dismiss=False, size_hint_x=.6, size_hint_y=None, title='A popup')
self.g = GridLayout(cols=1, spacing=10, padding=[0,10,0,-5])
self.g.bind(minimum_height=self.fix_popup_height) # <- here's the magic
self.g.add_widget(Button(text='Test1', size_hint_y=None, height=32))
self.g.add_widget(Button(text='Test2', size_hint_y=None, height=32))
self.p.add_widget(self.g)
def fix_popup_height(self, grid, *args):
# a generalized function that, when bound to minimum_height for a grid with popup widgets,
# this will set the height of the popup
height = 0
height += 33 # for popup label
height += 4 # for popup line widget
height += 24 # for popup padding
height += 2 # for spacing between main popup widgets
for child in grid.children:
height += child.height + 10 # adds 10 for the spacing between each child
grid.parent.parent.parent.height = height # sets popup height
pass
class TheApp(App):
def build(self):
return Builder.load_string(kv)
TheApp().run()
Notable changes from the OP:
Bind the minimum_height
of the widget container to the fix_popup_height()
function; this will trigger each time a widget is added to the popup.
Declare the fix_popup_height()
within the screen class.
Here's the fixed popup:
Upvotes: 0
Reputation: 38962
I have modified your code to do what I think you want. Basically it adds the minimum_height
from the GridLayout
, that is added to your Popup
, to the calculated height of the title
and the dividing bar. The first Button
in the GridLayout
now adds another Button
to the GridLayout
for testing.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import Screen, ScreenManager
kv = '''
ScreenManagement:
id: 'manager'
BrokenPopup:
name: 'broken'
manager: 'manager'
<BrokenPopup>:
BoxLayout:
Button:
text: 'Test'
on_release: root.p.open()
'''
class ScreenManagement(ScreenManager):
pass
class BrokenPopup(Screen):
def __init__(self, **kwargs):
super(BrokenPopup,self).__init__(**kwargs)
self.popup_title_height = None
self.p = Popup(auto_dismiss=False, size_hint_x=.6, size_hint_y=None, title='A popup')
self.g = GridLayout(cols=1, spacing=10)
self.g.bind(minimum_height=self.fix_size)
self.g.add_widget(Button(text='Test1', size_hint_y=None, height=32, on_release=self.add_one))
self.g.add_widget(Button(text='Test2', size_hint_y=None, height=32))
self.p.add_widget(self.g)
def add_one(self, *args):
self.g.add_widget(Button(text='Another', size_hint_y=None, height=32))
def get_popup_title_height(self):
height = 0
popupGrid = self.p.children[0]
height += popupGrid.padding[1] + popupGrid.padding[3]
for child in popupGrid.children:
if isinstance(child, BoxLayout):
continue
else:
height += child.height + popupGrid.spacing[1]
self.popup_title_height = height
def fix_size(self, *args):
if self.popup_title_height is None:
self.get_popup_title_height()
self.p.height = self.g.minimum_height + self.popup_title_height
class TheApp(App):
def build(self):
return Builder.load_string(kv)
TheApp().run()
I cheated a bit by looking at the code for Popup
and the style.kv
file to see how the Popup
is displayed. So, if any of that is changed, this may not work.
Upvotes: 1