You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Gtk4垂直布局中如何将按钮栏固定在窗口底部

Gtk4垂直布局中如何将按钮栏固定在窗口底部

嗨,我来帮你搞定这个布局问题~ 你遇到的核心问题是没搞清楚Gtk4里Box布局中expand和valign的配合逻辑,光设置valign是不够的,得让中间的滚动窗口抢占剩余空间,才能把底部按钮挤到窗口最下方。

问题原因

在垂直方向的Gtk.Box里,子元素默认只会占用自身需要的最小空间,valign=Gtk.Align.FILL只有当元素被分配了比自身自然高度更大的空间时才会生效。你之前只给滚动窗口设置了valign=FILL,但没让它去抢占窗口的剩余空间,所以它还是只占自己需要的高度,底部按钮自然不会被推到底部。

解决步骤

你只需要做两个关键调整:

  1. 给中间的ScrolledWindow设置expand=True,让它填充垂直方向上除了顶部控件区和底部按钮区之外的所有剩余空间
  2. 确保底部按钮容器的valign=Gtk.Align.END(你已经设置了,这部分保留即可)

另外我还发现你代码里有个小bug:底部两个按钮都用了clear变量,第二个按钮会覆盖第一个的绑定,我也顺便帮你修正了。

修改后的完整代码

#!/usr/bin/python3

import gi
gi.require_version("Gtk", "4.0")
from gi.repository import Gtk, Gio, GObject, GLib

class TestItem(GObject.Object):
    __gtype_name__ = "TestItem"
    name = GObject.Property(type=str)
    value = GObject.Property(type=int)

    def __init__(self, name: str, value: int):
        super().__init__()
        self.name = name
        self.value = value

def make_table_column(title, field):
    """
    Make a column for a field
    """
    template = b"""<interface>
  <template class="GtkListItem">
    <property name="child">
      <object class="GtkLabel">
        <binding name="label">
          <lookup name="%s" type="TestItem">
            <lookup name="item">GtkListItem</lookup>
          </lookup>
        </binding>
      </object>
    </property>
  </template>
</interface>
""" % (field.encode('ascii'),)
    template = GLib.Bytes.new(template)
    factory = Gtk.BuilderListItemFactory.new_from_bytes(None, template)
    col = Gtk.ColumnViewColumn(title = title, factory = factory,
                               resizable = True)
    return col
        
class Test(Gtk.ApplicationWindow):
    def __init__(self, **kargs):
        super().__init__(**kargs, title="Test")        
        
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)

        control = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
        control.append(Gtk.Label(label = "Name:"))
        self.name = Gtk.Entry()
        control.append(self.name)
        control.append(Gtk.Label(label = "Value:"))
        self.value = Gtk.SpinButton.new_from_range(1, 100, 1)
        control.append(self.value)
        button = Gtk.Button.new_from_icon_name('list-add')
        button.connect("clicked", self.add)
        control.append(button)
        vbox.append(control)

        self.model = Gio.ListStore(item_type = TestItem)
        view = Gtk.ColumnView()
        self.selection = Gtk.NoSelection(model = self.model)
        view.set_model(self.selection)

        view.append_column(make_table_column("Item name", 'name'))
        view.append_column(make_table_column("Value", 'value'))
        
        scroll = Gtk.ScrolledWindow()
        scroll.set_child(view)
        scroll.set_propagate_natural_width(True)
        scroll.set_propagate_natural_height(True)
        scroll.set_valign(Gtk.Align.FILL)
        # 关键修改:让滚动窗口抢占剩余垂直空间
        scroll.set_expand(True)
        vbox.append(scroll)
        
        buttons = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
        buttons.set_valign(Gtk.Align.END)
        clear = Gtk.Button.new_from_icon_name('edit-clear')
        clear.connect("clicked", self.clear)
        # 修正变量名重复的bug
        increase_btn = Gtk.Button.new_from_icon_name('list-add')
        increase_btn.connect("clicked", self.increase)
        buttons.append(clear)
        buttons.append(increase_btn)
        vbox.append(buttons)
        
        self.set_child(vbox)
        self.set_default_size(600, 400)
        
    def add(self, _widget):
        name = self.name.get_buffer().get_text()
        value = int(self.value.get_value())
        item = TestItem(name, value)
        self.model.insert(0, item)

    def increase(self, _widget):
        for item in self.model:
            item.value += 1
        
    def clear(self, _widget):
        self.model.remove_all()

def on_activate(app):
    win = Test(application=app)
    win.present()

app = Gtk.Application(application_id='org.kilobug.test')
app.connect('activate', on_activate)
app.run(None)

效果说明

修改之后,不管你怎么调整窗口大小,底部的按钮栏都会牢牢固定在窗口底部,中间的ColumnView会自动填充剩下的所有空间,完全符合你想要的效果~

备注:内容来源于stack exchange,提问作者Kilobug

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 18:03:02