PySide Qt: автоматический вертикальный рост для виджета TextEdit и расстояние между виджетами в вертикальной компоновке

enter image description here

Мне нужно решить две проблемы с моим выше виджет.

  1. Я хотел бы иметь возможность определить объем пространства между виджетами post, показанными на изображении (они выглядят нормально, но я хочу знать, что это сделано).
  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 ответов


1) макеты

другой ответ здесь очень неясен и, возможно, выключен о том, как работают поля макета. На самом деле очень проста.

  1. макеты имеют поля содержимого
  2. виджеты имеют поля содержимого

оба они определяют заполнение вокруг того, что они содержат. Параметр поля 2 на макете означает 2 пикселя заполнения со всех сторон. Если у вас есть родительско-дочерние виджеты и макеты, что всегда так при создании пользовательского интерфейса каждый объект может иметь определенные поля, которые вступают в силу индивидуально. То есть... Родительский макет, задающий поле 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

вы можете видеть, что маржа верхнего уровня составляет 10 с каждой стороны, и макет ребенка-20 с каждой стороны. Ничего сложного с точки зрения математики.

маржа также может быть указана на основе каждой стороны:

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

I подкласс QTextEdit ->GrowingTextEdit, и подключил сигнал, излучаемый из его документа, к слоту sizeChange это проверяет высоту документа. Я также включил атрибут heightMin и heightMax, чтобы вы могли указать, насколько велик или мал его авторост. Если вы попробуете это, вы увидите, что при вводе в поле виджет начнет изменять размер, а также сжиматься при удалении строк. Вы также можете отключить полосы прокрутки, если хотите. Прямо сейчас каждое текстовое редактирование имеет свои собственные бары, в дополнение к родительской области прокрутки. Кроме того, я думаю, вы можете добавить небольшое значение pad в docHeight, так что он расширяется достаточно, чтобы не показывать полосы прокрутки для контента.

этот подход не очень низком уровне. Он использует часто выставляемые сигналы и дочерние элементы виджета для получения уведомлений об изменениях состояния. Его довольно часто использовать сигналы для расширения функциональности имеющихся виджетов.

Auto-Growing widget example picture


Для Ответа На Вопрос 1:

родительские виджеты и макеты имеют поля, в дополнение к параметру интервала самого макета. По некоторым причинам и влиянию тестирования это apprent, что поля применяются как к внешней области родителя, так и к внутренней области.

так, например, если поле в 2 пикселя указано для родителя, вертикальная граница имеет <--2 pixel | 2 pixel --> margin в дополнение к полям макета (в этом случае HBoxLayout). Если макет имеет 2 пикселя, а также область вокруг горизонтальной линии будет выглядеть так:

<-- 2 pixel | 2 pixel --> <-- 2 pixel (L) 2 pixel--> (W)

редактировать возможно, это больше похоже на следующее:| 2 pixel --> (L) 2 pixel --> <-- 2 pixel (W)

здесь | является вертикальной линией родителя (L) является вертикальной линией макета и (W) - граница встроенного виджета в горизонтальной компоновке.

интервал макета является дополнительным параметром, который определяет, сколько пространства вставлено между виджеты макета в дополнение к любым полям макета.

приведенное выше описание может быть неточным (поэтому не стесняйтесь редактировать его там, где оно неточно), но установка полей родителя и макета на ноль, а также расстояние между макетами на ноль приводит к результату, который вам нужен.

для пункта 2:

я не думаю, что есть прямой способ решить эту проблему (вам, вероятно, придется прибегнуть к подключению на более низком уровне, который требует более глубокого понимания API). Я думаю, вы должны использовать QLabel виджет вместо QTextEdit виджет. Метки не имеют представления и, таким образом, расширяются по мере необходимости, по крайней мере, так они работают по умолчанию, пока родитель не ограничен в своем движении.

Итак, изменить QTextEdit to Qlabel и добавьте прокрутку к родителю, и все должно работать так, как вы хотите. У меня такое чувство, что вы выбрали QTextEdit из-за этого фона. Исследовать пути в HTML работает в виджетах QT, и вы можете изменить фон с помощью 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