PyQt: как обрабатывать автоматическое изменение размеров виджетов при изменении их содержимого

У меня возникают некоторые проблемы с размером виджетов qt4 при изменении их содержимого.

Я проиллюстрирую свои проблемы двумя простыми сценариями:

Сценарий 1:

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

Сценарий 2:

У меня есть виджет QTextEdit. Иногда, когда я изменяю его содержимое с помощью QTextEdit.setHtml(), отображаемый HTML-контент больше не вписывается в виджет с его текущим размером. Виджет начинает показывать горизонтальные и/или вертикальные полосы прокрутки, и я могу использовать их для прокрутки содержимого HTML.

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

Как обрабатываются эти сценарии? Я использую PyQt4.

Изменить: после прочтения как комментария, так и первого ответа (который упоминает ввод содержимого в виджет), я снова рассмотрел вопрос. Я был неприятно удивлен, узнав ужасную опечатку. Я имел в виду QTextBrowser, когда писал QTextEdit, извиняясь за то, что вас вводили в заблуждение. То есть: у меня есть виджет, который отображает HTML-код, который я меняю, и я бы хотел, чтобы виджет вырос настолько, чтобы отображать все, не имея полос прокрутки.

Что касается QLineEdit вместо QLabel - я пошел на QLineEdit, так как заметил, что я не могу выбрать текст из QLabel с помощью мыши для его копирования. С QLineEdit это возможно.

Ответ 1

Я отвечаю на С++ здесь, так как это то, что я больше всего знаю, и ваша проблема не специфична для PyQt.

Обычно вам нужно вызвать QWidget::updateGeometry(), когда возможно изменение sizeHint(), так же, как вам нужно позвонить QWidget::update(), когда содержимое может быть изменено.

Однако ваша проблема заключается в том, что sizeHint() не изменяется, когда текст добавляется к QLineEdit и QTextEdit. По какой-то причине: люди не ожидают, что их диалоги будут расти как-они-типа:)

Тем не менее, если вы действительно хотите, чтобы в этих виджетах возникало поведение типа "растение по типу", вам нужно унаследовать их и переопределить sizeHint() и minimumSizeHint(), чтобы вернуть больший размер и, возможно, setText(), append() и т.д., чтобы вызвать updateGeometry(), поэтому замечено изменение размера.

Расчет размера будет не совсем тривиальным и будет легче для QLineEdit, чем для QTextEdit (который тайно a QAbstractScrollArea), но вы можете посмотреть sizeHint() и minimumSizeHint() реализаций для вдохновения (также для QComboBox, который имеет способ сделать именно то, что вы хотите: QComboBox::AdjustToContents.

РЕДАКТИРОВАТЬ: Ваши два варианта использования (QTextBrowser без полос прокрутки и QLineEdit вместо QLabel только для выбора текста там) могут быть решены с помощью QLabel и достаточно недавнего Qt. В Qt 4.2 QLabel получил уведомление об активации ссылки и так называемые "флагов текстового взаимодействия" (один из которых - TextSelectableByMouse). Единственное отличие, которое я смог разобрать, это то, что загрузка нового контента не является автоматическим, нет истории, и в QLabel нет намека на микрофокус (т.е. Табуляция из ссылки на ссылку).

Ответ 2

В случае QTextBrowser вы можете получить размер документа, используя

QTextBrowser::document()->size();

после установки html, а затем после этого измените размер QTextBrowser.

Ответ 3

Возможно, посмотрите Python QT Automatic Widget Resizer. Это написано на python, но это может дать вам некоторые идеи о том, как делать то, что вам нужно.

Ответ 4

Я получаю аналогичный эффект, используя следующий класс С++:

textedit.h

#ifndef TEXTEDIT_H
#define TEXTEDIT_H

#include <QTextEdit>

class TextEdit : public QTextEdit
{
  Q_DISABLE_COPY( TextEdit )

public:
  TextEdit( QWidget* parent = NULL );
  TextEdit( const QString& text, QWidget* parent = NULL );
  virtual ~TextEdit();

  void fitToDocument( Qt::Orientations orientations );
  virtual QSize sizeHint() const;

private:
  int fittedHeight_;
  Qt::Orientations fittedOrientations_;
  int fittedWidth_;
};

#include "textedit-inl.h"

#endif // TEXTEDIT_H

TextEdit-inl.h

#ifndef TEXTEDITINL_H
#define TEXTEDITINL_H

#include "textedit.h"

inline TextEdit::TextEdit( QWidget* parent ) :
    QTextEdit( parent ), fittedOrientations_( 0 )
{ }

inline TextEdit::TextEdit( const QString& text, QWidget* parent ) :
    QTextEdit( text, parent ), fittedOrientations_( 0 )
{ }

inline TextEdit::~TextEdit()
{ }

inline QSize TextEdit::sizeHint() const
{
  QSize sizeHint = QTextEdit::sizeHint();
  if( fittedOrientations_ & Qt::Horizontal )
    sizeHint.setWidth( fittedWidth_ );
  if( fittedOrientations_ & Qt::Vertical )
    sizeHint.setHeight( fittedHeight_ );
  return sizeHint;
}

#endif // TEXTEDITINL_H

textedit.cpp

#include "textedit.h"

void TextEdit::fitToDocument( Qt::Orientations orientations )
{
  QSize documentSize( document()->size().toSize() );
  QSizePolicy sizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
  if( orientations & Qt::Horizontal ) {
    fittedWidth_ = documentSize.width() + (width() - viewport()->width());
    sizePolicy.setHorizontalPolicy( QSizePolicy::Fixed );
  }
  if( orientations & Qt::Vertical ) {
    fittedHeight_ = documentSize.height() + (width() - viewport()->width());
    sizePolicy.setVerticalPolicy( QSizePolicy::Fixed );
  }
  fittedOrientations_ = orientations;
  setSizePolicy( sizePolicy );
  updateGeometry();
}

например, вызов TextEdit::fitToDocument( Qt::Horizontal ) будет устанавливать ширину виджета на фиксированную ширину, достаточно большую, чтобы соответствовать документу и его окружению (например, вертикальная полоса прокрутки, если таковая имеется). если ваша цель состоит в том, чтобы это произошло при каждом изменении содержимого, подключите сигнал QTextEdit::textChanged() к слоту, который вызывает TextEdit::fitToDocument().

Что касается вашей проблемы с QLabel, решение прост: вызов QLabel::setTextInteractionFlags( Qt::LinksAccessibleByMouse | Qt::TextSelectableByMouse ).

Ответ 5

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