PyGTK:动态标签包装

发布于 2024-08-14 01:27:23 字数 2900 浏览 6 评论 0原文

这是一个已知错误/问题,当父母的变化。这是那些非常烦人的小细节之一,如果可能的话我想解决它。

我遵循了 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()

It's a known bug/issue that a label in GTK will not dynamically resize when the parent changes. It's one of those really annoying small details, and I want to hack around it if possible.

I followed the approach at 16 software, but as per the disclaimer you cannot then resize it smaller. So I attempted a trick mentioned in one of the comments (the set_size_request call in the signal callback), but this results in some sort of infinite loop (try it and see).

Does anyone have any other ideas?

(You can't block the signal just for the duration of the call, since as the print statements seem to indicate, the problem starts after the function is left.)

The code is below. You can see what I mean if you run it and try to resize the window larger and then smaller. (If you want to see the original problem, comment out the line after "Connect to the size-allocate signal", run it, and resize the window bigger.)

The Glade file ("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>

The Python code:

#!/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()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

情深如许 2024-08-21 01:27:23

VMware 的 libview 有一个名为 WrapLabel 的小部件,它应该可以完成您想要的操作,但它是用 C++ 编写的。 Python 翻译可在 Meld 存储库 中找到(与 busybox.py 分开)。

VMware's libview has a widget called WrapLabel which should do what you want, but it's in C++. A Python translation is available in the Meld repository (separated out from busybox.py).

无所谓啦 2024-08-21 01:27:23

这是killown 解决方案的一行变体:

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

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

不知道为什么宽度有一个“-1”,但这似乎无害!

Here's a one-line variation on killown's solution:

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

The above will make sure that the label takes on the width allocated to it, so that word-wrapping is happy.

Not sure why there's a "-1" for the width, but it seems harmless!

岁月苍老的讽刺 2024-08-21 01:27:23

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

编辑:

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()

example for resize and wrap the label dynamically:

EDIT:

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()
黯然 2024-08-21 01:27:23

你可以用这个。不确定它最初来自哪里。创建标签,然后调用 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)

You can use this. Not sure where it came from originally. Create your label and then call 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)
忆悲凉 2024-08-21 01:27:23

在 GTK 3 中,这是使用高度换宽度和宽度换高度尺寸请求自动完成的。

In GTK 3, this is done automatically using height-for-width and width-for-height size requests.

感性 2024-08-21 01:27:23

我只是想分享如何使用 wraplabel.py

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

所以现在唯一的问题是将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 并禁用编辑、光标和灵敏度。然而,TextView 的颜色与背景不同,这在 Gtk 主题面前很难修复。此解决方案的另一个问题是 TextView 捕获鼠标滚动事件。这使得鼠标滚动其中包含这些 TextView 的框非常不稳定。我尝试了很多方法来解决鼠标滚动问题,但从未弄清楚。否则使用 TextViews 确实有效。因此,如果您的文本标签不在滚动窗格内并且 WrapLabel 解决方案不适合您,您可能会考虑这一点。

I just wanted to share how I made Kai's solution work with PyGtk and Glade-3 using wraplabel.py.

I didn't want to have to modify Glade catalogs to get WrapLabel in Glade and I'm not sure if that would work anyway with a PyGtk component. I was however pleasantly surprised to find that simply by putting the WrapLabel class in the python environment before calling into gtk.Bilder() it will load the class as a component.

So now the only problem was to get the WrapLabels into the glade file. First I changed the names of all the labels I wanted to wrap to wlabel###, where ### is some number. Then I used a sed expression to replace the classes, but since I didn't want to add extra processing to the build system I ended up adding the following in 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)

I'm sure there are more elegant ways to do the substitution but this worked well. However, I found I had one more problem. When I opened one of the dialogs with the wrapped labels some of the text was not visible. Then when I resized the window with the mouse, even a little bit, everything would snap in to place. Some how the labels were not getting the right sizes when initialized. I fixed this with another work around. When opening one of the dialogs I run this code:

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)

This just sets the size one point too big, presents the window and then resets to the correct size. This way the WrapLabels get the signal to resize after the dialog layout is complete.

There is still one small glitch. When you open the dialog sometimes you can see the text snapping in to place. Otherwise, it seems to work.

NOTE 1) All the variations of calling label.set_size_request(size.width - 1, -1) on size-allocate caused the GUI to lockup for me. Probably depends on the parent widgets.

NOTE 2) Another solution is to use TextView's and disable editing, the cursor and sensitivity. However, TextViews have a different color than the background which is difficult to fix in the face of Gtk themes. The other problem with this solution is that TextViews capture mouse scroll events. This makes mouse scrolling a box with these TextViews inside it very erratic. I tried many things to solve the mouse scroll problem but never did figure it out. Otherwise using TextViews does work. So you might consider this if your text labels are not inside a scroll pane and the WrapLabel solution doesn't work for you.

神妖 2024-08-21 01:27:23

我修改了其他答案中的代码以获得性能更好一点的回调:

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 数字是通过反复试验获得的。最好从小部件中的某个位置获取它们,但我找不到与小部件的任何关系。这使得标签在增长和收缩时都能很好地调整大小,并且线条不会被切断。

I have modified the code that was in the other answers to get a callback that behaved a little better:

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)

The -20 and +6 numbers were obtained by trial and error. It would be nice to get them from somewhere in the widgets, but I couldn't find any relationship to the widgets. This makes the label resize fine both in growing and shrinking and lines are not cut.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文