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


Введите описание изображения здесь

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

  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) увеличивает его.

Edit

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

Я ответил на свой собственный вопрос, но он многословен и основан на причине и аффекте. Правильный хорошо написанный ответ в стиле учебника для решения обеих проблем получает награду: D

Edit 2

Используя мой собственный ответ ниже, я решил эту проблему. Я приму свой собственный ответ. сейчас.

Введите описание изображения здесь

2 9

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)

Пример Изображения Макета

Вы можете видеть, что верхний уровень поля равен 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, чтобы оно расширяется ровно настолько, чтобы не показывать полосы прокрутки для содержимого.

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

Авто-растущий виджет пример изображения

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

Родительские виджеты и макеты имеют поля, в дополнение к параметру spacing самого макета. Из некоторых причинно-следственных тестов видно, что поля применяются как к внешней области родителя, так и к внутренней области.

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

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

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

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

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

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

Для пункта 2:

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

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

Edit

Введите описание изображения здесь

Этот виджет (извините за размер) создается следующим кодом на 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