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

Gtk4中如何旋转控件?Gtk3迁移至Gtk4的Label旋转方案问询

在Gtk4中实现旋转Label(替代Gtk3的set_angle)

Gtk4移除了GtkLabel的set_angle方法,直接用CSS transform会导致布局尺寸不匹配(仅视觉旋转,不影响控件布局占位)。正确的做法是自定义一个包裹GtkLabel的Widget,通过重写measure和size_allocate方法来处理尺寸交换和旋转变换,确保布局和视觉一致。

可运行实现示例(Python/PyGObject)

import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk, Gsk, Gdk

class RotatedLabel(Gtk.Widget):
    def __init__(self, label_text, angle=90):
        super().__init__()
        self.label = Gtk.Label(label=label_text)
        self.label.set_parent(self)
        self.angle = angle  # 旋转角度(单位:度)

    def do_measure(self, orientation, for_size):
        # 旋转后,控件的宽度对应原Label的高度,高度对应原Label的宽度
        if orientation == Gtk.Orientation.HORIZONTAL:
            child_min_h, child_nat_h = self.label.measure(Gtk.Orientation.VERTICAL, for_size)
            return child_min_h, child_nat_h, -1, -1
        else:
            child_min_w, child_nat_w = self.label.measure(Gtk.Orientation.HORIZONTAL, for_size)
            return child_min_w, child_nat_w, -1, -1

    def do_size_allocate(self, width, height, baseline):
        # 给Label分配交换后的宽高(旋转后原高度变宽度,原宽度变高度)
        child_width = height
        child_height = width

        # 让Label在分配的区域内居中
        x = (width - child_height) // 2
        y = (height - child_width) // 2

        # 创建旋转变换:围绕Label中心旋转
        transform = Gsk.Transform.new()
        # 平移到Label中心位置
        transform = transform.translate((x + child_width/2, y + child_height/2))
        # 转换角度为弧度并旋转
        transform = transform.rotate(self.angle * 3.14159265 / 180)
        # 平移回原点,确保旋转中心正确
        transform = transform.translate((-child_width/2, -child_height/2))

        self.label.set_transform(transform)
        self.label.size_allocate(child_width, child_height, baseline)

# 测试窗口
class TestWindow(Gtk.ApplicationWindow):
    def __init__(self, app):
        super().__init__(application=app, title="Rotated Label Demo")
        self.set_default_size(400, 300)

        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=20)
        box.set_margin_top(50)
        box.set_margin_start(50)
        self.set_child(box)

        # 普通标签
        normal_label = Gtk.Label(label="Normal Label")
        box.append(normal_label)

        # 顺时针旋转90度的标签
        rotated_label = RotatedLabel("Rotated 90°", angle=90)
        box.append(rotated_label)

        # 逆时针旋转90度的标签
        rotated_label2 = RotatedLabel("Rotated -90°", angle=-90)
        box.append(rotated_label2)

class MyApp(Gtk.Application):
    def __init__(self):
        super().__init__(application_id="com.example.RotatedLabelDemo")

    def do_activate(self):
        win = TestWindow(self)
        win.present()

if __name__ == "__main__":
    app = MyApp()
    app.run()

关键实现说明

  1. 重写do_measure方法

    • 旋转后控件的布局尺寸会交换:原Label的高度变为旋转后控件的宽度,原Label的宽度变为旋转后控件的高度。通过交换子控件的测量结果,让布局系统正确计算控件的占位空间。
  2. 重写do_size_allocate方法

    • 给内部Label分配交换后的宽高,确保文本有足够的绘制空间。
    • 通过Gsk.Transform实现围绕中心旋转:先将坐标平移到Label中心,旋转后再平移回原点,避免旋转后文本偏移或超出控件边界。
  3. 为什么CSS transform不适用?
    CSS的transform仅在绘制阶段生效,不会改变布局系统计算的控件尺寸,导致旋转后的文本超出控件占位范围,出现重叠或截断问题。而自定义Widget的方式让布局系统感知到旋转后的尺寸,确保整体布局协调。

内容的提问来源于stack exchange,提问作者Thayne

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 14:47:20