我试图创建MWE来展示我的问题,而我最终却对此更加困惑。
我想创建一个包含多个小部件的应用。其中一个小部件将保留长文本,我预计它将跨越大约3行。我希望持有此文本的标签增加其高度以适应文本大小。
这是我到目前为止所得到的
layout = BoxLayout(orientation='horizontal')
subLayout = TwoLabelCombo()
subLayout.label1.text = ('foo:')
subLayout.label2.text = 'bar'*50
subLayout.label2.width = int(Window.width*0.8)
subLayout.label2.text_size = [subLayout.label2.width,None]
subLayout.label2.size_hint_y = None
subLayout.label2.height = subLayout.label2.texture_size[1]
subLayout.height = subLayout.label2.texture_size[1]
layout.height = subLayout.height
TwoLabelCombo
是BoxLayout
,其方向设置为水平,适用于size_hint_x
设置为0.2和0.8的标签。 layout
在应用中占据整行,其下方和下方的布局相似。文本确实跨越多行,但布局不会随文本更新,因此与其他小部件重叠。打印texture_size
我知道它的高度为0.如何获得正确的文字大小?
答案 0 :(得分:0)
所以这里有两个问题。首先,将Label
的大小调整为内容。
您已根据height
分配texture_size
,从而走上正轨。但问题是,当您第一次创建Label
时,纹理尚未渲染,因此您无法知道大小。关键是使用property bindings代替 - 每次height
更改时,texture_size
都会更新。
所以,在the kv language(在Kivy中定义UI的首选方式):
Label:
text: 'some really really really long text here'
size_hint_y: None
height: self.texture_size[1]
text_size: self.width, None
属性在kv中引用时会自动绑定。但你可以在Python中类似地做到这一点:
lbl = Label(text='some really really really long text here',
size_hint_y=None)
lbl.bind(texture_size=lambda instance, value: setattr(instance, 'height', value[1]))
lbl.bind(width=lambda instance, value: setattr(instance, 'text_size', (value, None)))
其次,存在包含BoxLayout
的问题。即使将这些Label
设置为正确的尺寸,它们所在的BoxLayout
仍然会正常布局。这是BoxLayout
的替代品,它将根据子项的大小调整自身大小(适当设置size_hint
时):
class CollapsingBoxLayout(BoxLayout):
def __init__(self, **kwargs):
super(CollapsingBoxLayout, self).__init__(**kwargs)
self._trigger_update_size = Clock.create_trigger(self._update_size)
def on_children(self, *_):
for c in self.children:
c.bind(size=self._trigger_update_size)
self._update_size()
def _update_size(self, *_):
if self.size_hint_y is None:
self.height = max(c.height for c in self.children) if self.children else 0
if self.size_hint_x is None:
self.width = max(c.width for c in self.children) if self.children else 0
最后,一个完整的工作示例:
import kivy
kivy.require('1.8.0')
from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
class CollapsingBoxLayout(BoxLayout):
def __init__(self, **kwargs):
super(CollapsingBoxLayout, self).__init__(**kwargs)
self._trigger_update_size = Clock.create_trigger(self._update_size)
def on_children(self, *_):
for c in self.children:
c.bind(size=self._trigger_update_size)
self._trigger_update_size()
def _update_size(self, *_):
if self.size_hint_y is None:
self.height = max(c.height for c in self.children) if self.children else 0
if self.size_hint_x is None:
self.width = max(c.width for c in self.children) if self.children else 0
root = Builder.load_string('''
BoxLayout:
orientation: 'vertical'
CollapsingBoxLayout:
size_hint_y: None
canvas.before:
Color:
rgba: 1, 0, 0, 0.2
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'Paragraph 1'
size_hint_x: 0.2
size_hint_y: None
height: self.texture_size[1]
text_size: self.width, None
Label:
canvas.before:
Color:
rgba: 0, 1, 0, 0.2
Rectangle:
pos: self.pos
size: self.size
size_hint_x: 0.8
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur sollicitudin dignissim orci. Pellentesque laoreet magna quis augue dictum fringilla. Vivamus nec adipiscing nunc. Aliquam pharetra auctor justo vel rutrum. Sed sodales nulla sed odio fermentum, vel pulvinar nibh hendrerit. Phasellus a volutpat leo. Donec id hendrerit velit. Curabitur eget suscipit neque, nec tincidunt nulla. Donec feugiat, urna quis porttitor aliquet, nibh est laoreet ligula, vitae vestibulum leo purus quis ante. Maecenas magna nisi, molestie eu ipsum quis, tempor tempor turpis. Vivamus a fringilla enim. Quisque aliquam elit tortor, nec mollis tellus facilisis accumsan. Phasellus sagittis commodo mauris in vestibulum. Mauris sed ultrices enim.'
size_hint_y: None
height: self.texture_size[1]
text_size: self.width, None
CollapsingBoxLayout:
size_hint_y: None
canvas.before:
Color:
rgba: 0, 0, 1, 0.2
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'Paragraph 2'
size_hint_x: 0.2
size_hint_y: None
height: self.texture_size[1]
text_size: self.width, None
Label:
canvas.before:
Color:
rgba: 0, 1, 0, 0.2
Rectangle:
pos: self.pos
size: self.size
size_hint_x: 0.8
text: 'Duis egestas dui lobortis ante rutrum, nec consectetur arcu sollicitudin. Phasellus ut felis facilisis, eleifend odio malesuada, placerat odio. Etiam convallis non mi at tempor. Nunc gravida est magna, a hendrerit nulla condimentum a. Proin tristique velit quis dui convallis, vitae sodales nunc condimentum. In sollicitudin eros augue, sit amet blandit neque accumsan eu. Mauris non risus at nisl vestibulum dignissim quis non arcu. Integer ullamcorper felis eu neque viverra placerat. Vivamus magna quam, porta ac tincidunt a, imperdiet sed purus. Phasellus tempus ac neque vel accumsan. Pellentesque ligula justo, auctor eget aliquet ultricies, volutpat scelerisque ligula. Maecenas dictum velit id neque rhoncus fermentum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur dictum enim nisl, ut elementum lectus viverra sit amet. Praesent vel tempor risus, at congue turpis. Praesent in justo lobortis, gravida lacus id, facilisis orci.'
size_hint_y: None
height: self.texture_size[1]
text_size: self.width, None
''')
class TestApp(App):
def build(self):
lbl = Label(text=('hello this is some long long text! ' * 10), size_hint_y=None)
lbl.bind(texture_size=lambda instance, value: setattr(instance, 'height', value[1]))
lbl.bind(width=lambda instance, value: setattr(instance, 'text_size', (value, None)))
root.add_widget(lbl)
return root
if __name__ == '__main__':
TestApp().run()
答案 1 :(得分:0)
问题可能是texture_size
没有更新,直到纹理实际生成并显示,这可能不会在方法结束时发生(但可能在下一帧之前发生)。因此,您通常希望绑定到属性,而不是采用它们的瞬时值。
您可以更简单地表达您的整个规则,并使用这种额外的免费功能,使用kv语言(我对您在帖子中未包含的小部件添加进行了一些猜测,但是您应该看到一般的想法。
: size_hint_y:无 height:label2.texture_size [1] 标签:#tag1 文字:' foo:' 标签:#glab2 id:label2 文字:' bar' * 50 width:int(Window.width * 0.8)#这可能通过布局更好 text_size:self.width,无
我也稍微改变了一些事情,设置根BoxLayout的高度而不是标签的高度,我认为这会更好。
当然你也可以在python中通过使用widget的bind方法重新创建相关的绑定来实现这一点(这就是kv在幕后所做的事情),但它更详细。
我有一段视频,其中包括here。
答案 2 :(得分:0)
以防万一其他人正在寻找自扩展ScrollView的解决方案,其中包含BoxLayout或GridLayout,供参考实际上是自扩展的小部件。在Ryan P的回答中,我得到了大部分的回答;但后来我意识到他正在解决一个非常具体的用例,这就是他使用max函数的原因。 我需要的是一个随着新小部件的添加而扩展的盒子。所以我只是将_update_size()的代码修改为:
def _update_size(self, *_):
temp_height = 0
if self.size_hint_y is None:
for c in self.children:
temp_height += c.height
self.height = temp_height + 10 # Add a buffer to the bottom to prevent text clipping.
if self.size_hint_x is None:
self.width = max(c.width for c in self.children) if self.children else 0
一些警告我单独留下宽度部分,因为对我的用例采用这种方式是有意义的,但它可以用类似的方式修改。
使用此方法更改Label小部件大小的最简单方法是使用remove_widget()删除小部件,然后使用kivy.lang.Builder.load_string()创建一个新小部件,并将该小部件添加到自我扩展中/折叠BoxLayout或GridLayout,它将重新计算它的大小,并将适当调整滚动视图的滚动距离。不要忘记size_hint_y:无必须等于none或不应用更新。
希望这足以帮助任何人通过边缘案例。