PyGTK:动态标签包装

时间:2009-12-12 15:24:04

标签: pygtk

当父级更改时,GTK中的标签不会动态调整大小known bug/issue。这是其中一个非常讨厌的小细节,如果可能的话,我想破解它。

我在16 software处遵循了该方法,但根据免责声明,您无法将其调整为较小。所以我尝试了其中一条评论中提到的一个技巧(信号回调中的set_size_request调用),但这会产生某种无限循环(试试看看)。

有没有人有其他想法?

(你不能在调用期间阻止信号,因为print语句似乎表明,问题在函数离开后开始。)

代码如下。你可以看到我的意思,如果你运行它并尝试调整窗口大小然后更小。 (如果您想查看原始问题,请在“连接到大小分配信号”之后注释掉该行,运行它,然后将窗口调整得更大。)

Glade文件(“example.glade”):

<?xml version="1.0"?>
<glade-interface>
  <!-- interface-requires gtk+ 2.16 -->
  <!-- interface-naming-policy project-wide -->
  <widget class="GtkWindow" id="window1">
    <property name="visible">True</property>
    <signal name="destroy" handler="on_destroy"/>
    <child>
      <widget class="GtkLabel" id="label1">
        <property name="visible">True</property>
        <property name="label" translatable="yes">In publishing and graphic design, lorem ipsum[p][1][2] is the name given to commonly used placeholder text (filler text) to demonstrate the graphic elements of a document or visual presentation, such as font, typography, and layout. The lorem ipsum text, which is typically a nonsensical list of semi-Latin words, is a hacked version of a Latin text by Cicero, with words/letters omitted and others inserted, but not proper Latin[1][2] (see below: History and discovery). The closest English translation would be "pain itself" (dolorem = pain, grief, misery, suffering; ipsum = itself).</property>
        <property name="wrap">True</property>
      </widget>
    </child>
  </widget>
</glade-interface>

Python代码:

#!/usr/bin/python

import pygtk
import gobject
import gtk.glade

def wrapped_label_hack(gtklabel, allocation):
    print "In wrapped_label_hack"
    gtklabel.set_size_request(allocation.width, -1)
    # If you uncomment this, we get INFINITE LOOPING!
    # gtklabel.set_size_request(-1, -1)
    print "Leaving wrapped_label_hack"

class ExampleGTK:

    def __init__(self, filename):
        self.tree = gtk.glade.XML(filename, "window1", "Example")
        self.id = "window1"
        self.tree.signal_autoconnect(self)

        # Connect to the size-allocate signal
        self.get_widget("label1").connect("size-allocate", wrapped_label_hack)

    def on_destroy(self, widget):
        self.close()

    def get_widget(self, id):
        return self.tree.get_widget(id)

    def close(self):
        window = self.get_widget(self.id)
        if window is not None:
            window.destroy()
        gtk.main_quit()

if __name__ == "__main__":
    window = ExampleGTK("example.glade")
    gtk.main()

7 个答案:

答案 0 :(得分:4)

以下是killown解决方案的单行变体:

label.connect('size-allocate', lambda label, size: label.set_size_request(size.width - 1, -1))

以上将确保标签占用分配给它的宽度,以便自动换行。

不确定为什么宽度为“-1”,但它似乎无害!

答案 1 :(得分:3)

VMware的libview有一个名为WrapLabel的小部件应该可以做你想要的,但它是用C ++编写的。 Meld repository中提供了Python翻译(与busybox.py分开)。

答案 2 :(得分:3)

调整大小并动态包装标签的示例:

编辑:

import gtk

class DynamicLabel(gtk.Window):
    def __init__(self):
        gtk.Window.__init__(self)

        self.set_title("Dynamic Label")
        self.set_size_request(1, 1)
        self.set_default_size(300,300) 
        self.set_position(gtk.WIN_POS_CENTER)

        l = gtk.Label("Dynamic Label" * 10)
        l.set_line_wrap(True)
        l.connect("size-allocate", self.size_request)

        vbox = gtk.VBox(False, 2)
        vbox.pack_start(l, False, False, 0)

        self.add(vbox)
        self.connect("destroy", gtk.main_quit)
        self.show_all()

    def size_request(self, l, s ):
        l.set_size_request(s.width -1, -1)

DynamicLabel()
gtk.main()

答案 3 :(得分:2)

你可以用它。不知道它最初来自哪里。创建标签,然后调用label_set_autowrap(label)

def label_set_autowrap(widget): 
    "Make labels automatically re-wrap if their containers are resized.  Accepts label or container widgets."
    # For this to work the label in the glade file must be set to wrap on words.
    if isinstance(widget, gtk.Container):
        children = widget.get_children()
        for i in xrange(len(children)):
            label_set_autowrap(children[i])
    elif isinstance(widget, gtk.Label) and widget.get_line_wrap():
        widget.connect_after("size-allocate", _label_size_allocate)


def _label_size_allocate(widget, allocation):
    "Callback which re-allocates the size of a label."
    layout = widget.get_layout()
    lw_old, lh_old = layout.get_size()
    # fixed width labels
    if lw_old / pango.SCALE == allocation.width:
        return
    # set wrap width to the pango.Layout of the labels
    layout.set_width(allocation.width * pango.SCALE)
    lw, lh = layout.get_size()  # lw is unused.
    if lh_old != lh:
        widget.set_size_request(-1, lh / pango.SCALE)

答案 4 :(得分:1)

在GTK 3中,这是使用height-for-width和width-to-height size请求自动完成的。

答案 5 :(得分:1)

我只是想分享一下我使用wraplabel.py如何使Kai的解决方案与PyGtk和Glade-3一起使用。

我不想修改Glade目录以获得Glade中的WrapLabel,我不确定这是否适用于PyGtk组件。然而,令我惊喜地发现,只需在调用 gtk.Bilder()之前将 WrapLabel 类放在python环境中,它就会将该类作为组件加载。< / p>

所以现在唯一的问题是将 WrapLabels 放入林间空地文件中。首先,我将要包装的所有标签的名称更改为 wlabel ### ,其中 ### 是一些数字。然后我使用sed表达式替换类,但由于我不想在构建系统中添加额外的处理,我最终在python中添加了以下内容:

import re
import gtk
from wraplabel import WrapLabel

. . .

# Filter glade
glade = open(filename, 'r').read()
glade = re.subn('class="GtkLabel" id="wlabel',
                'class="WrapLabel" id="wlabel', glade)[0]

# Build GUI
builder = gtk.Builder()
builder.add_from_string(glade)

我确信有更优雅的方式来进行替换,但效果很好。但是,我发现我还有一个问题。当我用包装标签打开其中一个对话框时,一些文本不可见。然后,当我用鼠标调整窗口大小时,即使是一点点,一切都会卡入到位。一些标签在初始化时没有得到正确的尺寸。我用另一种解决方法解决了这个问题打开其中一个对话框时,我运行以下代码:

def open_dialog(self, dialog):
    # Hack to make WrapLabel work
    dims = dialog.get_size()
    dialog.resize(dims[0] + 1, dims[1] + 1)
    dialog.present()
    dialog.resize(*dims)

这只是将大小设置为一个点太大,呈现窗口然后重置为正确的大小。这样, WrapLabels 会在对话框布局完成后获取调整大小的信号。

还有一个小故障。有时您打开对话框时可以看到文本卡入到位。否则,似乎有效。

注1)在 size-allocate 上调用 label.set_size_request(size.width - 1,-1)的所有变体都会导致GUI锁定。可能取决于父窗口小部件。

注2)另一种解决方案是使用TextView并禁用编辑,光标和灵敏度。但是,TextViews具有与背景不同的颜色,这在Gtk主题面前难以修复。此解决方案的另一个问题是TextViews捕获鼠标滚动事件。这使得鼠标在其中滚动一个带有这些TextView的框非常不稳定。我尝试了很多东西来解决鼠标滚动问题,但从未弄明白。否则使用TextViews确实有效。因此,如果文本标签不在滚动窗格内并且WrapLabel解决方案不适合您,则可以考虑这一点。

答案 6 :(得分:0)

我修改了其他答案中的代码,以获得一个表现更好的回调:

def on_label_size_allocate(self, label, allocation, *args):
  """ Callback that re-allocates the size of a label to improve word wrap. """
  layout = label.get_layout()
  layout.set_width((allocation.width-20) * pango.SCALE)
  _, lh = layout.get_pixel_size()
  label.set_size_request(-1, lh+6)

通过反复试验获得-20和+6数字。从窗口小部件的某个地方获取它们会很好,但我找不到与窗口小部件有任何关系。这使得标签在生长和收缩时都可以很好地调整大小,并且不会切割线条。