如何调整标签的宽度?

Posted

技术标签:

【中文标题】如何调整标签的宽度?【英文标题】:How do I resize a Label's width? 【发布时间】:2017-02-18 08:10:30 【问题描述】:

一段时间以来,我一直在尝试调整标签的宽度,但这似乎是不可能的,或者至少对我来说是不可理解的......

为了清楚起见,我将用图像解释我的情况。

这是带有“正常”标签的窗口的外观...

这就是窗口看起来非常大的文本......

现在,这就是我希望它在文本大得离谱时的样子

看到了吗?

示例代码如下:

# -*- coding: utf-8 -*-

import gi
import signal
import hashlib
import random
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Pango

class Test(Gtk.Window):
    def __init__(self):
        def center(a):
            a.props.valign = Gtk.Align.CENTER
            a.props.halign = Gtk.Align.CENTER
        Gtk.Window.__init__(self, title="Test")
        self.set_position(Gtk.WindowPosition.CENTER)
        self.set_border_width(10)
        self.set_resizable(False)
        self.connect("delete-event", Gtk.main_quit)

        Box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
        Button = Gtk.Button("Change Label")
        Label = Gtk.Label()

        Test = "I'm a Label, Yay!"

        center(Box)
        center(Label)
        center(Button)

        Label.set_markup('<span><big>%s</big></span>' % Test)
        Label.set_ellipsize(Pango.EllipsizeMode.END)
        Label.set_justify(Gtk.Justification.LEFT)

        self.add(Box)

        Box.add(Label)
        Box.add(Button)

        #UNKNOWN/FORBIDDEN/DEPRECIATED/INCOMPREHENSIBLE MAGIC!
            #Uncomment only one or both, and watch it's magic!

        #self.set_resizable(True)
        #Box.set_resize_mode(Gtk.ResizeMode.QUEUE)

        def change_label(self):
            Label.set_markup('<span><big>%s</big></span>' % str(hashlib.sha384(str(random.random())).hexdigest()))

        Button.connect("clicked", change_label)

if __name__ == '__main__':
    MainWindow = Test()
    MainWindow.show_all()
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    Gtk.main()

请注意,窗口不可调整大小。

如果您取消注释“魔术”部分中的一个或两个,您将看到窗口如何改变它的行为!

但是

使用这两种方法都有两个缺点:

如果您取消注释 #self.set_resizable(True) WINDOW 将再次变得可调整大小,我不希望这种情况发生

#Box.set_resize_mode(Gtk.ResizeMode.QUEUE) 似乎是“完美”的解决方案,但尽管它可以满足我的要求,但在最近的 Gtk 版本中它似乎已被“弃用”。

此外,在其他一些窗口管理器和主题中,当您多次更改标签的字符串时,它会导致窗口的尺寸出现一些中断。

您对此有什么建议吗?

我完全受够了这个。我已经尝试了几个星期,但没有结果:c

也许有一种方法可以模拟 Window 可调整大小时的行为,而不显示 Resize Grips & Maximize Button?

或者set_resize_mode(Gtk.ResizeMode.QUEUE) 的更新方法是什么?

PD:Label.set_max_width_chars() 不是我要搜索的内容

【问题讨论】:

你能解释一下为什么set_max_width_chars() 不是一个选项吗? 因为这会将单词限制为您请求的指定长度。这在某些情况下会起作用,但是让我们假设您可能想要添加德语翻译。例如,在英语中,您可以将 Label 的 Text 设置为“awesome”和 `Label.set_max_width_chars(7)。在德语中,Label 的文本是“Ehrfurcht gebietend”(至少谷歌翻译这么说)。 我明白了。您能否使用字符串长度来设置宽度值,使其与初始文本匹配,然后如果标签更改,宽度保持不变。 我想要做的是告诉标签、标签的父级或窗口本身在实际宽度内显示尽可能多的文本。我不想使用Label.set_max_width_chars() 手动“限制”标签的宽度。我需要它是这样的:“嘿,对不起,我显示了我能显示的所有文本,这不是我的错”我不希望它依赖于手动声明的值,只依赖于实际窗口的宽度,这可能会有所不同.你为什么不试试我的示例代码或多或少地了解我想要实现的目标? 我发现了一些有趣的东西:developer.gnome.org/pygtk/stable/… 如果我理解正确,它说 Gtk.Label 自动调用queue_resize() 每当它检测到它自己的标签发生变化时,我正在考虑: * 有没有办法“停用”这种行为,所以 Gtk.Label 不会自动调整大小。也许抑制对queue.resize 的调用也许我们应该创建一个不调用queue.resize 的自定义Gtk.Label 【参考方案1】:

经过数小时的研究和试验,我找到了另一种完成这项任务的好方法。在我看来,这是最好的方法,除非它可能会受到一点限制,但它可以让你用更少的代码做更多的事情。

这是我所做的:

从 1° 方法中,我删除了“hackish”代码 然后,我添加了一个“Mini-Box”对象,它是一个 Gtk.ButtonBox(一种特殊的盒子...)并将其设置为“Homogeneous” 之后,我从“Box”中删除了“Button”对象,并将“Label”和“Button”对象都添加到了“Mini-Box”中 更不用说我还必须将“Mini-Box”添加到“Box”中......

完成!

示例代码如下:

#!/usr/bin/python2

# -*- coding: utf-8 -*-

import gi
import signal
import hashlib
import random
import time
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Pango

class TestWindow(Gtk.Window):

    def __init__(self):

        Gtk.Window.__init__(self, title="Test")
        self.set_position(Gtk.WindowPosition.CENTER)
        self.set_border_width(10)
        self.set_resizable(False)
        self.connect("delete-event", Gtk.main_quit)

        ##Declare Objects

        #Boxes
        Box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
        Mini_Box = Gtk.ButtonBox(orientation=Gtk.Orientation.HORIZONTAL, spacing=10, homogeneous=True)

        #Buttons
        Button = Gtk.Button.new_with_mnemonic("_Randomize Label")

        #Labels
        Label = Gtk.Label()

        #Variables & Misc
        Test = "I'm a Label, Yay!" #Label's width should NOT be larger than the Button's size to be displayed entirely, though...

        ##Change Objects Properties

        #Alignments
        self.center(Box)

        #Labels
        Label.set_markup('<span><big>%s</big></span>' % Test)
        Label.set_ellipsize(Pango.EllipsizeMode.END)
        Label.set_justify(Gtk.Justification.LEFT)

        ##Packaging

        #Boxes
        self.add(Box)

        Box.add(Mini_Box)
        Mini_Box.add(Label)
        Mini_Box.add(Button)

        def change_label(self):
            Label.set_markup('<span><big>%s</big></span>' % str(hashlib.sha384(str(random.random())).hexdigest()).rstrip())

        Button.connect("clicked", change_label)

    def center(self, object):
        object.props.valign = Gtk.Align.CENTER
        object.props.halign = Gtk.Align.CENTER

if __name__ == '__main__':
    MainWindow = TestWindow()
    MainWindow.show_all()
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    Gtk.main()

不会发新的截图,因为结果和上面一样……

【讨论】:

我认为这将是我对这个话题的“权威”回答。支持它,以便人们首先阅读此内容【参考方案2】:

好吧,经过一番思考,我找到了一种“骇人听闻”的方式来实现我的目标。

我会尝试用文字来解释它,然后我会发布“最终”代码,但我相信它可以被优化,或者至少这是我希望的 :)

我所做的是获取这两个代码(Jacques Gaudin 的原始和修改版本),分析它们并创建一个不太复杂的版本。

然后我意识到 Jacques Gaudin 的修改使 Window set_size_request 依赖于开发人员,因为它需要宽度和高度。

作为“解决方法”,我决定执行以下操作:

清理代码 让窗口不受限制地“出现”,这样它就可以计算出正确显示所需的确切宽度和高度,并立即隐藏窗口以避免用户过早按下“随机按钮”。 一旦 Window 隐藏了自己,请求它的确切值(宽度和高度) 收集这些值后,当窗口仍处于隐藏状态时,重新声明 MainWindow 对象,但这次将其参数从 (None, None) 更改为之前收集的值:(Width, Height) 接下来,由于更改其参数,Window 需要重新计算一些内部值 最后,一旦窗口完成重新计算值,它会再次显示自身,但这次以所需的宽度和高度正确显示并且即使由于某种原因标签增长,窗口将保持相同的大小.

我知道这不是“最佳”答案,但至少它有效。

很遗憾,还有几个问题:

我们如何才能将标签“居中”? 如果这个方法用在慢机器上,会不会窗口在隐藏时会变慢,如果用户过早按下“随机化”按钮,会导致某种错误/故障? 动态文本翻译怎么样?他们甚至可能不需要“重新启动”窗口吗? (嗯,这不是什么大问题,但仍然好奇地想一想……) 有没有更好的方法来实现这一点?(最重要的问题:c)

代码如下:

#!/usr/bin/python2

# -*- coding: utf-8 -*-

import gi
import signal
import hashlib
import random
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Pango

class TestWindow(Gtk.Window):
    
    def __init__(self, width, height):
        
        if width is None:
            width = -1
            
        if height is None:
            height = -1
            
        Gtk.Window.__init__(self, title="Test")
        self.set_position(Gtk.WindowPosition.CENTER)
        self.set_border_width(10)
        self.set_resizable(False)
        self.set_size_request(width, height)
        self.connect("delete-event", Gtk.main_quit)
        
        ##Declare Objects
        
        #Boxes
        Box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
        
        #Buttons
        Button = Gtk.Button.new_with_mnemonic("Randomize Label")
        
        #Labels
        Label = Gtk.Label()
        
        #Variables & Misc
        Test = "I'm a Label, Yay!" #You can change me for a bigger label if you want
        
        ##Change Objects Properties
        
        #Alignments (Could break things...)
        #self.center(Box)
        #self.center(Label)
        #self.center(Button)
        
        #Labels
        Label.set_markup('<span><big>%s</big></span>' % Test)
        Label.set_ellipsize(Pango.EllipsizeMode.END)
        
        if width is not -1:
            Label.set_max_width_chars(1)
            
        Label.set_justify(Gtk.Justification.FILL)
        
        ##Packaging
        
        #Boxes
        self.add(Box)
        
        Box.pack_start(Label, True, True, 0)
        Box.add(Button)
        
        def change_label(self):
            Label.set_markup('<span><big>%s</big></span>' % str(hashlib.sha384(str(random.random())).hexdigest()))
            
        Button.connect("clicked", change_label)
            
    def center(self, object):
        object.props.valign = Gtk.Align.CENTER
        object.props.halign = Gtk.Align.CENTER
        
if __name__ == '__main__':
    MainWindow = TestWindow(None, None)
    MainWindow.show_all()
    MainWindow.hide()
    Width, Height = MainWindow.get_size()
    MainWindow = TestWindow(Width, Height)
    MainWindow.show_all()
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    Gtk.main()

以下是一些截图:

这是“在”按下按钮之前...

这是“按下按钮后”...

【讨论】:

【参考方案3】:

您可能想了解 Gtk 3.12 中的帧时钟。这是一个很好的article。 用于排队调整大小的新方法是 Gtk.Widget.queue_resize()

如果某些状态更改导致您的小部件的大小发生变化,您调用 gtk_widget_queue_resize() 它将请求布局阶段并将您的小部件标记为需要重新布局。

我相信下面修改后的示例代码可以满足您的需求。诀窍在于有子框来包围Gtk.LabelGtk.Button。然后在标签上将max_width_chars 设置为1,将ellipsize 设置为END,将hexpand 设置为True,并将halign 设置为FILL

import signal
import hashlib
import random
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Pango

class Test(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self, title="Test")
        self.set_size_request(250,-1)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.set_border_width(10)
        self.set_resizable(False)
        self.connect("delete-event", Gtk.main_quit)

        Box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
        Sub_box_left = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        Sub_box_right = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        Button = Gtk.Button("Change Label")
        Label = Gtk.Label()

        Test = "I'm a Label, Yay!"

        Label.set_markup('<span><big>%s</big></span>' % Test)
        Label.set_max_width_chars(1)
        Label.set_ellipsize(Pango.EllipsizeMode.END)
        Label.set_hexpand(True)
        Label.props.halign = Gtk.Align.FILL
        Label.set_justify(Gtk.Justification.LEFT)

        self.add(Box)

        Sub_box_left.add(Label)
        Sub_box_right.add(Button)

        Box.add(Sub_box_left)
        Box.add(Sub_box_right)

        def change_label(self):
            Label.set_markup('<span><big>%s</big></span>'%str(hashlib.sha384(str(random.random())).hexdigest()))

        Button.connect("clicked", change_label)

if __name__ == '__main__':
    MainWindow = Test()
    MainWindow.show_all()
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    Gtk.main()

【讨论】:

我根据上面的讨论编辑了我的答案。我希望这是您想要实现的目标。 确实,这是一个很好的方法。我决定分析这两个代码,发现实际上你可以修改更少的“我的”代码来实现你的能力。 例如:* 如果您添加Label.set_max_width_chars(1)Label.set_hexpand(True)self.set_size_request(250,-1) 并删除center(Box)center(Label)center(Button),您将获得与使用相同的效果“你的”代码 此外,还有另一种设置Label.set_hexpand(True) 的方法。您需要使用Box.pack_start(Label, True, True, 0) 将标签正确包装在盒子内。这样,您就是在告诉 Box 让 Label 展开并填充到可用空间中。这样,您就不需要 Label.set_hexpand(True)Label.props.valign = Gtk.Align.CENTER 很高兴它终于奏效了。我发布的代码只是这个想法的一个说明。您可以根据自己的喜好进行调整。 另外额外的盒子应该不再是必要的了......这里的缺点是:我们如何“居中”标签? center(Label) 将覆盖 Label.set_hexpand(True)Box.pack_start(Label, True, True, 0) 我也忘了提到,尽管付出了所有努力,如果你添加和删除所有这些代码行,如果你删除 self.set_size_request(250,-1) 一切都会丢失。这与将Label.set_max_width_chars(x) 添加到 1° 代码几乎相同,因为无论如何您都在为容器手动设置固定大小,我试图避免这种情况

以上是关于如何调整标签的宽度?的主要内容,如果未能解决你的问题,请参考以下文章

如何自动调整标签大小以适应固定宽度的容器?

使用 HTML 和 CSS 打印标签。如何根据标签的高度和宽度调整标签和容器的大小?

如何在自定义 tableView Cell 中调整标签高度和宽度

如何使用自动布局调整 UILabel 宽度以适合文本?

iPhone - 根据文字调整 UILabel 宽度

当视图宽度发生变化时,如何使 UILabel 正确调整其尺寸?