PySide Qt:自动垂直增长TextEdit Widget和垂直布局中小部件之间的间距

10

在此输入图片描述

我需要解决上面的小部件中的两个问题。

  1. 我想定义在图像中显示的帖子小部件之间放置的空间量(它们看起来很好,但我想知道如何完成)。
  2. 我想让文本编辑器垂直增长,而不是水平增长,这取决于它们包含的文本量。

对于问题1,填充小部件的代码如下:

self._body_frame = QWidget()
self._body_frame.setMinimumWidth(750)
self._body_layout = QVBoxLayout()
self._body_layout.setSpacing(0)
self._post_widgets = []
for i in range(self._posts_per_page):
    pw = PostWidget()
    self._post_widgets.append(pw)
    self._body_layout.addWidget(pw)

    self._body_frame.setLayout(self._body_layout)

SetSpacing(0)并没有让物体更加靠近,但是SetSpacing(100)会增加间距。

编辑

(对于问题2)我没有提到这一点,但我希望父部件有一个垂直滚动条。

我已经回答了自己的问题,但它非常啰嗦,并且基于因果关系。一篇适当编写的教程式风格的完整答案可以解决这两个问题并获得悬赏 :D

编辑2

使用下面的我的答案,我已经解决了这个问题。我现在将接受自己的答案。

enter image description here

2个回答

14

1) 布局

这里的另一个答案关于布局边距的工作方式非常不清楚,可能是错误的。实际上它非常简单明了。

  1. 布局有内容边距
  2. 小部件有内容边距

这两者都定义了它们包含的内容周围的填充。布局的边距设置为2表示在所有侧面都有2像素的填充。如果您拥有父子小部件和布局(当您组合UI时始终如此),则每个对象可以具有特定的边距,这些边距会单独生效。也就是说……一个父布局指定了2的边距,而一个子布局指定了2的边距,将有效地显示4个像素的边距(显然,在小部件具有框架的情况下,中间会有一些框架绘制)。

一个简单的布局示例说明了这一点:

w = QtGui.QWidget()
w.resize(600,400)

layout = QtGui.QVBoxLayout(w)
layout.setMargin(10)
frame = QtGui.QFrame()
frame.setFrameShape(frame.Box)
layout.addWidget(frame)

layout2 = QtGui.QVBoxLayout(frame)
layout2.setMargin(20)
frame2 = QtGui.QFrame()
frame2.setFrameShape(frame2.Box)
layout2.addWidget(frame2)

Layout Image Example

你可以看到顶层的 margin 在每个边上都是 10,而子布局在每个边上都是 20。从数学角度来说并没有什么复杂的地方。
margin 也可以按照每个边指定:
# left: 20, top: 0, right: 20, bottom: 0
layout.setContentsMargins(20,0,20,0)

您可以在布局中设置间距。间距是放置在布局的每个子元素之间的像素量。将其设置为0意味着它们紧贴在一起。间距是布局的一个特性,而边距是整个对象的一个特性。布局可以在周围有边距,也可以在其子元素之间有间距。小部件的子元素可以有自己的边距,这些边距是它们各自显示的一部分。

layout.setSpacing(10) # 10 pixels between each layout item

2)自动调整大小的QTextEdit

现在是您问题的第二部分。我相信有几种方法可以创建自动调整大小的QTextEdit。但是一种方法是观察文档中的内容更改,然后根据文档高度调整小部件:

class Window(QtGui.QDialog):

    def __init__(self):
        super(Window, self).__init__()
        self.resize(600,400)

        self.mainLayout = QtGui.QVBoxLayout(self)
        self.mainLayout.setMargin(10)

        self.scroll = QtGui.QScrollArea()
        self.scroll.setWidgetResizable(True)
        self.scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
        self.mainLayout.addWidget(self.scroll)

        scrollContents = QtGui.QWidget()
        self.scroll.setWidget(scrollContents)

        self.textLayout = QtGui.QVBoxLayout(scrollContents)
        self.textLayout.setMargin(10)

        for _ in xrange(5):
            text = GrowingTextEdit()
            text.setMinimumHeight(50)
            self.textLayout.addWidget(text)


class GrowingTextEdit(QtGui.QTextEdit):

    def __init__(self, *args, **kwargs):
        super(GrowingTextEdit, self).__init__(*args, **kwargs)  
        self.document().contentsChanged.connect(self.sizeChange)

        self.heightMin = 0
        self.heightMax = 65000

    def sizeChange(self):
        docHeight = self.document().size().height()
        if self.heightMin <= docHeight <= self.heightMax:
            self.setMinimumHeight(docHeight)

我对 QTextEdit 进行了子类化,创建了一个名为 GrowingTextEdit 的类,并将其文档发出的信号连接到一个名为 sizeChange 的槽中,该槽检查文档高度。我还包括了 heightMin 和 heightMax 属性,让您可以指定自动增长的最小和最大尺寸。如果您试用一下,您会看到当您在框中输入时,小部件会开始自动调整大小,并在您删除行时缩回。您也可以关闭滚动条。现在每个文本编辑器都有自己的滚动条,除了父滚动区域外。此外,我认为您可以向 docHeight 添加一个小的填充值,以使其扩展到足够的大小,以不显示内容的滚动条。

这种方法并不是真正的低级别方法。它使用常见的暴露信号和小部件的子成员,以便您接收状态更改的通知。利用信号来扩展现有小部件的功能是相当普遍的。

Auto-Growing widget example picture


1
谢谢!这是一个很好的问题,关于自定义大小的小部件。 - jdi

1
针对问题1:
父级小部件和布局都有边距,除了布局本身的间距参数。通过一些因果测试,很明显边距既适用于父级的外部区域,也适用于内部区域。
例如,如果将2像素的边距指定给父级,则垂直边框除了布局(在此情况下为HBox布局)的边距外还有<--2像素| 2像素-->的边距。如果布局也有2像素的边距,则水平线周围的区域将如下所示: <-- 2像素| 2像素--> <-- 2像素(L) 2像素--> (W) 编辑:也许更像这样:| 2像素-->(L) 2像素--><-- 2像素(W) 其中|是父级的垂直线,(L)是布局的垂直线,(W)是水平布局中嵌入小部件的边框。

布局的间距是一个额外的参数,它控制布局中小部件之间插入的空间量,除了任何布局边距。

上面的描述可能不准确(因此请随意在不准确的地方进行编辑),但将父级和布局的边距以及布局间距设置为零会产生您想要的结果。

对于第二点:

我认为没有直接解决这个问题的方法(您可能需要在更低的级别上挂钩,这需要更深入地了解API)。我认为您应该使用QLabel小部件而不是QTextEdit小部件。标签没有视图,因此根据需要扩展,至少默认情况下是这样工作的,只要父级没有受到其移动的限制。

因此,将QTextEdit更改为Qlabel并向父级添加滚动视图,一切都应该按照您的要求工作。我有一种感觉,您选择QTextEdit是因为它的背景。研究QT小部件中HTML的工作方式,您可能能够通过HTML修改背景。

编辑

enter image description here

这个小部件(请原谅它的大小)是在OS X上使用PyQT编写的以下代码创建的:

import sys
from PyQt4 import Qt

class PostMeta(Qt.QWidget):
    posted_at_base_text = "<b> Posted At:</b>"
    posted_by_base_text = "<b> Posted By:</b>"

    def __init__(self):
        Qt.QWidget.__init__(self)
        self._posted_by_label = Qt.QLabel()
        self._posted_at_label = Qt.QLabel()
        layout = Qt.QVBoxLayout()
        layout.setMargin(0)
        layout.setSpacing(5)
        layout.addWidget(self._posted_by_label)
        layout.addWidget(self._posted_at_label)
        layout.addStretch()
        self.setLayout(layout)
        self._posted_by_label.setText(PostMeta.posted_by_base_text)
        self._posted_at_label.setText(PostMeta.posted_at_base_text)


class FramePost(Qt.QFrame):
    def __init__(self):
        Qt.QFrame.__init__(self)
        layout = Qt.QHBoxLayout()
        layout.setMargin(10)
        self.te = Qt.QLabel()
        self.te.setStyleSheet("QLabel { background : rgb(245,245,245) }")
        self.te.setFrameStyle( Qt.QFrame.Panel |  Qt.QFrame.Sunken)
        self.te.setLineWidth(1)
        self._post_meta = PostMeta()
        layout.addWidget(self._post_meta)
        vline = Qt.QFrame()
        vline.setFrameShape(Qt.QFrame.VLine)
        layout.addWidget(vline)
        layout.addWidget(self.te)
        self.te.setText(
            """            line one
            line two
            line three
            line four
            line five
            line six
            line seven
            line eight
            line nine
            line ten
            line eleven
            line twelve
            line thirteen""")
        self.setLayout(layout)

        self.setFrameStyle(Qt.QFrame.Box)
        self.setLineWidth(2)

app = Qt.QApplication(sys.argv)
w = Qt.QWidget()
layout = Qt.QHBoxLayout()
fp = FramePost()
layout.addWidget(fp)
w.setLayout(layout)
w.show()
app.exec_()

左侧小部件中的标签显示了间距和边距微调,我使用了一个QLabel来显示帖子文本。请注意,我已经微调了标签的外观,使其看起来更像默认的QTextEdit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接